In this side tutorial we are going to be adding a soundbank that will load all of our sounds, and then we call play them via an ID whenever we want. This tutorial will only deal with sounds, not music, and mind you it's a very basic tutorial that gets the job done. There is much that can be added to this class, for channels, groups and such, but we're dealing with basics here. We'll base this tutorial off of my SDL Events tutorial. So use those project files if you need something to work off of.

The first thing you need to do is download SDL mixer from the SDL website. It's also included in the SDL library I provide on my website. Be sure to put the include files in the same directory as your SDL include files, and your lib files in the same directory as your SDL lib files to make things easier.

Start by opening your project, and going under the linker settings. Add SDL_mixer after SDLmain, something like:

mingw32
SDLmain
SDL_mixer
SDL

If you don't remember where to find all these settings, hope on over to the first tutorial SDL Tutorial Basics to get a refresher on linking. If you have a .lib file, instead of a .a library file, use the browse button to find the library file that way.

Just a little side note here about sounds. SDL_mixer treats any sound in memory as a chunk. This chunk basically contains the frequency data. What's nice about SDL_mixer is that it takes this data and sends it to the audio output, meaning it has taken care of all the hard work. So all that we have to do is load our data, from a WAV file for instance, and then send it to SDL_mixer to be played.

First, create two new files, CSoundBank.h and CSoundBank.cpp. Open up the header file first:
#ifndef _CSOUNDBANK_H_
    #define _CSOUNDBANK_H_

#include <SDL.h>
#include <SDL_mixer.h>
#include <vector>

class CSoundBank {
    public:
        static CSoundBank           SoundControl;

        std::vector<Mix_Chunk*>     SoundList;

    public:
        CSoundBank();

        int OnLoad(char* File);

        void OnCleanup();

    public:
        void Play(int ID);
};

#endif
We have a static control object first, this lets us add sounds and play sounds from anywhere in our program. Think of this as your master control for adding/playing sounds. We then have our SoundList, this is a list of SDL mixer sounds. Now, a Mix_Chunk object contains the information needed to play a sound. What we will do later on is load a wav file into a Mix_Chunk object. Think of it as the sound equivalent to SDL_Surface. Then we have some basic functions for loading, cleanup up, and playing a sound. Simple enough.

Next, open up CSoundBank.cpp:
#include "CSoundBank.h"

CSoundBank CSoundBank::SoundControl;

CSoundBank::CSoundBank() {
}

int CSoundBank::OnLoad(char* File) {
    Mix_Chunk* TempSound = NULL;

    if((TempSound = Mix_LoadWAV(File)) == NULL) {
        return -1;
    }

    SoundList.push_back(TempSound);

    return (SoundList.size() - 1);
}

void CSoundBank::OnCleanup() {
    for(int i = 0;i < SoundList.size();i++) {
        Mix_FreeChunk(SoundList[i]);
    }

    SoundList.clear();
}

void CSoundBank::Play(int ID) {
    if(ID < 0 || ID >= SoundList.size()) return;
    if(SoundList[ID] == NULL) return;

    Mix_PlayChannel(-1, SoundList[ID], 0);
}
This is also pretty straight forward. We have our static control, our empty constructor, and then our loading function. We basically take the filename of the sound to be loaded, and try to load it via Mix_LoadWAV into the Mix_Chunk object. Notice I am making a temporary object first, and then throwing it into the list. At the very end of the function, I return the ID of the last inserted sound. So, for instance, say I loaded SoundA and SoundB:
int SoundA = CSoundBank::SoundControl.OnLoad("sounda.wav");
int SoundB = CSoundBank::SoundControl.OnLoad("soundb.wav");
The ID for playing this sounds later would be returned by the OnLoad function.

Okay, next we have our cleanup function which goes through the list and frees the sound chunks. Simple enough, just like SDL_FreeSurface.

Lastly, we have our Play function that actually plays the sound. We pass an ID (the one we got returned from the Load function) first. We do a little boundary check to make sure the ID is valid, and then actually try to play the sound. The Mix_PlayChannel takes three arguments:

The first specifies which channel to play the sound on, -1 means use the first available.
The second specifies which sound chunk to play.
The last specifies how many times to play this sound (loop). -1 would be infinite.

We haven't done much with channels in this class, but the class is easily changeable to add that functionality. Basically, a channel allows you to set certain settings to certain sounds. For instance, you can set volume or panning on the first channel, so any sounds played on that channel would be affected.

So lets do a little test now. Open up CApp.h and add the header file, and add some variables and functions to the class:
#include "CSoundBank.h"

//...

class CApp {
    public:
        int SoundA;
        int SoundB;

        //... other code

    public:
        void OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode);
};
Now, open up CApp_Init.cpp so we can load some sounds:
if((SoundA = CSoundBank::SoundControl.OnLoad("sounda.wav")) == -1) {
    return false;
}

if((SoundB = CSoundBank::SoundControl.OnLoad("soundb.wav")) == -1) {
    return false;
}
Now, to make sounds when we press a key, open up CApp_OnEvent.cpp and add the function:
void CApp::OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode) {
    if(sym == SDLK_1) {
        CSoundBank::SoundControl.Play(SoundA);
    }

    if(sym == SDLK_2) {
        CSoundBank::SoundControl.Play(SoundB);
    }
}
We aren't done yet, we have to do a little bit to start SDL_mixer. This is just like starting up SDL, but instead we need to start up SDL_mixer to access sounds. Open up CApp_Init.cpp and add the following somewhere after SDL_Init:
if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0) {
    return false;
}
Mix_OpenAudio basically Inits SDL_mixer. This function takes 4 arguments. The first is used to specify the frequency, 44100 is usually a good number. But depending on your wav files, you may have to change this. Another common number is 22050. I won't get into detail about frequencies and such, just leave it alone unless you really need to change it. The next is the format, again, it's best to leave it unless you know much about audio (it's basically the size of the samples used, 8bit or 16bit). The next is the number of channels, 1 = mono, 2 = stereo. Note, these channels are different than the other channels I was talking about before. The other channels are "mixing channels," basically streams that I can push audio through. These channels though, are the number of audio outputs. Think of it like the number of speakers that you might have. 2 speakers, 2 channels. The last number is used to set the size of the samples (in bytes). Again, unless you really know what you are doing, it's best to leave these numbers alone.

To tidy things up, we need to make sure we stop the SDL_mixer service when we are done, as well as freeing any sounds we may have loaded. So open up CApp_OnCleanup.cpp and add the following:
CSoundBank::SoundControl.OnCleanup();

Mix_CloseAudio();
And we are done! Try compiling, and pressing the 1 and 2 keys to here some sounds. If you need some WAV files, download the project files below to grab them.