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.
Email (required, not published):
Website:
I've rethinkered this code to make it as decoupled as posible, depending only on a SDL and SDL_Mixer context being initialized.
Read about it on my blog:
http://heavydeck.wordpress.com/2012/04/03/toying-with-sdl-and-sdl_mixer/
Email (required, not published):
Website:
Email (required, not published):
Website:
Is there an easier way to implement this into my app?
I downloaded the source files and it was a lot, more than I expected. It also seems a bit difficult to get it to work, plus the exe app with it didn't work when I tried it. But that aside.
Basically I want to embed it, the code part only what I need. And than end up with a simple command like the "int SoundA" example so I can use something similar to just play sounds and thats it.
I have other stuff in my app and don't want this to become the main part of it. If you know what I mean.
Thanks.
Email (required, not published):
Website:
I tried to check the search directories, but they seems fine.
I also put all the dll's needed in system32 and in folder with my game.
It still errors me the same way.
It also seems to happen with SDL_image.
Can you help me?
Email (required, not published):
Website:
Email (required, not published):
Website:
1. Thanks for telling me about an error i made (-1 in place of -l) :)
2. The problem is that I have all dll's and headers fine, and I didn't found anything in tutorials about something else. On my other PC, it works fine with the same settings and data. Do I need an "SDL_image.a" file or something like that?
Email (required, not published):
Website:
My fail, both are getting the same error ^^'
Email (required, not published):
Website:
Email (required, not published):
Website:
Now only to download them...
Email (required, not published):
Website:
it's an L not a 1.
Email (required, not published):
Website:
Email (required, not published):
Website:
wouldn't it be better to create a destructor, which cleans up everything? I mean with a destructro you can't forget to clean up and get memory leaks, because the destructro will be called automaticilly when the object doesn't exist any more. What do you thing?
Email (required, not published):
Website:
Email (required, not published):
Website:
I don't know if this will be of any help to anyone, but just in case, I'll post this comment. I'd been sitting for hours trying to find the bug which made my program return "-1". I'd tried everything, pasting in the sample code and so on. Nothing helped.
Then suddenly I saw that I had been trying to load the sound files first, and then starting up the sound mixer afterwards.
I guess this is a pretty "n00bish" mistake, but maybe it will help someone like me :)
Also these are all fantastic tutorials! Thanks a lot!
Email (required, not published):
Website:
That's not normal, no. Can I see some of your audio settings and perhaps code to further help you?
Email (required, not published):
Website:
Email (required, not published):
Website: