SDL_OpenAudio can't set custom buffer size?

Been using SDL for years with no issues (shipped 5 SDL games so far).

Audio is skipping in my latest game during CPU-intensive moments. Trying to figure out why, I tried adjusting the buffer size (SDL_AudioSpec.samples) in my call to SDL_OpenAudio. Seemed to have no effect, which was surprising.

Trying to figure out why buffer size was having no effect on drop-outs, I discovered that whatever size I specified was being ignored. In the “obtained” return parameter of OpenAudio, I’m seeing 470 samples always, with a full buffer size in bytes of 1880 (16-bit, stereo samples, 4*470 = 1800).

I’m also seeing this same byte length 1800 in each callback call. I’m guessing that it’s simply too short to weather CPU-intensive moments in my game. But how can I change it? And where is 470 coming from?

I’m testing this on Ubuntu Linux (old version, Fiesty). Here’s the output of my sdl-config:

sdl-config --version --cflags --libs --static-libs
1.2.11
-I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT
-L/usr/lib -lSDL
-L/usr/lib -lSDL -lpthread

From some of the error messages that I’ve seen when the audio device is already in use, I believe this is ALSA-backed SDL. Yeah, if I have my MP3 player going when I launch my game, I see this:

ALSA lib pcm_dmix.c:864:(snd_pcm_dmix_open) unable to open slave

Here’s my code:

SDL_AudioSpec audioFormat;

    /* Set 16-bit stereo audio at 22Khz */
    audioFormat.freq = soundSampleRate;
    audioFormat.format = AUDIO_S16;
    audioFormat.channels = 2;
    //audioFormat.samples = 512;        /* A good value for games */
    //audioFormat.samples = 1024;        
    audioFormat.samples = 8192;        
    //audioFormat.samples = 8;        
    audioFormat.callback = audioCallback;
    audioFormat.userdata = NULL;
    
    SDL_AudioSpec actualFormat;

    /* Open the audio device and start playing sound! */
    if( SDL_OpenAudio( &audioFormat, &actualFormat ) < 0 ) {
        AppLog::getLog()->logPrintf( 
            Log::ERROR_LEVEL,
            "Unable to open audio: %s\n", SDL_GetError() );
        }
    else {
        AppLog::getLog()->logPrintf( 
            Log::INFO_LEVEL,
            "Successfully opened audio: %dHz, sample buffer size=%d\n", 
            actualFormat.freq, actualFormat.samples  );
        }

No matter which buffer size I pick there, I get this printed in my log:

L4 | Sun Nov 21 18:46:00 2010 (679 ms) | general | Successfully opened audio: 22050Hz, buffer=470

I’m going to DL the SDL 1.2.11 source tarball and take a look eventually, but I’ve been unable to find anything about this problem online. Yeah, various docs say, “The audio hardware may pick a better buffer size for you.” The other thing I’ve found, when searching for “sdl audio 470” is that some other games on Linux seem to get stuck with that value as well (people debugging other problems often include the printout from the game’s audio layer, which reveals a 470-sample buffer size).

Maybe this was something fixed in SDL 1.2.14 with “Updated ALSA support to the latest stable API”.

Any thoughts?

Jason

Investigating my ALSA setup. Here’s the output of aplay -vv

Seems that my hardware has a buffer size larger than 1880:

aplay -vv PCGaming_Rohrer.wav
Playing WAVE ‘PCGaming_Rohrer.wav’ : Signed 16 bit Little Endian, Rate 44100 Hz, Mono
Plug PCM: Rate conversion PCM (48000, sformat=S16_LE)
Its setup is:
stream : PLAYBACK
access : RW_INTERLEAVED
format : S16_LE
subformat : STD
channels : 1
rate : 44100
exact rate : 44100 (44100/1)
msbits : 16
buffer_size : 15052
period_size : 940
period_time : 21333
tick_time : 0
tstamp_mode : NONE
period_step : 1
sleep_min : 0
avail_min : 940
xfer_align : 940
start_threshold : 15040
stop_threshold : 15052
silence_threshold: 0
silence_size : 0
boundary : 986447872
Slave: Route conversion PCM (sformat=S16_LE)
Transformation table:
0 <- 0
1 <- 0
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 1
rate : 48000
exact rate : 48000 (48000/1)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tick_time : 0
tstamp_mode : NONE
period_step : 1
sleep_min : 0
avail_min : 1024
xfer_align : 1024
start_threshold : 16384
stop_threshold : 16384
silence_threshold: 0
silence_size : 0
boundary : 1073741824
Slave: Direct Stream Mixing PCM
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (48000/1)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tick_time : 0
tstamp_mode : NONE
period_step : 1
sleep_min : 0
avail_min : 1024
xfer_align : 1024
start_threshold : 16384
stop_threshold : 16384
silence_threshold: 0
silence_size : 0
boundary : 1073741824
Hardware PCM card 0 ‘Intel 82801CA-ICH3’ device 0 subdevice 0
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (48000/1)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tick_time : 4000
tstamp_mode : NONE
period_step : 1
sleep_min : 0
avail_min : 1024
xfer_align : 1024
start_threshold : 1
stop_threshold : 1073741824
silence_threshold: 0
silence_size : 1073741824
boundary : 1073741824

Switching to an OSS-bound build of libSDL (instead of the ALSA-bound one) fixes this problem.

Turns out this is a problem with the way SDL 1.2 interacts with ALSA when ALSA is inflexible about period size. Total buffer size should be somewhat of period size, according to ALSA sample code. SDL forces total buffer size to be 2 * period size always. And if your period size is fixed for some reason, then you lose the ability to control your buffer size (not good).

Behavior is the same in the latest 1.2 code in HG.

I’ve submitted a bug report here:

http://bugzilla.libsdl.org/show_bug.cgi?id=1079

I hope this info proves useful to someone else who stumbles upon the same problem!

Jason