package { import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.media.ID3Info; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundLoaderContext; import flash.media.SoundTransform; import flash.net.URLRequest; import flash.system.System; import mx.binding.utils.ChangeWatcher; public class SoundManager extends EventDispatcher { /* In this array we will store dynamicly generated properties to the Sound and SoundChannel objects so that we can use "delete" to remove the objects from memory. Be sure to remember that if we forget to remove and event listener from something it will not be unloaded from memory. */ private var dynamicProps:Array = [null, null]; private var channelWatcher:ChangeWatcher; public function SoundManager() { } /* keep this getter private to prevent accidental public access to the actual Sound class. This way we enfore that the class dosen't get any event listeners that we are not aware of */; private function get sound():Sound { return dynamicProps[0]; } private function set sound(value:Sound):void { destroySoundChannel(); destroySound(); System.gc(); //force garbage collection dynamicProps[0] = value; addSoundListeners(); dispatchEvent(new Event("soundChanged")); } private function get channel():SoundChannel { return dynamicProps[1]; } private function set channel(value:SoundChannel):void { destroySoundChannel(); dynamicProps[1] = value; addSoundChannelListeners(); dispatchEvent(new Event("channelChanged")); } private function destroySound():void { //stop and cleanup any existing Sound if ( dynamicProps[0] != null ) { //cancel any remaining downloading try { Sound(dynamicProps[0]).close(); } catch ( e:Error ) {} removeSoundListeners(); delete dynamicProps[0]; dynamicProps[1] = null; } } private function destroySoundChannel():void { //stop, and cleanup any existing SoundChannel if ( dynamicProps[1] != null ) { SoundChannel(dynamicProps[1]).stop(); removeSoundChannelListeners(); delete dynamicProps[1]; dynamicProps[1] = null; } } private function addSoundListeners():void { if ( dynamicProps[0] != null ) { Sound(dynamicProps[0]).addEventListener(Event.COMPLETE, handleSoundLoaded); Sound(dynamicProps[0]).addEventListener(Event.ID3, handleSoundID3); Sound(dynamicProps[0]).addEventListener(IOErrorEvent.IO_ERROR, handleSoundIoError); Sound(dynamicProps[0]).addEventListener(ProgressEvent.PROGRESS, handleSoundProgress); Sound(dynamicProps[0]).addEventListener(Event.OPEN, handleSoundOpen); } } private function removeSoundListeners():void { if ( dynamicProps[0] != null ) { Sound(dynamicProps[0]).removeEventListener(Event.COMPLETE, handleSoundLoaded); Sound(dynamicProps[0]).removeEventListener(Event.ID3, handleSoundID3); Sound(dynamicProps[0]).removeEventListener(IOErrorEvent.IO_ERROR, handleSoundIoError); Sound(dynamicProps[0]).removeEventListener(ProgressEvent.PROGRESS, handleSoundProgress); Sound(dynamicProps[0]).removeEventListener(Event.OPEN, handleSoundOpen); } } private function addSoundChannelListeners():void { if ( dynamicProps[1] != null ) { SoundChannel(dynamicProps[1]).addEventListener(Event.SOUND_COMPLETE, handleSoundComplete); channelWatcher = ChangeWatcher.watch(SoundChannel(dynamicProps[1]), "position", handlePositionChange); } } private function removeSoundChannelListeners():void { if ( dynamicProps[1] != null ) { SoundChannel(dynamicProps[1]).removeEventListener(Event.SOUND_COMPLETE, handleSoundComplete); channelWatcher.unwatch(); channelWatcher = null; } } private function handleSoundOpen(e:Event):void { dispatchEvent(new Event(Event.OPEN)); } private function handlePositionChange(e:Event):void { dispatchEvent(new Event("channelPositionChange")); } private function handleSoundProgress(e:ProgressEvent):void { var evt:ProgressEvent = new ProgressEvent(e.type, e.bubbles, e.cancelable, e.bytesLoaded, e.bytesTotal); dispatchEvent(evt); } private function handleSoundIoError(e:IOErrorEvent):void { var evt:IOErrorEvent = new IOErrorEvent(e.type, e.bubbles, e.cancelable, e.text, e.errorID); dispatchEvent(evt); } private function handleSoundID3(e:Event):void { dispatchEvent(new Event(Event.ID3)); } private function handleSoundLoaded(e:Event):void { dispatchEvent(new Event(Event.COMPLETE)); } private function handleSoundComplete(e:Event):void { dispatchEvent(new Event(Event.SOUND_COMPLETE)); } public function load(req:URLRequest, context:SoundLoaderContext=null):void { sound = new Sound(); sound.load(req, context); } public function unload():void { sound = null; } public function play(startTime:Number, loops:int=0, sndTransform:SoundTransform=null):void { if ( sound != null ) { stop(); channel = sound.play(startTime, loops, sndTransform); dispatchEvent(new Event("soundTransformChanged")); } } public function stop():void { if ( channel != null ) { channel.stop(); } } [Bindable("channelPositionChange")] public function get position():Number { if ( channel == null ) { return -1; } return channel.position; } [Bindable("soundChanged")] public function get length():Number { if ( sound == null ) { return -1; } return sound.length; } [Bindable("id3")] public function get id3():ID3Info { if ( sound == null ) { return null; } return sound.id3; } public function get isBuffering():Boolean { if ( sound == null ) { return false; } return sound.isBuffering; } [Bindable("soundTransformChanged")] public function get soundTransform():SoundTransform { if ( channel == null ) { return null; } return channel.soundTransform; } public function set soundTransform(value:SoundTransform):void { if ( channel == null ) { return; } channel.soundTransform = value; dispatchEvent(new Event("soundTransformChanged")); } } }