Actionscript 3 SoundManager.(4.0.0)
With the booming of AIR developmemt for mobile platform I thought it was time to make a deep and complete update to my SoundManager notably in the domain of memory and gc management. Also there was a couple of new features that I really wanted to include. So let me introduce my SoundManager 4.0.0 especially made for managing external streaming sounds and library sounds. Besides the number of features it includes (more than any other SoundManager out there) it's also the only one that can manage efficiently millions of sounds if needed with complete memory control and very little CPU use.
The new features summary:
purge() and GC() for easy memory management.
Possibility to use the SoundManager without registering any sounds! (no code to write)
Create as many master volumes as you want! (a master volume affects the overall volume level of a group of sounds).
All operations are made via static methods which is more CPU friendly on mobile devices.
No Sound instances are created prior to playing them. You could register thousands of sounds without seeing a relevant memory usage increase. (can be overwritten)
Play a sequence of sounds.
Let's go first over all the regular SoundManager features then we'll explore the new ones.
Registering and play an external sound:[as] SoundManager.addSound("testplay", "assets/mysound.mp3"); SoundManager.play("testplay"); [/as]
Registering and playing a library sound:[as] SoundManager.addClass("testplay", MySound); SoundManager.play("testplay"); [/as]
When registering a sound you can specify:[as] SoundManager.addSound("testplay", "", 0.5, true, true, true, true, new SoundLoaderContext()); //0.5 a default volume that will be used when playing that sound //true if you want that sound to loop //true if you want that sound to overlap itself when played again //(for example if false upon the click of a button the same sound //will not play again until it has finished playing) //true takes precedence over overlap. If true if already playing this sound will stop and start again. //only in addSound method: //true if you want the SoundManager to immediately create that sound and start loading it. //a SoundLoaderContext (you can provide a global SoundLoaderContext //to apply to all loading sounds via SoundManager.SOUNDCONTEXT) [/as]
When registering your sounds you can retrieve a SoundGroup instance:[as] var group:SoundGroup = SoundManager.addSound("testplay", "assets/mysound.mp3"); [/as]
You can use a SoundGroup instance to manage and change the default behavior of that sound:[as] group.loop = false; group.overlap = false; group.overwrite = false; [/as]
Or get a SoundGroup that way:[as] var group:SoundGroup = SoundManager.getSoundGroup("testplay"); [/as]
Most of the time a SoundGroup will contain only one sound but if it contains more than one sound a random one will be chosen at play time. Perfect feature for playing sound effects or random looping background musics.[as] SoundManager.addSound("testplay", "assets/mysound.mp3"); SoundManager.addSound("testplay", "assets/mysecondsound.mp3"); SoundManager.play("testplay");//a random one is played [/as]
A SoundGroup containing multiple sounds is still managed as one and can be paused, resumed, etc just like a regular sound even if that SoundGroup has generated many SoundChannel (they will all pause if pause() is called for example).
Typical SoundManager features:[as] var channel:SoundChannel; channel = SoundManager.play("testplay", 1); channel = SoundManager.fadeIn("testplay", 0, 2, 0.8); channel = SoundManager.playAt("testplay", 1000, 1); SoundManager.setVolume("testplay", 0.6); SoundManager.mute("testplay"); SoundManager.unmute("testplay"); SoundManager.isMuted("testplay");//true/false SoundManager.isMuted();//true if all sounds are muted SoundManager.muteAll(); SoundManager.unmuteAll(); SoundManager.pause("testplay"); SoundManager.resume("testplay"); SoundManager.isPaused("testplay");//true/false SoundManager.isPaused();//true if all sounds are paused SoundManager.pauseAll(); SoundManager.resumeAll(); channel = SoundManager.getChannel("testplay");//return SoundChannel or null var channels:Vector.
There's also a convenient debugmode:[as] SoundManager.DEBUGMODE = true; //if true will throw errors if unable to play a sound. //if false will dispatch an error event instead. [/as]
Playing sounds without registering them by code:
If you pass a well formatted xml to the SoundManager you can let the SoundManager figure out what sound to register when it's time to play them. Advantage is simple, you can play any sound coming from your xml and you haven't written any line of code to register them! Included in the zip there's also a little AIR app I wrote and use to create that xml from any folder containing sounds. It also converts .wav to .mp3 on the fly. Also included a JSFL script that create an xml from all sounds in a .fla library and replace all sounds in MovieClip timelines by the correct SoundManager code. Keep in mind that with that system you cannot preload an external sound but you can still register yourself the sound you want to preload and let the SoundManager take care of the rest. Don't worry about duplicate sounds, the SoundManager always checks if a sound already exist before registering it so you'll never have any duplicate (you can register the same sound with 2 or more different names though). This xml system works also with Class name of sound coming from a Flash library.[as] SoundManager.SOUNDXML = mysoundxml; SoundManager.BASEPATH = 'assets'; //use BASEPATH to specify a path to append to the sound paths //this is useful when sounds are coming from different places //in debug mode and production [/as]
Memory management:[as] SoundManager.purge();//removes all internal references so they can be gc(). //SoundManager is still operational and you can register new sounds. //if SoundManager has a correct XML set, it can keep playing the sounds in it. SoundManager.GC();//same as purge() but qualify this SoundManager instance for gc() as well. //everything will have to be reset before playing another sound. [/as]
Create master volumes:
Some people had trouble understanding the purpose of the masterVolume. It's quite simple really. Let's say you play 3 sounds at different volume like 0.5, 0.7, 1, if you set the SoundManager.masterVolume to 1 all sound playing keep their current volume, if you set SoundManager.masterVolume to 0.5 all sound playing see their volume divided by 2: 0.25, 0.35, 0.5. It's a sound multiplicator for all playing sounds. Simply plug that with a slider component and you control the volume of all playing sounds. Now with SoundManager 4.0 you can also create your own mastervolumes for any group of sounds.[as] var soundcategory:SoundCategory = new SoundCategory(); soundcategory.name = "effects"; var soundgroup:SoundGroup = SoundManager.getSoundGroup("sound1"); soundgroup.category = soundcategory; var soundgroup:SoundGroup = SoundManager.getSoundGroup("sound2"); soundgroup.category = soundcategory; var soundgroup:SoundGroup = SoundManager.getSoundGroup("sound3"); soundgroup.category = soundcategory; SoundManager.setCategoryVolume(0.5); [/as]
Those sounds will no longer be affected by the SoundManager.masterVolume but by their own masterVolume called “effects”. If you want to reassign a sound to be controlled by the SoundManager.masterVolume:[as] var soundgroup:SoundGroup = SoundManager.getSoundGroup("sound3"); soundgroup.category = null; // SoundManager.masterVolume will now affect that sound. [/as]
Playing a sequence of sounds:
Create a SoundSequence instance and pass in an Array of sound name and time (in milliseconds) gaps. In this next example the sequence will play the sound sound1 then wait 250 milliseconds then play sound2, etc … Each time a new sound plays you can get the corresponding SoundChannel by registering for the event SoundManagerEvent.NEW_CHANNEL.[as] var sequence:SoundSequence = new SoundSequence(["sound1", 250, "sound2", 560, "sound3"]); sequence.addEventListener(SoundManagerEvent.NEW_CHANNEL, handleNewCannel); sequence.addEventListener(SoundManagerEvent.END_SEQUENCE, handleEndSequence); sequence.playSequence(); sequence.stop(); sequence.pause(); sequence.resume(); [/as]
Feel free to contact me for bug reports, share your experience or to request new features.