Audio clicks using callback

I am using SDL for an audio project.

I tried running a simple test playing a sinewave, but I always get a clicking sound related to the buffer size.

I haven’t been able to figure out what the problem exactly is.There was a similar post here but It didn’t seem to be resolved.

The clicking is definitely happening in relation to the buffer size…

If anyone could help it would be much appreciated.
Thanks!

#include <iostream>
#include <stdio.h>
#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
#include "math.h"

#define sample_rate 44100
#define tau 6.2831853

float phase = 0, step = 440*tau/sample_rate;

void audioCb(void* userdata, Uint8* stream, int len){

      short* out = (short*)stream;

      for (int i = 0; i < len; i++) 
      {

      out[i] = (short)5000*sinf(phase);

      phase+=step;
      if(phase >= tau){ phase -= tau; }    

      }


}

int main(int argc, char** argv){
        if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) != 0){
                std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
                return 1;
        }

        SDL_AudioSpec audio;

        audio.format =  AUDIO_S16; 
        audio.freq = sample_rate;
        audio.samples = 2048;
        audio.channels = 1;
        audio.userdata = NULL;
        audio.callback = audioCb;

        if ( SDL_OpenAudio(&audio, NULL) < 0 ) {
            std::cout << "unable to open audio" << std::endl;
            return 1;
        }  

        SDL_PauseAudio(0);

        puts("press enter to exit");
        std::getchar();

        SDL_Quit();

        return 0;
}

As soon as I posted this, something worked by trial/error.

making the loop int the callback go to len/2 stopped the clicks.

So I guess since in the spec it says the length is in bytes, and my chosen sample format is 16 bit short, I am filling the buffer at len/2?

You figured this out, but I wanted to expand on the explanation for anyone finding this on a Google search:

The division is correct: if len=10, it wants you to fill a buffer of ten 8-bit bytes, but “out” is an array of 16-bit ints, so writing to out[0] would be bytes 1 and 2, and out[9] would be bytes 19 and 20, which means you’re writing more audio data than you need (hence the weird sound because you’re missing pieces of your sine wave in the playback when SDL doesn’t look past the array it wanted filled) and also you’re overwriting random memory and all bets are off.

(This callback uses a number way higher than 10 in practice, that’s just for clarity here.)

So len / 2 (more specifically: len / sizeof (Sint16)) means the last thing you write to is out[4], for bytes 9 and 10, and everythings works out. If you had used Float32 format, it would be len/4…sizeof (float).

Also note that this is for Mono, for Stereo it’d be
len/(2*sizeof(Sint16)) because there’s one 2 byte (1 Sint16) sample for
the left and one for the right channel.

By the way, Ryan, why does the callback use Uint8* and not just void*?
With void* it’d be clearer that the actual type varies.

why does the callback use Uint8* and not just void*?

I always assumed it was to make it easy to move around on byte offsets regardless of your data (void is clearer, but you can’t do myVoidPtr += sizeof(Sint16). Maybe we should change that for 2.1, though.

1 Like

hmm it’s true that this way it’s easier to work with byte offsets, but I guess the usual (and easier+more intuitive) way is to cast it to Sint16 (or whatever is appropriate for the used sampleformat) before operating in the pointer

Changing it for 2.1 sounds like a good idea :+1:

1 Like