SDL audio callback buffer size

Started by
5 comments, last by ultramailman 9 years, 11 months ago

Hello all.

This is my first time going into the sound module of SDL, and I was able to get it to play a sound whenever the spaceship shoots a laser, or whenever it gets hit. However, the sound comes out too slow and isn't in sync with the rapid firing laser.

The problem is the audio callback function is called only around once every second, while the space ship can shoot 10 lasers per second. So I added some print statements to show what the buffer size is, and found out that the buffer size is 22056 (close to half of the frequency, 44100). So I tried changing the buffer size through SDL_AudioSpec.samples, but it is still 22056. Then I tried changing SDL_AudioSpec.freq to something lower, and that was able to change the buffer size.

So now I can change the buffer size, but only if also change the frequency. But that makes the sounds sound slow-motion.

Is there a way to customize the buffer size without changing the frequency? Or can it be a hardware limitation?

Here's code showing what I mean


void SoundSystem::init()
{
    SDL_AudioSpec want;
    Sound::getSpec("sound/checkspec.wav", &want); // use a dummy file to get the spec, this dummy file has same format as other sound files
    want.userdata = this;
    want.callback = SoundSystem::callback;
    //want.freq = 1024; // this changes buffer size to 1024, but makes sounds sound very slow and deep
    //want.samples = 512; // this is what I should use to change buffer size to 512, but it doesn't do anything
    good = (0 == SDL_OpenAudio(&want, &spec));
    if(good)
        SDL_PauseAudio(0);
}
 
void SoundSystem::callback(void *userdata, Uint8 *stream, int len)
{
    auto sys = (SoundSystem *) userdata;
    SDL_memset(stream, 0, len);
    std::cout << len << '\n';    //printing this shows the buffer size
    sys->lock(); // sdl spinlock
    for(Playback & p : sys->playbacks) // mix all sounds in a list
    {
        if(p.buf == nullptr)
            continue;
        unsigned left = p.pos + len > p.len ?  p.len - p.pos : len;
        SDL_MixAudioFormat(stream, p.buf + p.pos, sys->spec.format, left, SDL_MIX_MAXVOLUME);
        
        p.pos += left;
        if(p.pos == p.len)
            p = Playback{nullptr, 0, 0};
    }
    sys->unlock();
}
bool Sound::getSpec(std::string path, SDL_AudioSpec * spec)
{
    unsigned len;
    Uint8 * buf;
    bool good = SDL_LoadWAV(path.c_str(), spec, &buf, &len);
    if(good)
        SDL_FreeWAV(buf);
    return good;
}
Advertisement

SDL_AudioSpec appears to have 2 buffer sizes, size (in bytes) and samples. Does Sound::getSpec fill in the size in bytes too? http://wiki.libsdl.org/SDL_AudioSpec

Perhaps it would be best to ensure the buffer size in bytes is zero?

Also, what spec is returned when calling SDL_OpenAudio? Does it have a buffer size you'd expect?

Oh, I've never tried filling in the spec for getSpec (edidited first post, getSpec is now included). I've always thought loadwav is the one that is responsible for filling it.

It's still worth a try though, so I did:


bool Sound::getSpec(std::string path, SDL_AudioSpec * spec)
{
    unsigned len;
    Uint8 * buf;
    // spec->samples = 512; // this doesn't seem to do anything
    //spec->size = 1024; // doesn't do anything, gets zeroed out by SDL_LoadWAV's call to SDL_memset(spec, 0, sizeof *spec)
    bool good = SDL_LoadWAV(path.c_str(), spec, &buf, &len);
    if(good)
        SDL_FreeWAV(buf);
    return good;
}

Also, what spec is returned when calling SDL_OpenAudio? Does it have a buffer size you'd expect?

And the obtained spec is indeed different from the wanted spec, printing shows 11028 samples and 22056 buffer size, as expected. Besides, spec.size is calculated, so either I fill it in before it is calculated and it doesn't do anything, or I fill it in after it is already filled in by SDL and don't know what I might have messed up.

The SDL audio system is very low level. Is there a reason you're not using something like SDL_Mixer?

Oh, no reason at all. It's there, and I've decided to use it, that's all. SDL audio is not very straightforward, but I think I've got it working pretty much the way I've wanted, except this problem with the low rate of callback. I hope to be able to fix it, or at least find out why I can't do it.

Oh, I've never tried filling in the spec for getSpec (edidited first post, getSpec is now included). I've always thought loadwav is the one that is responsible for filling it.

It's still worth a try though, so I did:


bool Sound::getSpec(std::string path, SDL_AudioSpec * spec)
{
    unsigned len;
    Uint8 * buf;
    // spec->samples = 512; // this doesn't seem to do anything
    //spec->size = 1024; // doesn't do anything, gets zeroed out by SDL_LoadWAV's call to SDL_memset(spec, 0, sizeof *spec)
    bool good = SDL_LoadWAV(path.c_str(), spec, &buf, &len);
    if(good)
        SDL_FreeWAV(buf);
    return good;
}

Also, what spec is returned when calling SDL_OpenAudio? Does it have a buffer size you'd expect?

And the obtained spec is indeed different from the wanted spec, printing shows 11028 samples and 22056 buffer size, as expected. Besides, spec.size is calculated, so either I fill it in before it is calculated and it doesn't do anything, or I fill it in after it is already filled in by SDL and don't know what I might have messed up.

When I said fill it in yourself, I meant after SDL has filled it in. Get the format from your wav, but then overwrite all 3 parameters related to buffer sizes (sample rate, size in samples and size in bytes). In your initial post you were only overwriting 2 of them. Alternatively, you could try reducing the size of your wav to 1024 samples (or whatever you want your buffer size to be).

However, I'd advise not using a wav file to dictate your sound engine mix format. SDL_AudioSpec isn't that complicated, so it should be no problem to fill it out manually from hard coded values or a config file. That way you can be 100% confident what your format is.

@ LongPointer, you are right, using checkspec.wav is kind of silly. I don't use it anymore. As for filling the numbers in after OpenAudio, it doesn't work, because I already got the audio device.

This topic is closed to new replies.

Advertisement