.heading{fontFamily:Arial;fontWeight:bold;color:#C73105;fontSize:12pt;} .logo{fontFamily:Arial;fontWeight:bold;color:navy;fontSize:10pt;} .mainTitle{fontFamily:Arial;fontWeight:normal;color:grey;fontSize:9pt;} .mainTitleBold{fontFamily:Arial;fontWeight:bold;color:navy;fontSize:11pt;} Ready...'); } private function PageLoaded():void { /* This starts the timers that drive the VU meter and the auto-reconnect process */ var myTimer:Timer = new Timer(10, 0); myTimer.addEventListener("timer", onTimer); myTimer.start(); var myTicker:Timer = new Timer(3000, 0); myTicker.addEventListener("timer", onTicker); myTicker.start(); if(Application.application.parameters.autoStart) { // if the script is invoked with ?autoStart=1, it will start playback automatically var myStarter:Timer = new Timer(750, 1); myStarter.addEventListener("timer", onStarter); myStarter.start(); } metadataLoader = new URLLoader; metadataLoader.addEventListener(Event.COMPLETE, onCompleteMetadata); } private function onStarter(event:TimerEvent):void { onPlayEvent("playback"); } private function onTimer(event:TimerEvent):void { if(sc) { vuMeter.setProgress(sc.leftPeak * 100, 100); bufferMeter.setProgress(bufferAvg, 2000); if(s.isBuffering) { buffering = true; debugOut('Buffering...'); } else if(buffering) { buffering = false; debugOut('Playing...'); } /* if(sc.leftPeak > 0.75) vuMeter.barColor = 0xFF0000; else vuMeter.barColor = 0x00FF00; */ } } private function onTicker(event:TimerEvent):void { // if we are still playing and our link is dead // instruct it to rise again if(playing && retrying) { retries++; debugOut('Retrying... (' + retries + ')'); // doPlay(); } // this fetches metadata from a php script on the server that parses out the current // song from the icecast status page metadataLoader.load(new URLRequest("http://www.wshr.org/metadata.php")); } public function onCompleteMetadata(e:Event):void { var x:XML = new XML(metadataLoader.data); id3Display.htmlText = '' + x.song + ''; } public function doPlay():void { // this function actually starts playback - hopefully var url:URLRequest = new URLRequest("http://icecast.sheer.us:8000/wshr"); // debugOut('doplay'); if(sc) sc.stop(); // stop any playback that might have already been happening if(s) // close any previous sound channel that might have been open try { s.close(); } catch (e:Object) { Alert(e); } s = new Sound(null,new SoundLoaderContext(7500,true)); // create a new sound, encourage buffering s.addEventListener(Event.OPEN,onOpen); s.addEventListener(IOErrorEvent.IO_ERROR,onIOError); s.addEventListener(Event.COMPLETE,onComplete); s.addEventListener(Event.ID3, onID3); // this will never fire ;-( s.addEventListener(ProgressEvent.PROGRESS, onProgress); s.load(url); // load the stream sc = s.play(); // start playback } public function doSkip():void { // this calls a php script that then calls *another* php script on the encoder machine // which sends SIGUSR2 to ices. It's a total hack. var u:URLLoader = new URLLoader(new URLRequest('http://www.wshr.org/skip.php')); } public function onID3(e:Event):void { // this never happens because IceCast apparently strips out ID3 information // so we have a hackish metadata system, see above debugOut('onID3'); id3Display.htmlText = '' + s.id3.TIT2 + ''; } public function onProgress(e:ProgressEvent):void { // this keeps a moving average filter of the amount of buffer data we have var bc:int = e.bytesTotal - e.bytesLoaded; bufferAvg = ((bufferAvg * 100) + bc) / 101; /* todo: there's a bug where sometimes the player starts at half the correct sample rate. We should be able to detect this because the buffer will grow to a unreasonable size very quickly. We should detect this and restart the stream */ } public function onPlayEvent(sAction:String):void { debugOut('Playback requested'); playing = true; retrying = false; retries = 0; doPlay(); btnPlay.enabled = false; btnStop.enabled = true; } private function onOpen(event:Event):void { // this gets called when the mp3 stream has been successfully opened if(retrying) retrying = false; retries = 0; debugOut('Playing...'); // This timer invokes the stream switch when it fires var t:Timer = new Timer(switchTime,1); t.addEventListener("timer",onSwitch); t.start(); } private function onSwitch(event:Event):void { // open new sound channel var url:URLRequest = new URLRequest("http://icecast.sheer.us:8000/wshr?" + Math.random()); s2 = new Sound(null,new SoundLoaderContext(7500,true)); s2.addEventListener(Event.OPEN,onSwitchOpen); s2.addEventListener(IOErrorEvent.IO_ERROR,onIOError); s2.addEventListener(Event.COMPLETE,onComplete); s2.addEventListener(Event.ID3, onID3); s2.addEventListener(ProgressEvent.PROGRESS, onProgress); s2.load(url); debugOut('Switching...'); sc2 = s2.play(); } private function onSwitchOpen(event:Event):void { // set timer to shut down old sound channel var t:Timer = new Timer(250,1); t.addEventListener("timer",onSwitchClose); t.start(); // set timer for next switch var t2:Timer = new Timer(switchTime,1); t2.addEventListener("timer",onSwitch); t2.start(); } private function onSwitchClose(event:Event):void { // close and stop the old channel, then give the primary over to the new channel // there's a potential bug here if the user presses stop in the window between, but // statistically rather unlikely sc.stop(); s.close(); sc = sc2; s = s2; // clear the 'switch' message from the status display debugOut('Playing...'); } private function stopSound():void { if (btnPlay.enabled==false) { sc.stop(); s.close(); sc = null; s = null; debugOut('Stopped...'); playing = false; retrying = false; btnStop.enabled = false; //btnPause.enabled = false; btnPlay.enabled = true; //vis.visible = false; } } /* private function pauseSound():void { if (btnPause.label=="Pause") { btnPause.label = "Resume" } else { btnPause.label = "Pause" } //mp3Stream.togglePause(); } */ private function catchAll(event:Object):void { if (event.type == "netStatus") { debugOut("catchAll:code:" + event.info.code); } else { debugOut("catchAll:event:" + event); } } public function debugOut(str:String):void { // several different output modes // this has turned into a inapprorpaitely-named status window // but the other code is still here so it can be uncommented for debugging //debugOutDisplay.text = debugOutDisplay.text + str + "\n"; //debugOutDisplay.text = str; //debugOutDisplay.htmlText = str; debugOutDisplay.htmlText = str + "
" + debugOutDisplay.htmlText; } public function onComplete(event:Object):void { // the stream is never supposed to go away, so if it does, we restart retrying = true; sc = null; // debugOut("onComplete"); doPlay(); } public function onIOError(event:Object):void { // the stream is never supposed to go away, so if it does, we restart retrying = true; sc = null; // debugOut("onIOError"); doPlay(); } ]]>