Skip to content

Instantly share code, notes, and snippets.

@FrancescoMaisto
Last active November 9, 2019 20:22
Show Gist options
  • 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

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