XNA Tutorial - Sound Manager
Making a sound manager is easy as 1, 2, 3 so why don’t you learn with me :)
An issue that came up when messing with my current engine was dealing with sounds. I knew off the bat that just using the standard methods of calling sound.Play() using the SoundEffect class wouldn't cut it. What if we want no music to play? What if we want to mute all sounds? Then we are put in quite the pickle, I'm mean if you're a hardy fellow you can put an if infront of each sound call. What I also knew I wanted to be able to do was just call the sound to play and not have to deal with it past that. In example: SoundManager.Instance.Play("soundName", 100 ) and then bam the sound plays. We can also apply any other effects we want with a modified constructor. I also knew I wanted to have effects that could change all the noises active.
So with these facts laid out I immediately thought of my best friend Mr.Singleton Pattern. Using him we can have our calls at a global level and not have to deal with a disgusting tentacle class ( what I sometimes call important classes that need to be passed through the constructors of nearly any class, for example how we pass spritebatch through to anything with a draw method and that then tends to be passed even deeper ). Then the other thing that sounds nice would be to keep all the background work contained to one class, this would store all the sounds in the games content folder and hold all the playing sounds and remove them as needed. Some may concider this gross and I don't blame them, but I like it. Feel free to just keep it all in one class.
Now let's start off with a class called something like "SoundManager". In this class let's put the good ole fashion singleton pattern:
public static SoundManager Instance { get { if (instance == null) // if the instance doesn't exist then create it { instance = new SoundManager(); }
return instance; // returns itself } }
For those of you who don't know what this is, it is a pattern that essentially states "Hey, there's only ever going to be one of me so like why bother instantiating me all around your code and just instantiate me once in myself." What I mean by that is the singleton pattern creates a global point of access to the class, and it ensures only one instance of the class is ever created.
This is the fundemental part of making this approach work.
Now we're going to go ahead and move to the other class for a second. We're going to now create a class called SoundDictionary where we'll store all the work that will actually be done with the sounds for the most part. In here we need to create a dictionary for our sounds to be loaded in, an arraylist to hold the active sounds and an arraylist to hold the sounds that need to be removed.
Dictionary<string, SoundEffect> sounds = new Dictionary<string, SoundEffect>(); ArrayList activeSounds; ArrayList soundsToRemove;
Now we need a way of adding sounds to the dictionary from the outside. Create a function called AddSound that takes in a soundeffect and a string.
public void AddSound(SoundEffect sound, string name) { if (!sounds.ContainsKey(name)) { sounds.Add(name, sound); } }
It's pretty self-explanatory there, just makes sure the name isn't already loaded in and if it isn't it adds it in.
Now we can get down to the Update function. Stick a standard Update in there with GameTime in the constructor if you want.
Now we need a foreach loop to make sure all the sounds are still running, and if not we'll take them out.
foreach (SoundEffectInstance s in activeSounds) { if (s.State == SoundState.Stopped) soundsToRemove.Add(s); }
After that we gotta actually remove those useless guys:
if (soundsToRemove.Count > 0) foreach (SoundEffectInstance s in soundsToRemove) { s.Dispose(); activeSounds.Remove(s); }
Now we need a function that will actually play the sound:
public void PlaySound(string name) { if (sounds.ContainsKey(name) && !disableSounds) { if ((!(disableSongs && name.Length > 4 && name.Substring(0, 5) == "Song"))) { SoundEffect soundEff = (SoundEffect)sounds[name]; SoundEffectInstance sin = soundEff.CreateInstance(); sin.Play(); activeSounds.Add(sin); } } }
We just make sure the sound of the given name exists and that sounds aren't disabled ( Just create two booleans: disableSounds and disableSongs ). At the heart all this does is make sure the sound is okay to play and if so creates an instance of the sound and plays it, then adds it to a list of active sounds. We can make all sorts of variants of this function now changing different properties of the SoundEffectInstance class.
Now what about those times when we want sound to just stop:
public void StopAllSounds() { foreach (SoundEffectInstance s in activeSounds) { s.Stop(); s.Dispose(); } activeSounds.Clear(); }
Whe can make modifications to this to do some fun stuff. I had my sounds dim out and wobble ear to ear. I'll include that in the link to my code at the bottom.
Now let's add one last function, a little guy meant to see if a dictionary key is already taken or not:
public bool ContainsSound(string name) { return sounds.ContainsKey(name); }
Okay, back to the Sound Manager. Almost there.
Let's create a private SoundDictionary for us to use. First let's add a LoadContent in there and pass through Content to it:
public void LoadContent(ContentManager Content) { DirectoryInfo DI = new DirectoryInfo(Content.RootDirectory + "\\Sounds"); FileInfo[] files = DI.GetFiles(); foreach (FileInfo f in files) { soundDirectory.AddSound(Content.Load<SoundEffect>("Sounds/" + Path.GetFileNameWithoutExtension(f.Name)), Path.GetFileNameWithoutExtension(f.Name)); } }
All this code does is automatically read in each sound in the Sound directory.
Now create an Update function and plug your SoundDictionary's update into it.
And lastly add a PlaySound function with name as a parameter:
public void PlaySound(string name) // Plays Given Sound { soundDirectory.PlaySound(name); }
Now that's that, you're done with your basic Sound Manager. Ez-pz. Maybe next time I'll do something a bit higher level. Like how to get an entity-part system off the ground in XNA. This is a very basic approach, I suggest taking what you now know and expand out from here. I like the idea of sounds having a custom class so then they can just act on themselves and make it semi-modular so you can just stick on sound effects for it to then use. You can also streamline a decent amount of the code in this.
If you have suggestions for how I should do my tutorials from now on lemme know. I’m pretty sure I’m mostly just making these for myself.
I'll put a link for the final code which includes a few extra things: Here
The linked code is a straight rip from an older version of my own project. Haven’t used it in a while but all seems like it should be plug and play for the most part.











