Programming ⇝ Tutorial: Using SDL2 and SDL_Mixer to Play Samples

It's pretty easy to use SDL2 and C++ to load .wav files and play them back. Using SDL_Mixer lets you use multiple audio playback channels so you can play more than one sound at a time.

This example is written using Linux. The code should also work under Windows, but it's up to you to set up your build environment and libraries.

The first thing you need to have is a working build environment. If you can run "g++ --version" on the command like without errors, you have the compiler you need.

The next step is to install the SDL2 development libraries. To get them on a Debian/Ubuntu system, run:

sudo apt-get install libsdl2-dev libsdl2-mixer-dev libsdl2-net-dev libsdl2-ttf-dev libsdl2-image-dev libsdl2-gfx-dev

Only the first two packages are required for this project, but if you're using SDL2, you'll probably want the other ones eventually anyway.

Once you've done that, visit FreeWaveSamples, download these two .wav files, and save them in the directory you want to work in:

Kick-Drum-1.wav

Snare-Drum-1.wav

Once you've done that, create a file called main.cpp and paste in the code below.

Code:

#include 

#ifdef WIN32
#include "SDL/include/SDL.h"
#undef main
#include "SDL/SDL_mixer.h"
#include "SDL/SDL_image.h"
#endif
#ifndef WIN32
#include "SDL.h"
#include "SDL_mixer.h"
#include "SDL_image.h"
#include 
#include 
#include 
#else
#include "Windows.h"
#endif
#include 

#define NUM_WAVEFORMS 2
const char* _waveFileNames[] =
{
"Kick-Drum-1.wav",
"Snare-Drum-1.wav",
};

Mix_Chunk* _sample[2];

// Initializes the application data
int Init(void) 
{
    memset(_sample, 0, sizeof(Mix_Chunk*) * 2);

    // Set up the audio stream
    int result = Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 512);
    if( result < 0 )
    {
        fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
        exit(-1);
    }

    result = Mix_AllocateChannels(4);
    if( result < 0 )
    {
        fprintf(stderr, "Unable to allocate mixing channels: %s\n", SDL_GetError());
        exit(-1);
    }

    // Load waveforms
    for( int i = 0; i < NUM_WAVEFORMS; i++ )
    {
        _sample[i] = Mix_LoadWAV(_waveFileNames[i]);
        if( _sample[i] == NULL )
        {
            fprintf(stderr, "Unable to load wave file: %s\n", _waveFileNames[i]);
        }
    }

    return true;
}

int main(int argc, char** argv)
{
    // Initialize the SDL library with the Video subsystem
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO );
    atexit(SDL_Quit);

    SDL_Window* window = SDL_CreateWindow("DrumPads",
										  SDL_WINDOWPOS_UNDEFINED,
										  SDL_WINDOWPOS_UNDEFINED,
										  256,
										  256,
 										  SDL_WINDOW_RESIZABLE);

    // Application specific Initialize of data structures
    if (Init() == false)
        return -1;

    // Event descriptor
    SDL_Event Event;

    bool done = false;
    while (!done)
    {
        bool gotEvent = SDL_PollEvent(&Event);

        while (!done && gotEvent)
        {
            switch (Event.type)
            {
                case SDL_KEYDOWN:
                    switch (Event.key.keysym.sym)
                    {
                        case 'q':
                            Mix_PlayChannel(-1, _sample[0], 0);
                            break;
                        case 'w':
                            Mix_PlayChannel(-1, _sample[1], 0);
                            break;
                        default:
                            break;
                    }
                    break;

                case SDL_QUIT:
                    done = true;
                    break;

                default:
                    break;
            }
            if( !done ) gotEvent = SDL_PollEvent(&Event);
        }
#ifndef WIN32
		usleep(1000);
#else
		Sleep(1);
#endif
    }

    for( int i = 0; i < NUM_WAVEFORMS; i++ )
    {
        Mix_FreeChunk(_sample[i]);
    }

    Mix_CloseAudio();
    SDL_Quit();
    return 0;
}

Now run this command to build the code:

g++ -o main -I/usr/include/SDL2 -L/usr/lib/ main.cpp -Wl,--allow-shlib-undefined -lSDL2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lrt

If all goes well, this will create a file called "main". To run it, enter ./main at the terminal. A transparent window will come up. You can hit the "Q" key to play a kick drum and the "W" key to play a snare drum.

And now let's dissect what the code is doing and why.

The program starts with main(). The first thing we do is call SDL_Init(). Every SDL program needs that, and there are a few settings you can use. Here we just use audio and video. In a game you might also want to initialize a joystick or game controller.

Next we tell the program to run SQL_Quit() at exit. This shuts down the SDL system if the program exits abnormally.

After that we create a window for our program. We're not going to draw anything to it since we only care about the audio for this example, but we need to create a window to make the keyboard work.

We then call the Init() function at the top of the file. The first thing that does its call Mix_OpenAudio, which starts the audio system. The settings we use are 44.1 kHz, 16-bit, stereo with a 512 byte buffer. You can use other audio formats, but that is the most common.

After opening the audio system, we call Mix_AllocateChannels(). This sets up our mixer to have 4 channels. Each channel gives us a note of polyphony since each sample needs its own channel to play back. You can use more channels, and each channel will use more system resources. Use too many and you'll overload the system and get audio glitches and dropouts. Use too few, and some sounds will not play or will be cut off.

Next we call Mix_LoadWAV() for each of our samples. This loads a .wav file from disk as a Mix_Chunk that can be sent to the SDL mixer for playback.

Once that's done, we return to our main function and create an event loop. We start by calling SDL_PollEvent() to check for any events in the queue. We then process each event and look for another. In this program we only care about two events - key presses and the quit event (generated by clicking the "X" on the app window).

When we see a key down event that matches the letter Q or the letter W, we call Mix_PlayChannel and give it one of our samples to play. We use -1 for the channel number so the mixer will play the sound on the next available channel. We could also use a specific channel for each sound instead.

If we get a quit event, we shut down everything by calling Mix_FreeChunk() on our samples, then Mix_CloseAudio() to shut the audio system down. After that we call SDL_Quit() to clean up the rest of SDL and exit the program.

It would be very easy to modify this program to add more samples and use different keys to trigger them.

If you'd like to draw a user interface and add clickable controls, there are plenty of tutorials on the web for drawing with SDL and getting user input.