AS3: SoundManager

UPDATED 1/6/2011: SoundManager v1.4 has been released and should be used instead of this class.

View Documentation
Download TweenLite
Download Class

I don't normally add sounds to my projects unless the client requests it but when they do I always struggle to cleanly do it. I just had to write a utility that would allow me to do this easily and quickly as sounds are usually an afterthought. After a little bit of programming and scouring the internet for examples, the SoundManager was born.

The SoundManager is a Singleton that does exactly what it says. It has a host of methods to choose from that should make adding sounds to your projects super simple. It has a dependency to TweenLite for the sound fading (which can be taken out but I use TweenLite in a lot of projects so I just made it easy on myself). For a complete list of methods, please review the documentation.

Actionscript:
  1. package com.reintroducing.sound
  2. {
  3.     import flash.media.Sound;
  4.     import flash.media.SoundChannel;
  5.     import flash.media.SoundLoaderContext;
  6.     import flash.media.SoundTransform;
  7.     import flash.net.URLRequest;
  8.     import flash.utils.Dictionary;
  9.     import flash.utils.getQualifiedClassName;
  10.    
  11.     import gs.TweenLite;   
  12.    
  13.     /**
  14.      * The SoundManager is a singleton that allows you to have various ways to control sounds in your project.
  15.      * <p />
  16.      * The SoundManager can load external or library sounds, pause/mute/stop/control volume for one or more sounds at a time,
  17.      * fade sounds up or down, and allows additional control to sounds not readily available through the default classes.
  18.      * <p />
  19.      * This class is dependent on TweenLite (http://www.tweenlite.com) to aid in easily fading the volume of the sound.
  20.      *
  21.      * @author Matt Przybylski [http://www.reintroducing.com]
  22.      * @version 1.0
  23.      */
  24.     public class SoundManager
  25.     {
  26. //- PRIVATE & PROTECTED VARIABLES -------------------------------------------------------------------------
  27.  
  28.         // singleton instance
  29.         private static var _instance:SoundManager;
  30.         private static var _allowInstance:Boolean;
  31.        
  32.         private var _soundsDict:Dictionary;
  33.         private var _sounds:Array;
  34.        
  35. //- PUBLIC & INTERNAL VARIABLES ---------------------------------------------------------------------------
  36.        
  37.        
  38.        
  39. //- CONSTRUCTOR -------------------------------------------------------------------------------------------
  40.    
  41.         // singleton instance of SoundManager
  42.         public static function getInstance():SoundManager
  43.         {
  44.             if (SoundManager._instance == null)
  45.             {
  46.                 SoundManager._allowInstance = true;
  47.                 SoundManager._instance = new SoundManager();
  48.                 SoundManager._allowInstance = false;
  49.             }
  50.            
  51.             return SoundManager._instance;
  52.         }
  53.        
  54.         public function SoundManager()
  55.         {
  56.             this._soundsDict = new Dictionary(true);
  57.             this._sounds = new Array();
  58.            
  59.             if (!SoundManager._allowInstance)
  60.             {
  61.                 throw new Error("Error: Use SoundManager.getInstance() instead of the new keyword.");
  62.             }
  63.         }
  64.        
  65. //- PRIVATE & PROTECTED METHODS ---------------------------------------------------------------------------
  66.        
  67.        
  68.        
  69. //- PUBLIC & INTERNAL METHODS -----------------------------------------------------------------------------
  70.    
  71.         /**
  72.          * Adds a sound from the library to the sounds dictionary for playing in the future.
  73.          *
  74.          * @param $linkageID The class name of the library symbol that was exported for AS
  75.          * @param $name The string identifier of the sound to be used when calling other methods on the sound
  76.          *
  77.          * @return Boolean A boolean value representing if the sound was added successfully
  78.          */
  79.         public function addLibrarySound($linkageID:*, $name:String):Boolean
  80.         {
  81.             for (var i:int = 0; i <this._sounds.length; i++)
  82.             {
  83.                 if (this._sounds[i].name == $name) return false;
  84.             }
  85.            
  86.             var sndObj:Object = new Object();
  87.             var snd:Sound = new $linkageID;
  88.            
  89.             sndObj.name = $name;
  90.             sndObj.sound = snd;
  91.             sndObj.channel = new SoundChannel();
  92.             sndObj.position = 0;
  93.             sndObj.paused = true;
  94.             sndObj.volume = 1;
  95.             sndObj.startTime = 0;
  96.             sndObj.loops = 0;
  97.             sndObj.pausedByAll = false;
  98.            
  99.             this._soundsDict[$name] = sndObj;
  100.             this._sounds.push(sndObj);
  101.            
  102.             return true;
  103.         }
  104.        
  105.         /**
  106.          * Adds an external sound to the sounds dictionary for playing in the future.
  107.          *
  108.          * @param $path A string representing the path where the sound is on the server
  109.          * @param $name The string identifier of the sound to be used when calling other methods on the sound
  110.          * @param $buffer The number, in milliseconds, to buffer the sound before you can play it (default: 1000)
  111.          * @param $checkPolicyFile A boolean that determines whether Flash Player should try to download a cross-domain policy file from the loaded sound's server before beginning to load the sound (default: false)
  112.          *
  113.          * @return Boolean A boolean value representing if the sound was added successfully
  114.          */
  115.         public function addExternalSound($path:String, $name:String, $buffer:Number = 1000, $checkPolicyFile:Boolean = false):Boolean
  116.         {
  117.             for (var i:int = 0; i <this._sounds.length; i++)
  118.             {
  119.                 if (this._sounds[i].name == $name) return false;
  120.             }
  121.            
  122.             var sndObj:Object = new Object();
  123.             var snd:Sound = new Sound(new URLRequest($path), new SoundLoaderContext($buffer, $checkPolicyFile));
  124.            
  125.             sndObj.name = $name;
  126.             sndObj.sound = snd;
  127.             sndObj.channel = new SoundChannel();
  128.             sndObj.position = 0;
  129.             sndObj.paused = true;
  130.             sndObj.volume = 1;
  131.             sndObj.startTime = 0;
  132.             sndObj.loops = 0;
  133.             sndObj.pausedByAll = false;
  134.            
  135.             this._soundsDict[$name] = sndObj;
  136.             this._sounds.push(sndObj);
  137.            
  138.             return true;
  139.         }
  140.        
  141.         /**
  142.          * Removes a sound from the sound dictionary.  After calling this, the sound will not be available until it is re-added.
  143.          *
  144.          * @param $name The string identifier of the sound to remove
  145.          *
  146.          * @return void
  147.          */
  148.         public function removeSound($name:String):void
  149.         {
  150.             for (var i:int = 0; i <this._sounds.length; i++)
  151.             {
  152.                 if (this._sounds[i].name == $name)
  153.                 {
  154.                     this._sounds[i] = null;
  155.                     this._sounds.splice(i, 1);
  156.                 }
  157.             }
  158.            
  159.             delete this._soundsDict[$name];
  160.         }
  161.        
  162.         /**
  163.          * Removes all sounds from the sound dictionary.
  164.          *
  165.          * @return void
  166.          */
  167.         public function removeAllSounds():void
  168.         {
  169.             for (var i:int = 0; i <this._sounds.length; i++)
  170.             {
  171.                 this._sounds[i] = null;
  172.             }
  173.            
  174.             this._sounds = new Array();
  175.             this._soundsDict = new Dictionary(true);
  176.         }
  177.  
  178.         /**
  179.          * Plays or resumes a sound from the sound dictionary with the specified name.
  180.          *
  181.          * @param $name The string identifier of the sound to play
  182.          * @param $volume A number from 0 to 1 representing the volume at which to play the sound (default: 1)
  183.          * @param $startTime A number (in milliseconds) representing the time to start playing the sound at (default: 0)
  184.          * @param $loops An integer representing the number of times to loop the sound (default: 0)
  185.          *
  186.          * @return void
  187.          */
  188.         public function playSound($name:String, $volume:Number = 1, $startTime:Number = 0, $loops:int = 0):void
  189.         {
  190.             var snd:Object = this._soundsDict[$name];
  191.             snd.volume = $volume;
  192.             snd.startTime = $startTime;
  193.             snd.loops = $loops;
  194.                
  195.             if (snd.paused)
  196.             {
  197.                 snd.channel = snd.sound.play(snd.position, snd.loops, new SoundTransform(snd.volume));
  198.             }
  199.             else
  200.             {
  201.                 snd.channel = snd.sound.play($startTime, snd.loops, new SoundTransform(snd.volume));
  202.             }
  203.            
  204.             snd.paused = false;
  205.         }
  206.        
  207.         /**
  208.          * Stops the specified sound.
  209.          *
  210.          * @param $name The string identifier of the sound
  211.          *
  212.          * @return void
  213.          */
  214.         public function stopSound($name:String):void
  215.         {
  216.             var snd:Object = this._soundsDict[$name];
  217.             snd.paused = true;
  218.             snd.channel.stop();
  219.             snd.position = snd.channel.position;
  220.         }
  221.        
  222.         /**
  223.          * Pauses the specified sound.
  224.          *
  225.          * @param $name The string identifier of the sound
  226.          *
  227.          * @return void
  228.          */
  229.         public function pauseSound($name:String):void
  230.         {
  231.             var snd:Object = this._soundsDict[$name];
  232.             snd.paused = true;
  233.             snd.position = snd.channel.position;
  234.             snd.channel.stop();
  235.         }
  236.        
  237.         /**
  238.          * Plays all the sounds that are in the sound dictionary.
  239.          *
  240.          * @param $useCurrentlyPlayingOnly A boolean that only plays the sounds which were currently playing before a pauseAllSounds() or stopAllSounds() call (default: false)
  241.          *
  242.          * @return void
  243.          */
  244.         public function playAllSounds($useCurrentlyPlayingOnly:Boolean = false):void
  245.         {
  246.             for (var i:int = 0; i <this._sounds.length; i++)
  247.             {
  248.                 var id:String = this._sounds[i].name;
  249.                
  250.                 if ($useCurrentlyPlayingOnly)
  251.                 {
  252.                     if (this._soundsDict[id].pausedByAll)
  253.                     {
  254.                         this._soundsDict[id].pausedByAll = false;
  255.                         this.playSound(id);
  256.                     }
  257.                 }
  258.                 else
  259.                 {
  260.                     this.playSound(id);
  261.                 }
  262.             }
  263.         }
  264.        
  265.         /**
  266.          * Stops all the sounds that are in the sound dictionary.
  267.          *
  268.          * @param $useCurrentlyPlayingOnly A boolean that only stops the sounds which are currently playing (default: true)
  269.          *
  270.          * @return void
  271.          */
  272.         public function stopAllSounds($useCurrentlyPlayingOnly:Boolean = true):void
  273.         {
  274.             for (var i:int = 0; i <this._sounds.length; i++)
  275.             {
  276.                 var id:String = this._sounds[i].name;
  277.                
  278.                 if ($useCurrentlyPlayingOnly)
  279.                 {
  280.                     if (!this._soundsDict[id].paused)
  281.                     {
  282.                         this._soundsDict[id].pausedByAll = true;
  283.                         this.stopSound(id);
  284.                     }
  285.                 }
  286.                 else
  287.                 {
  288.                     this.stopSound(id);
  289.                 }
  290.             }
  291.         }
  292.        
  293.         /**
  294.          * Pauses all the sounds that are in the sound dictionary.
  295.          *
  296.          * @param $useCurrentlyPlayingOnly A boolean that only pauses the sounds which are currently playing (default: true)
  297.          *
  298.          * @return void
  299.          */
  300.         public function pauseAllSounds($useCurrentlyPlayingOnly:Boolean = true):void
  301.         {
  302.             for (var i:int = 0; i <this._sounds.length; i++)
  303.             {
  304.                 var id:String = this._sounds[i].name;
  305.                
  306.                 if ($useCurrentlyPlayingOnly)
  307.                 {
  308.                     if (!this._soundsDict[id].paused)
  309.                     {
  310.                         this._soundsDict[id].pausedByAll = true;
  311.                         this.pauseSound(id);
  312.                     }
  313.                 }
  314.                 else
  315.                 {
  316.                     this.pauseSound(id);
  317.                 }
  318.             }
  319.         }
  320.        
  321.         /**
  322.          * Fades the sound to the specified volume over the specified amount of time.
  323.          *
  324.          * @param $name The string identifier of the sound
  325.          * @param $targVolume The target volume to fade to, between 0 and 1 (default: 0)
  326.          * @param $fadeLength The time to fade over, in seconds (default: 1)
  327.          *
  328.          * @return void
  329.          */
  330.         public function fadeSound($name:String, $targVolume:Number = 0, $fadeLength:Number = 1):void
  331.         {
  332.             var fadeChannel:SoundChannel = this._soundsDict[$name].channel;
  333.            
  334.             TweenLite.to(fadeChannel, $fadeLength, {volume: $targVolume});
  335.         }
  336.        
  337.         /**
  338.          * Mutes the volume for all sounds in the sound dictionary.
  339.          *
  340.          * @return void
  341.          */
  342.         public function muteAllSounds():void
  343.         {
  344.             for (var i:int = 0; i <this._sounds.length; i++)
  345.             {
  346.                 var id:String = this._sounds[i].name;
  347.                
  348.                 this.setSoundVolume(id, 0);
  349.             }
  350.         }
  351.        
  352.         /**
  353.          * Resets the volume to their original setting for all sounds in the sound dictionary.
  354.          *
  355.          * @return void
  356.          */
  357.         public function unmuteAllSounds():void
  358.         {
  359.             for (var i:int = 0; i <this._sounds.length; i++)
  360.             {
  361.                 var id:String = this._sounds[i].name;
  362.                 var snd:Object = this._soundsDict[id];
  363.                 var curTransform:SoundTransform = snd.channel.soundTransform;
  364.                 curTransform.volume = snd.volume;
  365.                 snd.channel.soundTransform = curTransform;
  366.             }
  367.         }
  368.        
  369.         /**
  370.          * Sets the volume of the specified sound.
  371.          *
  372.          * @param $name The string identifier of the sound
  373.          * @param $volume The volume, between 0 and 1, to set the sound to
  374.          *
  375.          * @return void
  376.          */
  377.         public function setSoundVolume($name:String, $volume:Number):void
  378.         {
  379.             var snd:Object = this._soundsDict[$name];
  380.             var curTransform:SoundTransform = snd.channel.soundTransform;
  381.             curTransform.volume = $volume;
  382.             snd.channel.soundTransform = curTransform;
  383.         }
  384.        
  385.         /**
  386.          * Gets the volume of the specified sound.
  387.          *
  388.          * @param $name The string identifier of the sound
  389.          *
  390.          * @return Number The current volume of the sound
  391.          */
  392.         public function getSoundVolume($name:String):Number
  393.         {
  394.             return this._soundsDict[$name].channel.soundTransform.volume;
  395.         }
  396.        
  397.         /**
  398.          * Gets the position of the specified sound.
  399.          *
  400.          * @param $name The string identifier of the sound
  401.          *
  402.          * @return Number The current position of the sound, in milliseconds
  403.          */
  404.         public function getSoundPosition($name:String):Number
  405.         {
  406.             return this._soundsDict[$name].channel.position;
  407.         }
  408.        
  409.         /**
  410.          * Gets the duration of the specified sound.
  411.          *
  412.          * @param $name The string identifier of the sound
  413.          *
  414.          * @return Number The length of the sound, in milliseconds
  415.          */
  416.         public function getSoundDuration($name:String):Number
  417.         {
  418.             return this._soundsDict[$name].sound.length;
  419.         }
  420.        
  421.         /**
  422.          * Gets the sound object of the specified sound.
  423.          *
  424.          * @param $name The string identifier of the sound
  425.          *
  426.          * @return Sound The sound object
  427.          */
  428.         public function getSoundObject($name:String):Sound
  429.         {
  430.             return this._soundsDict[$name].sound;
  431.         }
  432.        
  433.         /**
  434.          * Identifies if the sound is paused or not.
  435.          *
  436.          * @param $name The string identifier of the sound
  437.          *
  438.          * @return Boolean The boolean value of paused or not paused
  439.          */
  440.         public function isSoundPaused($name:String):Boolean
  441.         {
  442.             return this._soundsDict[$name].paused;
  443.         }
  444.        
  445.         /**
  446.          * Identifies if the sound was paused or stopped by calling the stopAllSounds() or pauseAllSounds() methods.
  447.          *
  448.          * @param $name The string identifier of the sound
  449.          *
  450.          * @return Number The boolean value of pausedByAll or not pausedByAll
  451.          */
  452.         public function isSoundPausedByAll($name:String):Boolean
  453.         {
  454.             return this._soundsDict[$name].pausedByAll;
  455.         }
  456.  
  457. //- EVENT HANDLERS ----------------------------------------------------------------------------------------
  458.    
  459.        
  460.  
  461. //- GETTERS & SETTERS -------------------------------------------------------------------------------------
  462.    
  463.         public function get sounds():Array
  464.         {
  465.             return this._sounds;
  466.         }
  467.    
  468. //- HELPERS -----------------------------------------------------------------------------------------------
  469.    
  470.         public function toString():String
  471.         {
  472.             return getQualifiedClassName(this);
  473.         }
  474.    
  475. //- END CLASS ---------------------------------------------------------------------------------------------
  476.     }
  477. }

If you found this post useful, please consider leaving a comment, subscribing to the feed, or making a small donation.

96 Comments

[...] technorati, twitter, more '; AS3 - SoundManager 1 Minute Ago View Documentation View Blog Post/Download Class I don

[...] SoundManager [...]

Thanks for this - a fabulous idea.

[...] Link [...]

Great idea!
Many thanks to share this!

Added the ability to load an xml list of sounds and then queue them and added a SoundManagerEvent class to dispatch sound_assets_loaded event. Had to do this really quickly so it extends displayobject and needs to be added to display list ... Let me know if you would like to use this or make it better ...

Sure Brendan, send it along to my email, matt at reintroducing dot com.

You're unmuteAllSounds() functionality is a bit underdeveloped...

Example... if you use the fadeSound feature, the result of using unmuteallSounds is erratic.

A much-welcomed feature.

Steven,
I just made a test fla and unmuteAllSounds() did as the name suggests after a fadeSound() operation. What is erratic about it for you?

Well, I say erratic because it is hard to define the problem that occurs.

I notice you use Tweenlite for the fading - are you using the latest version of Tweenlite? I am. Is it compatible?

I will try to mess with it a bit more and report my findings...however I suggest you stresstest unmuteallsounds - because I promise I'm not making it up, lol.

Steven,
It shouldn't matter the version of TweenLite you're using for the fading as that functionality has been unchanged since it was released (the fading of volume) for the most part.

If you can pinpoint the problem and capture it in a separated FLA outside of your project, i'd be more than happy to take a look at it as every test i've run on it has proven to come back positive. you could always send it to me at matt [at] reintroducing [dot] com. Thanks.

I have created a demo and emailed it to you.

Hi Matt,
thanks for a very useful class. Only thing missing really is event handlers. I found I was loading in external sounds (about 50 in one go - for offline content) and some of the filepaths were incorrect. I had no way of knowing which sounds were throwing exceptions.

In a nutshell I've added some elementary event handlers for the addExternalSound method. I've emailed the updated class file to matt [at] reintroducing [dot] com - maybe useful?

Oops - you may want to edit the email above to avoid spam - apologies - wasn't thinking

No worries Wouter, I edited the comment. Thanks for the input, when I get a moment I will take a look at it and think of the best way to add it in. I made a small change to the class for Steven but didn't post an update as I also have another update request I got from someone else so I plan to release all of it in a larger update in the hopefully near future, just have to finish up that stupid "work" stuff that everyone is so crazy about :P

Cheers for this. It's a well written class. I wrote something similar in AS2 a while back, and was looking for exactly this sort of thing.

Thanks!

no problem, glad it helped Daniel.

SoundManager attempts to load all the mp3s added to it immediately this can slow down performance if you're using a lot of mp3s especially large files like 1 MB+. It would be nice to have a load on demand feature or at least queue.

Thanks for class!

Kirit,
That has been requested and is on my list of things to do, i just have not had time to create it.

Hi,
Thanks for the SoundManager, but for us beginners, I have no idea how to use it. My teacher made a content manager and a stage manager. They included FLA along with the AS so that we can study and learn how it works and more importantly what it all means. Especially for us visual designers, like myself. I can wrap my brain around the logic of code yet, but I am good with getting it slowly piece by piece if I can see an example of it being used. Not just looking at the code. Can you provide an example of this class being used with a fla. Like how do I create a sound dictionary?

Keeya,
I didn't provide an example because the class is very well documented (at least I believe so). All you have to do is the following to run a function of the class:

SoundManager.getInstance().methodNameHere(). That will execute whichever method you'd like, be it addLibrarySound or play or what not.

All this needs is some constructive usage information ,so as to implement easily. Could you please provide that as well?

I'm going to be adding some functionality to this shortly (and by shortly I mean in the next couple of months :P ) and I'll provide some usage examples at that time. For right now, refer to the example I provided Keeya in the comment above yours to get you started. All other methods are called the same way as that one.

HAHA.. yeah.. I am reading AS3 for Beginners and I review this Sound Manager again.. and was like this is two advance. As a designer, I am visually creative...as a coder.. not so good. I didn't get long division in first grade. I was still learning colors. I did find what I needed or can learn from.. maybe you can provide a similar example. the only way I can learn something is by dissecting it.

http://www.learningactionscript3.com/2007/12/06/sound-mixer-source/#more-20

I can download the code and view it step by step..as well as be visual creative. I do apologize for I know even creating small projects is time consuming. I am open to any advice. I know your soundmanager class would make the above project even more "fierce" and manageable.

Hi

Thanks for the class. very easy to use and work perfectly. I did change it to use Tweener, but only because my project was using that. And added a fade all sound down / up

Hi Matt - Love your work - thanks for such great contributions.

One question I have:
I have to fire off a second sound after the first one has completed playing. In AS3 traditionally I could use: mySoundChannel.addEventListener(Event.SOUND_COMPLETE, myFunctionToCall);.

Without having to write a timer or use an interval to measure sound duration against playhead position through the Sound Managers existing methods, is there an easy way to setup an Event Listener for the end play of one of the dictionary sound items I have playing?

Thanks so much - again love your work!

[...] Posted ธันวาคม 8, 2008 Filed under: actionscript, research | SoundManager เป็น AS3 library [...]

re: Scott Bussey

I've added a quick Sound Complete listener to the class...

I'm not sure how to describe that process via blog comments, but feel free to hit me on AIM: producerism

hey guys,
some of the events you guys are asking for are going to be in the next update. i do plan on updating the class shortly i just have not had a chance to sit down and do it all as i've been busy lately and with the holidays coming up its eaten up some of my free time. hang tight and ill release an update soon.

One more thing to add, I found it extremely useful to change the getSoundObject function to either return the Sound object found, or to return false if none was found.

public function getSoundObject($name:String):*
{
if (this._soundsDict[$name])
{
return this._soundsDict[$name].sound;
} else {
return false;
}
}

T,
I could definitely see how that would be useful. For some reason I always run under the assumption that people will know they should only be calling stuff they know exists, which is an error in my ways. I will add in some checking like this in the next iteration of the class. Thanks!

[...] and the beginning of addExternalSound(). The 2 functions are the same as they appear on http://evolve.reintroducing.com/2008/07/15/news/as3-soundmanager/ so the impatient can repair the relevant code from [...]

Hi Matt.

Nice class. I modified it for use within PureMVC and emailed the class to you. Hope you find it useful.

Thanks!

Hello and thank you for releasing this class!

I am getting an error while trying to use this class. All information is below:

import com.Classes.SoundManager; (I changed the package definition in SoundManager.as to fit)

var manageSound:SoundManager = SoundManager.getInstance();

manageSound.addLibrarySound('basicBulletSoundClass', 'basicBulletSound');
manageSound.addLibrarySound('blitzBulletSoundClass', 'blitzBulletSound');
manageSound.addLibrarySound('giantBulletSoundClass', 'giantBulletSound');

And I get the error:

TypeError: Error #1007: Instantiation attempted on a non-constructor.
at com.Classes::SoundManager/addLibrarySound()
at TurretResistance_fla::MainTimeline/frame2()

---------------------
Any ideas?

Andreas,
Can you isolate the problem in an FLA and send the FLA to me along with the edited package for the class that you made? Off the top of my head I don't see any reason for the error but it could be something in the setup.

I have sent you an email with all information and files.

Thank you.

Andreas,
You're going to kick yourself for this (and I was too until I had a friend look at it as well) but your first parameter passed to the addLibrarySound method should not contain quotes. It is not a string but a linkage ID to the class (in your case basicBulletSoundClass, etc) and when you wrap it in a string flash gets confused and doesnt know its a class you're passing as a linkage id.

I couldn't figure out why this error was happening because i specifically remembered testing that method when I made the class and it was working fine. a second set of eyes helped a ton. sometimes its the smallest things!

Oh wow!

Thank you, I can finally add some sounds to my game!

/KicksSelf

Great job mate, well done! you saved me a lot of time...

it won't be a bad idea to release updated version of the class for the new version of TweenLite/Max.

that is a good point, vitaLee, I will add that to my "todo" list :P

@Andreas @Giorgos thanks for the kind words.

Matt, actually it turned out that it's not that good idea.
at first i thought that according to the new plugin architecture
TweenPlugin.activate([VolumePlugin]) should be added to the class, but i found out that VolumePlugin and 6 more plugins are activated by default in tweenlite to preserve backward compitability.
so there's no need to touch the class.
my confusion came from a certain situation and i think the class needs a feature. i tried to fade in external sound before i played it and error popped out. it would be better when calling SoundManager.fadeSound();
to check if the file was played and if not to fade it in automatically.
otherwise its kinda ugly to do somthing like this:
SoundManager.getInstance().addExternalSound('song.mp3', 'theme');
SoundManager.getInstance().playSound('theme', 0)
SoundManager.getInstance().fadeSound('theme', 1, 3);

that makes sense, i'll have to look into the best way to do this when I'm down to editing the class. thanks for the input.

Hi,

Thank you, it is really a useful class. I want to suggest a new method for adding already downloaded sounds if you are using a download manager class for queuing or prioritizing.

/**
* Adds an already downloaded sound to dictionary for playin immediately.
*
* @param $name The string identifier of the sound to be used when calling other methods on the sound
* @param $sound Sound object
* @return
*/
public function addLoadedSound($name:String, $sound:Sound):Boolean {
for (var i:int = 0; i < this._sounds.length; i++)
{
if (this._sounds[i].name == $name) return false;
}
var sndObj:Object = new Object();
sndObj.name = $name;
sndObj.sound = $sound;
sndObj.channel = new SoundChannel();
sndObj.position = 0;
sndObj.paused = true;
sndObj.volume = 1;
sndObj.startTime = 0;
sndObj.loops = 0;
sndObj.pausedByAll = false;
this._soundsDict[$name] = sndObj;
this._sounds.push(sndObj);
return true;
}

@bulupe: i am planning on doing some download managing in the next iteration of the class as someone else suggested so we'll see if a method like this will become necessary or not, but thank you for your input.

Hi,

Is there any way to have an onComplete event for fadeSound method, it would be really useful to have a listener on that, if not, any to cheat to acheive this?

Cheers,
Girogos

@Giorgos: since the fadeSound method uses TweenLite, you could edit it to include an onComplete: call to the TweenLite.to call. For more info, visit http://www.tweenlite.com to learn how to add callbacks .

Hey, one bug I would like to point out here.
When you play a looping sound it will loop from the position argument given in the play method. This means that when you pause and then unpause a looping sound just at the end of it, it will only loop that little bit left at the end, not continue from the beginning.
I just noticed this in my own sound manager class and thought I would point it out :)

Stuart,
I'll look into this for the next iteration as well. thanks.

[...] out this class, AS3 SoundManager, created by Matt Przybylski which contains a whole load of methods that make adding sounds to your AS3 projects super simple. [...]

Thanks for this buddy... I'll be testing it out straight away :)

Hello,

Any ideas of how to find if any of the loaded sounds is currentlly playing? How do I get something like this?
I know I can use the pauseAllSounds method however I need to get just the sound that currently playing so I can fade out the volume of it and then pause it.

Cheers,

great class Matt!!! really good contribution for us!

I added a simple method to add run-time library items...

/**
* Adds a sound from the a runtime shared library to the sounds dictionary for playing in the future.
*
* @param $loaderObj The parent containing the loaderInfo object...
* @param $linkageID The class name of the library symbol that was exported for AS
* @param $name The string identifier of the sound to be used when calling other methods on the sound
*
* @return Boolean A boolean value representing if the sound was added successfully
*/
public function addRuntimeLibrarySound($loaderObj:*, $linkageID:*, $name:String):Boolean
{
for (var i:int = 0; i < this._sounds.length; i++)
{
if (this._sounds[i].name == $name) return false;
}

var _soundClass:Class = $loaderObj.loaderInfo.applicationDomain.getDefinition($linkageID) as Class;

var sndObj:Object = new Object();
var snd:Sound = new _soundClass();

sndObj.name = $name;
sndObj.sound = snd;
sndObj.channel = new SoundChannel();
sndObj.position = 0;
sndObj.paused = true;
sndObj.volume = 1;
sndObj.startTime = 0;
sndObj.loops = 0;
sndObj.pausedByAll = false;

this._soundsDict[$name] = sndObj;
this._sounds.push(sndObj);

return true;
}

@Giorgos: there isnt a built in method to do this, but you could loop through all the sounds in the sounds array and check which one is not paused, then do your operations on that.

Good base for a very needed open source sound manager. It'd be great to have this SoundManager do for sounds what TweenMax does for tweening : )

I'm looking forward to further updates/releases.

Cool stuff. What software did you use to generate your documentation. Looks clean and "pretty".

@Kalani: I used the online tool ZenDoc by senocular. http://www.zendoc.org

I would like to attach some events to this, ProgressEvent, complete, when a song ends etc etc.

I have tried to toss a few things in there with limited success.

Thoughts?

@rob: events are one of the things I'd like to incorporate into this when I get some time. Its on the "to do" list, unfortunately that list is getting rather long and no time to hit any of those items currently.

Nice work Matt. Guttershark has a sound manager as well..

The flavor is a bit different. You can control sounds in groups, or individually.

http://codeendeavor.com/gsdocs/net/guttershark/managers/SoundManager.html

Supporting classes:

http://codeendeavor.com/gsdocs/net/guttershark/support/soundmanager/package-detail.html

Any idea why the fade function would be breaking on me with the latest tweenlite?

Sound plays, but it breaks and won't fade:

var sndTracker:SoundManager = SoundManager.getInstance();
sndTracker.addLibrarySound(Cheer,"cheerSnd");
sndTracker.playSound('cheerSnd', 1, 0, 0);
sndTracker.fadeSound('cheerSnd', 0, 4);

Getting this error:
Property volume not found on flash.media.SoundChannel and there is no default value

@Myron: If by latest you are referring to the v11 beta, I'm not certain but it may have to do with volume being a plugin that is not activated in TweenLite by default anymore. You'll have to do this:

Actionscript:
  1. import com.greensock.plugins.*;
  2.  
  3. TweenPlugin.activate([VolumePlugin]);

Hopefully that should fix it but I don't have time to test it at the moment so let me know.

Hi Matt,

I'm using your class and the ease of use is really a great help. Thanks.

Right now I'm using it for a game where variations of a song are played depending on the situation. E.g. in Super Mario World, when you ride on Yoshi, the music gets an additional drums track.

It works fine with simultaneously running music tracks, where the variations are managed via volumes. But the increasing number of running tracks is simply too performance-consuming.

My other approach is to read the current track's position and use that to start the other track. This revealed two issues:

1) Since every sound registered with addLibrarySound starts out with paused=true, playSound can never actually use the startTime parameter, or can it?
2) I changed the code to always use the starttime, but the switching is incorrect, the music stutters and the rhythm is destroyed.
Tracing the soundposition shows that the newly started track seems to have a start lag (sound position stays for a few frames). Do you have similar experience? Is it a Flash Player limitation?

I will try to create a sample file for you to test, but right now I lack sound files I'm allowed to actually send you.

At any rate, any feedback in the meanwhile would be highly appreciated.

Best,
YC

Hi, where i can find an example of usage of this class?
I do not know how to use it.

Thanks.

@Joe: The example is at the top of the class in the comments.

Hi, i have try with:
#######
import "com.reintroducing.sound.SoundManager.as"

var manageSound:SoundManager=SoundManager.getInstance();

manageSound.addLibrarySound(loop, 'loop');
manageSound.playSound('loop', 1, 0, 0);
manageSound.fadeSound('loop', 0, 4);
#######

but is not working..
where mistake?

Hi, i have try and now all work!!

####
import com.reintroducing.sound.SoundManager;

var manageSound:SoundManager=SoundManager.getInstance();

manageSound.addLibrarySound(loop, "loop");
manageSound.playSound("loop", 0, 0, 999999);
manageSound.fadeSound("loop", 0.5, 2);

//manageSound.pauseSound("loop");
//manageSound.stopSound("loop");
####

Thanks for you support!!
Very nice class!!

I modified the class a little bit

/**
* Fades the sound to the specified volume over the specified amount of time.
*
* @param $name The string identifier of the sound
* @param $targVolume The target volume to fade to, between 0 and 1 (default: 0)
* @param $fadeLength The time to fade over, in seconds (default: 1)
* @param $stopOnComplete Added by Danny Miller from K2xL, stops the sound once the fade is done if set to true
* @return void
*/
public function fadeSound($name:String, $targVolume:Number = 0, $fadeLength:Number = 1, $stopOnComplete:Boolean = false):void
{
var fadeChannel:SoundChannel = this._soundsDict[$name].channel;
if ($stopOnComplete)
{
TweenLite.to(fadeChannel, $fadeLength, {volume: $targVolume,onComplete:stopSound,onCompleteParams:[$name]});
}
else
{
TweenLite.to(fadeChannel, $fadeLength, {volume: $targVolume});
}
}

@Danny Miller: nice addition, i'll add that to the future release.

Speaking of which, I've actually worked on this class quite a bit and am gearing up to release a new version hopefully sooner than later although i'm still battling with a couple of issues. Once I get those squared away I'll post it.

This saves me writing a bunch of code... Thanks!

Hi Matt,

Do you know the situation with garbage collecting completed sounds? You fixed two issues in 1.2 after we last emailed, but I am using the previous version after all.

That's because I need overlapping sounds, in other words, play a sound even if it is already playing (think projectile firing sound where you cannot anticipate the amount of the required channels).

As I'm optimizing the performance of my game right now, I'm worried about that old version of playSound(). Overlapping sounds means something more is going on in that channel, but I don't know exactly what, and to what performance impact. Do I end up with a ridiculously bloated channel when having played a sound hundreds of times?

The other option is to use 1.2 in order to maintain the 1-sound-per-channel clarity. But for easy overlapping sound, I'm considering duplicating the sound internally when a playing sound name is requested again. But that would lead to more performance hit, wouldn't it? What happens when a sound finishes playing, anyway?

What would you recommend for my situation (which shouldn't be uncommon in game uses)?

If you could shed some light on the matter, I'd highly appreciate it. Thanks in advance.

Greets,
YC

I'm using the class on a current project and unmuteAllSounds() is not working at all when I choose to fade the sounds... How can this be fixed?

[...] SoundManager – Makes adding sounds to your projects super simplehttp://evolve.reintroducing.com/2008/07/15/news/as3-soundmanager/ [...]

[...] SoundManager - Makes adding sounds to your projects super simple http://evolve.reintroducing.com/2008/07/15/news/as3-soundmanager/ [...]

[...] SoundManager – Makes adding sounds to your projects super simple http://evolve.reintroducing.com/2008/07/15/news/as3-soundmanager/ [...]

Hi,
/*For BulkLoader - or similar- users */
using this class on a current project i've found usefull a new function to add a preloaded sound from a multiple files loader class (BulkLoader.as).
I've resolve this issue adding the following function:

/**
* Adds an preloaded sound to the sounds dictionary for playing in the future.
*
* @param $snd Sound object contaning the preload sound
* @param $name The string identifier of the sound to be used when calling other methods on the sound
*
* @return Boolean A boolean value representing if the sound was added successfully
*/
public function addPreloadedSound($snd:Sound,$name:String):Boolean
{

var sndObj:Object = new Object();

sndObj.name = $name;
sndObj.sound = $snd;
sndObj.channel = new SoundChannel();
sndObj.position = 0;
sndObj.paused = true;
sndObj.volume = 1;
sndObj.startTime = 0;
sndObj.loops = 0;
sndObj.pausedByAll = false;

this._soundsDict[$name] = sndObj;
this._sounds.push(sndObj);

return true;
}

It can be called this way:
var manageSound:SoundManager = SoundManager.getInstance();
manageSound.addPreloadedSound(_loader.getSound("soundtrack"), 'theSoundtrack');

Supposing "soundtrack" as the id of the bulkloader item added in the preload process.

Hope to be usefull for someone!

Bye!

@Nuajan That's a good addition and one I didn't think of. I've added it to the class as well as some other stuff that I never posted. It's at version 1.4 right now and I'm contemplating posting it online. I think I took care of a good amount of bugs so I may do so in the coming days.

[...] SoundManager – Makes adding sounds to your projects super simple http://evolve.reintroducing.com/2008/07/15/news/as3-soundmanager/ [...]

Hey matt.. Really nice library. Thanks. I just want to know whether it plays only the sounds that are exported in first frame. Because exporting all sounds to first frame creates burden on first frame.

@vikram: It depends on what method you use (adding library sound or external sound) and also how you handle your FLA in general. Normally what I do is have a main.fla where i house all my assets and everything which has clips associated with classes exported in the first frame and then I load that into my loader.fla which has only assets pertaining to my loader. This way I don't have to worry about exporting in first frame or not. Hope that helps.

[...] SoundManager - Makes adding sounds to your projects super simple http://evolve.reintroducing.com/2008/07/15/news/as3-soundmanager/ [...]

Hey Matt,

I see that you mention an updated version of your code i.e. 1.4. Why don't you post it? Because I intend to use an update version of your code with fixes and additions people have posted.

If you don't want to post it, can you email it to me?

Thanks

Hey Matt sweet class, I'm having trouble playing multiple instances of the same sound though. When I do I lose the ability to mute, stop or change the volume. Have you dealt with this?

@Graham: Please use SoundManager v1.4 as linked at the top of this post. If you are still having issues with that, if you could isolate the problem in a separate FLA and send me an example I'd appreciate it.

[...] SoundManager – Makes adding sounds to your projects super simple http://evolve.reintroducing.com/2008/07/15/news/as3-soundmanager/ [...]

Love the SoundManager. Thank you very much for setting this all up.

I found a small issue, which does not appear to be related to SoundManager, but that others may run into regarding sound loading. When loading AAC/M4A files into Flash, the duration appears to be incorrectly represented, which throws a wrench into buffering / playing.

I filed a bug with Adobe (https://bugs.adobe.com/jira/browse/FP-6251), and hope there's a fix soon. In the meantime, if anyone has a workaround, I'd love to hear it.

@Jeffery: Thanks for the kind words. Good catch on the AAC/M4A files, I never load anything other than MP3 files to be honest so I wasn't aware of the issue with duration and those filetypes.

@Everyone: Moving forward, comments should be directed to the new SoundManager post that is outlined as an Update at the top of this one.

I have a little-big problem with the library SoundManager. when you run the PlaySound method, uses the play method of the library flash.media.Sound. According to the documentation, this method should return a channel, but may also return a Null ( http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/Sound.html#play() )

Has not seen this option, and I get many times.

while looping, there is gap in the beginning. it is a flash bug but you can solve it.

[...] SoundManager – упрощает добавление звуков в проекты. Клёвый код, я на его основе сделал небольшой класс для работы со звуками. [...]

[...] TempoLite – маленькая и эффективная медиа-библиотека. SoundManager – упрощает добавление звуков в проекты. Клёвый код, я [...]

Leave a comment

(required)

(required)