rytone
rytone

Reputation: 165

Playing audio with stb_vorbis and SDL2 cuts off

No matter what what audio file I use, it always cuts off about a fourth of the way through. I have a feeling it's because I'm casting the decoded audio to a Uint8*, but if I don't, the audio plays really fast and it still only plays about half of the file. Also, using SDL_MixAudio instead of SDL_memcpy causes a bunch of copies of the sound to play over each other for some reason.

Uint8* audio_pos;
Uint32 audio_len;

void audioCallback(void* userdata, Uint8* stream, int len) {
    if (audio_len == 0) return;
    len = (len > audio_len ? audio_len : len);
    SDL_memcpy(stream, audio_pos, len);
    audio_pos += len;
    audio_len -= len;
}

int main(int argc, char *argv[]) {
    ...
    short* decoded;
    int channels, rate, len;
    len = stb_vorbis_decode_filename(RealPath("music.ogg").c_str(), &channels, &rate, &decoded);

    SDL_AudioSpec spec;
    spec.freq = rate;
    spec.format = AUDIO_S16;
    spec.channels = channels;
    spec.samples = 2048;
    spec.callback = audioCallback;
    spec.userdata = NULL;

    audio_pos = (Uint8*)decoded;
    audio_len = len;

    if (SDL_OpenAudio(&spec, NULL) < 0) {
        std::cout << "failed to open audio device: " << SDL_GetError() << '\n';
        SDL_GL_DeleteContext(context);
        SDL_Quit();
        return -1;
    }

    SDL_PauseAudio(0);

    SDL_Event windowEvt;
    while (true) {
        if (audio_len == 0) break;
        if (SDL_PollEvent(&windowEvt)) {
            if (windowEvt.type == SDL_QUIT) break;
            if (windowEvt.type == SDL_KEYUP && windowEvt.key.keysym.sym == SDLK_ESCAPE) break;
        }
        SDL_GL_SwapWindow(window);
    }
    ...
}

Upvotes: 0

Views: 1284

Answers (1)

Mike
Mike

Reputation: 1

stb_vorbis_decode_filename returns the "number of samples decoded," which is respect to an int16, and doesn't include the channel factor. You're looking for:

int32 length = stb_vorbis_decode_filename("thing.ogg", &channels, &rate, &decoded);
int32 audio_length = length * channels * (sizeof(int16) / sizeof(uint8));

With respect to SDL_MixAudio vs SDL_memcpy for sound overlap, you need to explicitly clear the stream with a silence value. For example, SDL_memset when you enter the SDL audio callback.

void audioCallback(void* userdata, Uint8* stream, int len) 
{
    SDL_memset(stream, 0, len);// silence the stream
    if (audio_len == 0) return;
    len = (len > audio_len ? audio_len : len);
    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);// this takes a volume argument
    audio_pos += len;
    audio_len -= len;
}

The zero passed to SDL_memset should be the same silence value you created in SDL_AudioSpec, when calling SDL_OpenAudioDevice.

Upvotes: 0

Related Questions