SDL mixer:Tutorials:Playing a WAV Sound File

From GPWiki
Jump to: navigation, search

Playing a WAV Sound File With SDL_mixer

In this tutorial, we'll be using the SDL_mixer library to play a WAV sound file. Yes, some of you may already know that the standard SDL library is capable of playing sound files on its own, but what it lacks is the ability to play multiple sounds concurrently. For this you need a mixer, hence the need for SDL_mixer. SDL_mixer has a number of other nice features that the standard SDL library lacks, so there's no real reason to use the native SDL sound functions!

If you do not already have SDL_mixer installed, the how to set up your SDL build environment tutorial has some tips that may help you get things rolling.

Remember to include SDL.h and SDL_mixer.h at the top of your program.

Enough rambling preambling - on with the initialization of SDL and SDL_mixer:

if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
	fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
	return 1;
}
 
int audio_rate = 22050;
Uint16 audio_format = AUDIO_S16SYS;
int audio_channels = 2;
int audio_buffers = 4096;
 
if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) != 0) {
	fprintf(stderr, "Unable to initialize audio: %s\n", Mix_GetError());
	exit(1);
}

First, you'll notice that we are still using the standard SDL_Init function to initialize SDL. Not only that, but we're passing it the constants SDL_INIT_VIDEO and SDL_INIT_AUDIO... what's up with that? Well, we're passing SDL_INIT_VIDEO because we're going to need a small window from which to operate, and we pass SDL_INIT_AUDIO to get things ready for audio playback. Yes, SDL_INIT_AUDIO is a standard SDL constant, not an SDL_mixer constant, but it must be passed in order for SDL_mixer to work.

Next you'll see four variables being created and initialized with values. These four variables correspond to the four arguments we must pass to the Mix_OpenAudio function in order to initialize SDL_mixer. The purpose of these four arguments are as follows:

int frequency 
The first argument passed to Mix_OpenAudio sets the playback frequency to be used by SDL_mixer. A value of 22050 (22.05KHz) is ideal for most games. 22.05KHz is not quite CD quality (you may pass a value of 44100 for CD quality sound), but it is quite a bit easier on your CPU. If sound quality is more important to you than CPU cycles, then by all means, pass 44100. On most modern computers you won't notice the CPU hit.
Uint16 format 
There are a number of possible sample format constants that we can use here:
  • AUDIO_U8 - Unsigned 8-bit samples
  • AUDIO_S8 - Signed 8-bit samples
  • AUDIO_U16LSB - Unsigned 16-bit samples in little-endian byte order
  • AUDIO_S16LSB - Signed 16-bit samples in little-endian byte order
  • AUDIO_U16MSB - Unsigned 16-bit samples in big-endian byte order
  • AUDIO_S16MSB - Signed 16-bit samples in big-endian byte order
  • AUDIO_U16 - Same as AUDIO_U16LSB
  • AUDIO_S16 - Same as AUDIO_S16LSB
  • AUDIO_U16SYS - Unsigned 16-bit samples in the system's byte order
  • AUDIO_S16SYS - Signed 16-bit samples in the system's byte order
 
Most of these constants can be ignored, but were listed here for completeness. If you're using 8-bit WAV file samples, then use the constant AUDIO_S8. If you are using 16-bit WAV file samples, then use the constant AUDIO_S16SYS. AUDIO_S16SYS will automatically match the user's system's byte order, so you don't have to worry about the endianness of the target system! Now, if you're not sure whether or not your samples are 8-bit or 16-bit, simply try one constant or the other. If you try playing an 8-bit sample using the 16-bit format constant, the sample may sound garbled, or may play back at double speed. Trial and error works wonders!
int channels 
The channels argument will accept a value of 2 for stereo sound, and 1 for monaural.
int chunksize 
This value determines the size of the memory chunks used for storage and playback of samples. A value of 4096 should be suitable for most games. Increasing the value will decrease the CPU load but will also decrease responsiveness of playback. If you find the mixer's play/stop response times to be too slow, you may wish to decrease this value.

If the Mix_OpenAudio call fails, a value of -1 will be returned. The function Mix_GetError can then be used to obtain a string describing the cause of this (or any) SDL_mixer error.

Now that initialization is complete, we can load our WAV file into memory:

Mix_Chunk *sound = NULL;
 
sound = Mix_LoadWAV("sound.wav");
if(sound == NULL) {
	fprintf(stderr, "Unable to load WAV file: %s\n", Mix_GetError());
}

The Mix_LoadWAV function accepts a string path argument, which must point to our WAV file. (Note: Mix_LoadWAV() can also read AIFF, RIFF, OGG, and VOC files.) Mix_LoadWAV returns a pointer to a Mix_Chunk, which we'll store in our sound variable. If the sound pointer is NULL then there must have been some kind of error. Again, we can use Mix_GetError to find out what went wrong. (An aside: Yes, all of the SDL_mixer functions begin with the prefix Mix_! This makes it easy to distinguish SDL_mixer calls from SDL calls.)

SDL_Surface *screen;
 
screen = SDL_SetVideoMode(320, 240, 0, SDL_ANYFORMAT);
if (screen == NULL) {
	fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
	return 1;
}

As mentioned above when we initialized SDL, we want a small window from which to work. The above code initializes a 320pixel by 240pixel window for us. If you're interested in how this works, or what arguments SDL_SetVideoMode accepts, see the tutorial on displaying a bitmap with SDL.

Things are now set, and we can begin playback of our sample:

int channel;
 
channel = Mix_PlayChannel(-1, sound, 0);
if(channel == -1) {
	fprintf(stderr, "Unable to play WAV file: %s\n", Mix_GetError());
}

The Mix_PlayChannel accepts three arguments:

int channel 
Passing -1 will cause Mix_PlayChannel to play our sample on the first available sound channel. This is probably what you'll want to do, in most cases. If for some reason you wish to specify a certain channel for playback, you may pass an integer designating the desired channel.
Mix_Chunk *chunk 
This is a pointer to the Mix_Chunk containing the sample we wish to play back.
int loops 
How many times should the sample be looped? Passing -1 plays the sample on an infinite loop. Passing 0 plays the sample once, passing 1 plays the sample twice, and so on.

The values passed to Mix_PlayChannel in the code above will result in our sample's playback, with zero loops, on the first available channel. Mix_PlayChannel returns an integer which corresponds to the channel the sample is being played on. If Mix_PlayChannel returns -1 an error has occurred.

We're almost done! The sample is now playing, all we have to do is wait for it to stop before we can clean up:

while(Mix_Playing(channel) != 0);
 
Mix_FreeChunk(sound);
 
Mix_CloseAudio();
SDL_Quit();

The while statement above makes use of the Mix_Playing function to ensure that execution does not proceed until our sample has stopped playing. Mix_Playing will return 0 when the sample playing on the given channel has stopped playing.

We then call Mix_FreeChunk, passing our sound pointer, to ensure that the Mix_Chunk has had its associated memory released. Two calls to Mix_CloseAudio and SDL_Quit are then made, to ensure that SDL_mixer and SDL have a chance to make their internal cleanups.

That's all there is to it!

Source code

  • Click here to download the sample source code for this tutorial.