-
-
Save FrancescoMaisto/5228998 to your computer and use it in GitHub Desktop.
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; } | |
} | |
} |
Nice lib but it lacks an ability to play music in a loop (not "999", "9999" etc)
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 :-)
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é
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
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.
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:
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":Here we add a sound to the soundManager straight from Starling's asset manager instead:
Let's play one of the sounds we just added:
Play a sound at half of its original volume and repeat it 999 times:
Stop a sound:
Change the volume of a sound to 80% of its original value:
Gradually lower the volume of a sound to half of its original value, making the transition last 2 seconds:
Fade-out a sound in 5 seconds:
Cross-fade between two sounds, the transition will take 5 seconds ("music2" fades out, "music1" fades in, both sounds must be already playing):
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):