Skip to content

Instantly share code, notes, and snippets.

@FrancescoMaisto
Last active November 9, 2019 20:22
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save FrancescoMaisto/5228998 to your computer and use it in GitHub Desktop.
Save FrancescoMaisto/5228998 to your computer and use it in GitHub Desktop.
This ActionScript class makes dealing with sounds in Starling very easy and intuitive: once you add a sound to the SoundManager you can easily play it, stop it, change its volume, fade it in, fade it out or cross-fade it with another sound.See more details and usage examples below, in the first comment.
package starling.extensions {
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.utils.Dictionary;
import starling.core.Starling;
public class SoundManager {
private static var _instance:SoundManager;
private var _isMuted:Boolean = false; // When true, every change in volume for ALL sounds is ignored
public var sounds:Dictionary; // contains all the sounds registered with the Sound Manager
public var currPlayingSounds:Dictionary; // contains all the sounds that are currently playing
public function SoundManager()
{
sounds = new Dictionary();
currPlayingSounds = new Dictionary();
}
// -------------------------------------------------------------------------------------------------------------------------
public function dispose():void {
sounds = null;
currPlayingSounds = null;
}
// -------------------------------------------------------------------------------------------------------------------------
/** Add sounds to the sound dictionary */
public function addSound(id:String, sound:Sound):void {
sounds[id] = sound;
}
// -------------------------------------------------------------------------------------------------------------------------
/** Remove sounds from the sound manager */
public function removeSound(id:String):void {
if (soundIsAdded(id)) {
delete sounds[id];
if (soundIsPlaying(id))
delete currPlayingSounds[id];
}
else {
throw Error("The sound you are trying to remove is not in the sound manager");
}
}
// -------------------------------------------------------------------------------------------------------------------------
/** Check if a sound is in the sound manager */
public function soundIsAdded(id:String):Boolean {
return Boolean(sounds[id]);
}
// -------------------------------------------------------------------------------------------------------------------------
/** Check if a sound is playing */
public function soundIsPlaying(id:String):Boolean {
for (var currID:String in currPlayingSounds) {
if ( currID == id )
return true;
}
return false;
}
// -------------------------------------------------------------------------------------------------------------------------
/** Play a sound */
public function playSound(id:String, volume:Number = 1.0, repetitions:int = 1, panning:Number = 0):void {
if (soundIsAdded(id))
{
var soundObject:Sound = sounds[id];
var channel:SoundChannel = new SoundChannel();
channel = soundObject.play(0, repetitions);
if (!channel)
return;
channel.addEventListener(Event.SOUND_COMPLETE, removeSoundFromDictionary);
// if the sound manager is muted, set the sound's volume to zero
var v:Number = (_isMuted)? 0 : volume;
var s:SoundTransform = new SoundTransform(v, panning);
channel.soundTransform = s;
currPlayingSounds[id] = { channel:channel, sound:soundObject, volume:volume };
}
else
{
throw Error("The sound you are trying to play (" + id + ") is not in the Sound Manager. Try adding it to the Sound Manager first.");
}
}
// -------------------------------------------------------------------------------------------------------------------------
/** Remove a sound from the dictionary of the sounds that are currently playing */
private function removeSoundFromDictionary(e:Event):void {
for (var id:String in currPlayingSounds)
{
if (currPlayingSounds[id].channel == e.target)
delete currPlayingSounds[id];
}
e.currentTarget.removeEventListener(Event.SOUND_COMPLETE, removeSoundFromDictionary);
}
// -------------------------------------------------------------------------------------------------------------------------
/** Stop a sound */
public function stopSound(id:String):void {
if (soundIsPlaying(id))
{
SoundChannel(currPlayingSounds[id].channel).stop();
delete currPlayingSounds[id];
}
}
// -------------------------------------------------------------------------------------------------------------------------
/** Stop all sounds that are currently playing */
public function stopAllSounds():void {
for (var currID:String in currPlayingSounds)
stopSound(currID);
}
// -------------------------------------------------------------------------------------------------------------------------
/** Set a sound's volume */
public function setVolume(id:String, volume:Number):void {
if (soundIsPlaying(id))
{
var s:SoundTransform = new SoundTransform(volume);
SoundChannel(currPlayingSounds[id].channel).soundTransform = s;
currPlayingSounds[id].volume = volume;
}
else
{
throw Error("This sound (id = " + id + " ) is not currently playing");
}
}
// -------------------------------------------------------------------------------------------------------------------------
/** Tween a sound's volume */
public function tweenVolume(id:String, volume:Number = 0, tweenDuration:Number = 2):void {
if (soundIsPlaying(id))
{
var s:SoundTransform = new SoundTransform();
var soundObject:Object = currPlayingSounds[id];
var c:SoundChannel = currPlayingSounds[id].channel;
Starling.juggler.tween(soundObject, tweenDuration, {
volume: volume,
onUpdate: function():void {
if (!_isMuted)
{
s.volume = soundObject.volume;
c.soundTransform = s;
}
}
});
}
else
{
throw Error("This sound (id = " + id + " ) is not currently playing");
}
}
// -------------------------------------------------------------------------------------------------------------------------
/** Cross fade two sounds. N.B. The sounds that fades out must be already playing */
public function crossFade(fadeOutId:String, fadeInId:String, tweenDuration:Number = 2, fadeInVolume:Number = 1, fadeInRepetitions:int = 1):void {
// If the fade-in sound is not already playing, start playing it
if (!soundIsPlaying(fadeInId))
playSound(fadeInId, 0, fadeInRepetitions);
tweenVolume (fadeOutId, 0, tweenDuration);
tweenVolume (fadeInId, fadeInVolume, tweenDuration);
// If the fade-out sound is playing, stop it when its volume reaches zero
if (soundIsPlaying(fadeOutId))
Starling.juggler.delayCall(stopSound, tweenDuration, fadeOutId);
}
// -------------------------------------------------------------------------------------------------------------------------
/** Sets a new volume for all the sounds currently playing
* @param volume the new volume value
*/
public function setGlobalVolume(volume:Number):void {
var s:SoundTransform;
for (var currID:String in currPlayingSounds) {
s = new SoundTransform(volume);
SoundChannel(currPlayingSounds[currID].channel).soundTransform = s;
currPlayingSounds[currID].volume = volume;
}
}
// -------------------------------------------------------------------------------------------------------------------------
/** Mute all sounds currently playing.
* @param mute a Boolean dictating whether all the sounds in the sound manager should be silenced (true) or restored to their original volume (false).
*/
public function muteAll(mute:Boolean = true):void {
if (mute != _isMuted)
{
var s:SoundTransform;
for (var currID:String in currPlayingSounds)
{
s = new SoundTransform(mute ? 0 : currPlayingSounds[currID].volume);
SoundChannel(currPlayingSounds[currID].channel).soundTransform = s;
}
_isMuted = mute;
}
}
// -------------------------------------------------------------------------------------------------------------------------
public function getSoundChannel(id:String):SoundChannel {
if (soundIsPlaying(id))
return SoundChannel(currPlayingSounds[id].channel);
throw Error("You are trying to get a non-existent soundChannel. Play the sound first in order to assign a channel.");
}
// -------------------------------------------------------------------------------------------------------------------------
public function getSoundTransform(id:String):SoundTransform {
if (soundIsPlaying(id))
return SoundChannel(currPlayingSounds[id].channel).soundTransform;
throw Error("You are trying to get a non-existent soundTransform. Play the sound first in order to assign a transform.");
}
// -------------------------------------------------------------------------------------------------------------------------
public function getSoundVolume(id:String):Number {
if (soundIsPlaying(id))
return currPlayingSounds[id].volume;
throw Error("You are trying to get a non-existent volume. Play the sound first in order to assign a volume.");
}
// --------------------------------------------------------------------------------------------------------------------------------------
// SETTERS & GETTERS
public function get isMuted():Boolean { return _isMuted; }
}
}
@FrancescoMaisto
Copy link
Author

SoundManager.as

This class makes dealing with sounds in Starling very easy and intuitive: once you add a sound to the SoundManager you can easily play it, stop it, change its volume, fade it in, fade it out or cross-fade it with another sound.

Examples

Create your SoundManager:

soundManager = SoundManager.getInstance();

In order to control a sound, we have to add it to the SoundManager.
In the following example we add an object of type Sound (called "backgroundMusic") to the SoundManager and name it "music1":

soundManager.addSound("music1", backgroundMusic );

Here we add a sound to the soundManager straight from Starling's asset manager instead:

soundManager.addSound("music2", assets.getSound("music2"));

Let's play one of the sounds we just added:

soundManager.playSound("music1");

Play a sound at half of its original volume and repeat it 999 times:

soundManager.playSound("music2", 0.5, 999);

Stop a sound:

soundManager.stopSound("music1");

Change the volume of a sound to 80% of its original value:

soundManager.setVolume("music1", 0.8);

Gradually lower the volume of a sound to half of its original value, making the transition last 2 seconds:

soundManager.tweenVolume("music1", 0.5, 2);

Fade-out a sound in 5 seconds:

soundManager.tweenVolume("music1", 0, 5);

Cross-fade between two sounds, the transition will take 5 seconds ("music2" fades out, "music1" fades in, both sounds must be already playing):

soundManager.crossFade("music2", "music1", 5);

If the sound that is fading-in ("music1") is not playing already, the crossFade method will start playing it automatically, in that case we tell the SoundManager what its final volume will be (0.7) and how many times we want "music1" to loop (999):

soundManager.crossFade("music2", "music1", 5, 0.7, 999);

@tonghae
Copy link

tonghae commented Jul 25, 2013

Nice lib but it lacks an ability to play music in a loop (not "999", "9999" etc)

@FrancescoMaisto
Copy link
Author

Thanks, that's true, but in which real-world scenario do you need to loop music more than 9999 times?

Also, you're welcome to add that ability yourself and I will add it to the class :-)

@rekomat
Copy link

rekomat commented Nov 20, 2013

Hi Francesco

Thanks for sharing! :)

I just implemented the SoundManager in my current project and there's one thing I stumbled across: When I change the global volume, the change only affects currently playing sound, not sounds that will play at a later time. I found this not so comfortable, specially for sound effects as I have to store the global volume somewhere outside SoundManager and pass the volume as param every time I play an effect.

Ok – cut the crap! – I made some changes to store the global volume but I'm to stupid to push the changes (not so savy with gists). If you're interested: give me a hint! :)

Here's some dummy code to demonstrate what I changed.

setGlobalVolume(0.8);
playSound('mySound', 0.5); // will play at effective volume 0.4
setGlobalVolume(0.9);      // mySound will play at effective volume 0.45
playSound('myOtherSound'); // myOtherSound will play at effective volume 0.9

I made the changes just the other night, so it's not tested well enough yet, but so far everything seems to be good.

Cheers,
René

@FrancescoMaisto
Copy link
Author

Hi René!
Sorry for the late reply, I just noticed your comment - for some reason Gist doesn't notify me of new comments.
I see you point and it's a good one, I'll look into it and modify the setGlobalVolume() method the way you have suggested.

Thanks for the feedback,
Francesco

@bali33
Copy link

bali33 commented Feb 20, 2014

Hi,

Is there a way to add a Sound by passing the mp3 file path ? This would be very convenient in order to stream sound.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment