Jake A
Jake A

Reputation: 135

Trying to play a WAV with SDL but getting buzzing noise

I'm writing this simple program to learn how to work with audio with SDL. The program opens a window with a button that says go and when you hit the button it should play a WAV of a drum sample. There are no errors and the program doesn't crash it's just that instead of playing the drum sound it makes a weird buzzing noise that doesn't stop until you exit the program. I think it's something to do with the audio callback function but I am very new to this so I'm not sure exactly how it works.

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <SDL2/SDL_audio.h>

TTF_Font* font;
SDL_Color white = {255, 255, 255};
SDL_AudioSpec frmt;
bool run = true;
bool playing = false;

struct sample
{
    Uint8 *data;
    Uint32 dpos;
    Uint32 dlen;
} sound;

bool buttonPress(int x, int y, SDL_Rect r)
{
    if (x < (r.x + r.w) && x > r.x)
    {
        if (y < (r.y + r.h) && y > r.y)
        {
            return true;
        }
    }
    return false;
}

void mixing(void *unused, Uint8 *stream, int len)
{
    Uint32 amount;

    if (playing)
    {
        amount = sound.dlen - sound.dpos;
        if ( amount > len ) {
            amount = len;
        }
        SDL_MixAudio(stream, &sound.data[sound.dpos], amount, SDL_MIX_MAXVOLUME);
        sound.dpos += amount;
    }
}

int main(int argc, char *argv[])
{
    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();

    frmt.channels = 1;
    frmt.format = AUDIO_S16;
    frmt.freq = 22050;
    frmt.samples = 512;
    frmt.userdata = NULL;
    frmt.callback = mixing;

    int mX, mY;
    Uint32 fpsTimer = SDL_GetTicks();

    font = TTF_OpenFont("octaFont.TTF", 20);

    SDL_Window* window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 400, 200, 0);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    SDL_Surface* surface = TTF_RenderText_Solid(font, "Go!", white);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_Rect button;
    button.w = 50;
    button.h = 35;
    button.x = 10;
    button.y = 10;
    SDL_Rect bg;
    bg.w = 400;
    bg.h = 200;
    bg.x = 0;
    bg.y = 0;
    SDL_Event e;

    if (SDL_OpenAudio(&frmt, NULL) < 0)
    {
        fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
        return 1;
    }

    SDL_PauseAudio(0);

    SDL_AudioSpec wave;
    SDL_AudioCVT cvt;
    Uint8 *data;
    Uint32 dlen;
    if (SDL_LoadWAV("sound.wav", &wave, &data, &dlen) == NULL)
    {
        fprintf(stderr, "Unable to load wave: %s\n", SDL_GetError());
        return 1;
    }
    SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16, 1, 22050);
    cvt.buf = malloc(dlen*cvt.len_mult);
    memcpy(cvt.buf, data, dlen);
    cvt.len = dlen;
    SDL_ConvertAudio(&cvt);
    SDL_FreeWAV(data);
    SDL_LockAudio();
    free(sound.data);
    sound.data = cvt.buf;
    sound.dlen = cvt.len_cvt;
    sound.dpos = 0;
    SDL_UnlockAudio();

    while (run)
    {
        while (SDL_PollEvent(&e))
        {
            switch (e.type)
            {
                case SDL_QUIT:
                    run = false;
                    break;
                case SDL_KEYDOWN:
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_SPACE:
                            playing = false;
                            break;
                        default:
                            break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    SDL_GetMouseState(&mX, &mY);
                    if (buttonPress(mX, mY, button))
                    {
                        playing = true;
                    }
                    break;
                default:
                    break;
            }
        }
        if (SDL_GetTicks() - fpsTimer >= 16)
        {
            SDL_RenderClear(renderer);
            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
            SDL_RenderFillRect(renderer, &bg);
            SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
            SDL_RenderDrawRect(renderer, &button);
            SDL_RenderCopy(renderer, texture, NULL, &button);
            SDL_RenderPresent(renderer);
            fpsTimer = SDL_GetTicks();
        }
    }

    SDL_CloseAudio();
    SDL_DestroyWindow(window);
    SDL_DestroyRenderer(renderer);
    TTF_Quit();
    SDL_Quit();
    return 0;
}

Upvotes: 1

Views: 649

Answers (1)

Craig Estey
Craig Estey

Reputation: 33601

The issue may be that you're using SDL_MixAudio.

Your code mixes in the data from the .wav file, but mixes it into what?

What is in stream before calling SDL_MixAudio?

I get the same buzz, but when I use memset to zero out the stream buffer before calling SDL_MixAudio, I get the correct sound from the .wav file.

I don't know for sure but I believe that on the second and subsequent calls to your mixing function, the data from the prior callback is still in the stream buffer.

Side note: When the .wav data has finished playing, we can reset sound.dpos to replay the file again.

Here's the refactored code:

void
mixing(void *unused, Uint8 *stream, int len)
{
    Uint32 amount;

// NOTE/DEBUG: force sound on
#if 1
    playing = 1;
#endif

// NOTE/FIX: zero out stream data so we don't mix in random data (from the
// prior round)
#if 1
        memset(stream,0,len);
#endif

    if (playing) {
        amount = sound.dlen - sound.dpos;
        if (amount > len) {
            amount = len;
        }
#if 1
        SDL_MixAudio(stream, &sound.data[sound.dpos], amount, SDL_MIX_MAXVOLUME);
#else
        memcpy(stream,&sound.data[sound.dpos],amount);
#endif
        sound.dpos += amount;

// NOTE/FIX(?) -- start wav at beginning after all data has been output
#if 1
        if (sound.dpos >= sound.dlen)
            sound.dpos = 0;
#endif
    }
}

Upvotes: 1

Related Questions