sdl audiocallback question

I initialized the sine wave array externally
Using memcpy in the callback
Noise will be generated every time if(gbufferpositon>gbufferMaxpositon)
gbufferpositon=0

I don’t think we have enough information to be able to determine why you get noise.

Are you saying the noise is only happening when you reach the end of the buffer?

If this is SDL 2, make sure you initialize the whole “stream” array each time (this might require multiple calls to memcpy).

#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <stdio.h>
#include <math.h>

#define PI2 6.28318530718

float cycle = 0;
float samplesFreq = 44100;
float curFreq = 440;
float audioVolume = 4000;
int bytesPerSample, bytesPerSecond, gBufferByteSize, gBufferMaxPos, gBufferCurPos;
static Uint8 *imt;
void callback(void *userdata, Uint8 * stream, int len)
{
	if(	gBufferCurPos >gBufferMaxPos)
	memset(stream,0,len);
	else{
	memcpy(stream, (void*)&imt[gBufferCurPos], len);
		gBufferCurPos += len; 
	}
}

int main(int argc, char **argv)
{
	SDL_Init(SDL_INIT_AUDIO);
	SDL_AudioSpec spec, aspec;	// the specs of our piece of "music"
	SDL_zero(spec);
	spec.freq = samplesFreq;	// declare specs
	spec.format = AUDIO_S16LSB;
	spec.channels = 1;
	spec.samples = 4096;
	spec.callback = callback;
	spec.userdata = NULL;

	// Open audio, if error, print
	int id;
	if ((id = SDL_OpenAudioDevice(nullptr, 0, &spec, &aspec, SDL_AUDIO_ALLOW_FORMAT_CHANGE)) <= 0)
	{
		fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
		exit(-1);
	}
	curFreq = 440;

	bytesPerSample = aspec.channels * sizeof(aspec) / 8;
	bytesPerSecond = aspec.freq * bytesPerSample;
	gBufferByteSize = bytesPerSecond * 6;
	gBufferMaxPos = bytesPerSecond * 5;
	imt = (Uint8*)new short[gBufferByteSize];
	memset(imt, 0, gBufferByteSize);
	short *snd = reinterpret_cast < short *>(imt);
	int len = gBufferByteSize / sizeof(short);
	for (int i = 0; i < len; i++)	// Fill array with frequencies, mathy-math 
									// stuff
	{
		snd[i] = audioVolume * sin(cycle);

		cycle += curFreq * PI2 / samplesFreq;
		if (cycle >= PI2)
			cycle -= PI2;
	}
	for (int i = len; i < gBufferByteSize; i++)	// Fill array with frequencies, mathy-math 
									// stuff
	{
		snd[i] = audioVolume * sin(cycle);

		cycle+= 0 * PI2 / samplesFreq;
		if (cycle >= PI2)
			cycle -= PI2;
	}
	/* Start playing, "unpause" */



	SDL_PauseAudioDevice(id, 0);

	while (true)	
	{

		SDL_LockAudioDevice(id);
		// SDL_GetAudioDeviceStatus(id);
/*		if (gBufferCurPos > len+1000)
		{
	//	gBufferCurPos=0;

		// SDL_PauseAudioDevice(id,1);
		}*/
		SDL_UnlockAudioDevice(id);
	}
}

Why does memset(stream,0,len); produce the same strange sound as SDL_PauseAudioDevice(id,1);

It is found that the sample frame = 1 will reduce the stuttering sound effect

Your recording sounds more like 880 Hz than 440 Hz.

When I run your program it sounds like 440 Hz.

I guess your recording was made when the sound-restart-code (inside the while loop) was uncommented and that’s what makes the “click” sounds. It happens too often but that has probably to do with the incorrect frequency and other bugs (see below).


Pay attention to units. gBufferCurPos is in bytes while len is in samples so you need to convert to the same unit before comparing them. E.g.

if (gBufferCurPos / bytesPerSample > len + 1000)

or

if (gBufferCurPos > (len + 1000) * bytesPerSample)

You might also want to make sure that samplesFreq is equal to aspec.freq before filling the snd array (or simply use aspec.freq directly).


The way you calculate bytesPerSample seems to be incorrect. Instead of sizeof(aspec) you probably meant SDL_AUDIO_BITSIZE(aspec.format).


You’re creating a buffer that is large enough to hold 6 seconds of sound:

gBufferByteSize = bytesPerSecond * 6;

But why is the max position at 5 seconds?

gBufferMaxPos = bytesPerSecond * 5;

If you want the position of the last byte in the buffer it would instead be:

gBufferMaxPos = gBufferByteSize - 1;

If you want the byte position of the last sample it would be:

gBufferMaxPos = gBufferByteSize - bytesPerSample;

It might be easier to just compare against gBufferByteSize inside the callback.

Update: I now realize that you might have made the max a bit shorter than the size to avoid running out of bounds in the callback but I think you should instead fix the logic inside the callback to do the right thing, something like:

void callback(void* userdata, Uint8* stream, int len)
{
	int bytesToWrite = clamp(gBufferByteSize - gBufferCurPos, 0, len);
	memcpy(stream, &imt[gBufferCurPos], bytesToWrite);
	memset(stream + bytesToWrite, 0, len - bytesToWrite);
	gBufferCurPos += bytesToWrite; 
}

Where clamp is a “clamping function” like std::clamp or SDL_clamp.

1 Like

OK, I’ll try.Think you

What does +1000 mean? :sweat:

#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <stdio.h>
#include <math.h>

#define PI2 6.28318530718

float cycle = 0;
float samplesFreq = 44100;
float curFreq = 440;
float audioVolume = 32000;
int bytesPerSample, bytesPerSecond, gBufferMaxPos, gBufferCurPos;
short *imt;
void callback(void *userdata, Uint8 * stream, int len)
{

	short *snd = reinterpret_cast < short *>(stream);
for(	int i = 0; i < len / 2;++i)
	{
		if (gBufferMaxPos > gBufferCurPos)
		{
			snd[i] = imt[gBufferCurPos];
			gBufferCurPos++;
		}
		else
			snd[i] = 0;
	}

}

int main(int argc, char **argv)
{
	SDL_Init(SDL_INIT_AUDIO);
	SDL_AudioSpec spec, aspec;	// the specs of our piece of "music"
	SDL_zero(spec);
	spec.freq = samplesFreq;	// declare specs
	spec.format = AUDIO_S16SYS;
	spec.channels = 1;
	spec.samples = 4096;
	spec.callback = callback;
	spec.userdata = NULL;

	// Open audio, if error, print
	int id = SDL_OpenAudioDevice(nullptr, 0, &spec, &aspec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);

	curFreq = 440;

	bytesPerSample = aspec.channels * (SDL_AUDIO_BITSIZE(aspec.format) / 8);
	bytesPerSecond = aspec.freq * bytesPerSample;

	gBufferMaxPos = bytesPerSecond;


	imt = new short[gBufferMaxPos];
	memset(imt, 0, gBufferMaxPos * 2);

	int len = gBufferMaxPos / 2;

	for (int i = 0; i < len; i++)
	{
		float ct = sin(cycle);
		imt[i] = audioVolume * ct;

			cycle += curFreq * PI2 / samplesFreq;
			if (cycle >= PI2)
				cycle -= PI2;
		

	}
	for (int i = len; i < len * 2; i++)
	{
		imt[i] = audioVolume * sin(cycle);

		if (audioVolume > 0)
			audioVolume -= 50;
		else
			audioVolume = 0;
		cycle += curFreq * PI2 / samplesFreq;
		if (cycle >= PI2)
			cycle -= PI2;
	
	}
	gBufferCurPos = 0;

	SDL_PauseAudioDevice(id, 0);

	// while (true)
	{
		SDL_Delay(2000);
		SDL_PauseAudioDevice(id, 1);
		delete[] imt;
	}
	SDL_Quit();
}

Thanks, the test works fine.

But can the device shutdown sound be turned off?

You have the right idea, but your fade out is running too fast, causing the final square wave at exit. Here’s a quick fix:

#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <stdio.h>
#include <math.h>

#define PI2 6.28318530718

float cycle = 0;
float samplesFreq = 44100;
float curFreq = 440;
float audioVolume = 32000;
int bytesPerSample, bytesPerSecond, gBufferMaxPos, gBufferCurPos;
short *imt;
void callback(void *userdata, Uint8 * stream, int len)
{

	short *snd = reinterpret_cast < short *>(stream);
for(	int i = 0; i < len / 2;++i)
	{
		if (gBufferMaxPos > gBufferCurPos)
		{
			snd[i] = imt[gBufferCurPos];
			gBufferCurPos++;
		}
		else
			snd[i] = 0;
	}

}

int main(int argc, char **argv)
{
	SDL_Init(SDL_INIT_AUDIO);
	SDL_AudioSpec spec, aspec;	// the specs of our piece of "music"
	SDL_zero(spec);
	spec.freq = samplesFreq;	// declare specs
	spec.format = AUDIO_S16SYS;
	spec.channels = 1;
	spec.samples = 4096;
	spec.callback = callback;
	spec.userdata = NULL;

	// Open audio, if error, print
	int id = SDL_OpenAudioDevice(nullptr, 0, &spec, &aspec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);

	curFreq = 440;

	bytesPerSample = aspec.channels * (SDL_AUDIO_BITSIZE(aspec.format) / 8);
	bytesPerSecond = aspec.freq * bytesPerSample;

	gBufferMaxPos = bytesPerSecond;


	imt = new short[gBufferMaxPos];
	memset(imt, 0, gBufferMaxPos * 2);

	int len = gBufferMaxPos / 2;

	for (int i = 0; i < len; i++)
	{
		float ct = sin(cycle);
		imt[i] = audioVolume * ct;

			cycle += curFreq * PI2 / samplesFreq;
			if (cycle >= PI2)
				cycle -= PI2;
		
	}

	/* Your fade was just too sharp, since the sample rate is running at 44100 hertz,
	 *  it was slamming the volume to zero fast enough to cause a square wave.
	 */
	for (int i = len; i < len * 2; i++)
	{
		imt[i] = audioVolume * sin(cycle);
		// adjust this multiplier for different fade speeds
		audioVolume *= 0.9992;

		cycle += curFreq * PI2 / samplesFreq;
		if (cycle >= PI2)
			cycle -= PI2;

		// it's OK to fill the data with Zero values after the fade since that's just an Off State for the speaker.
	}
	gBufferCurPos = 0;

	SDL_PauseAudioDevice(id, 0);

	// while (true)
	{
		SDL_Delay(2000);
		SDL_PauseAudioDevice(id, 1);
		delete[] imt;
	}
	SDL_Quit();
}
1 Like

It was present in your original code too. It’s the least number of samples to wait before restarting the sound after reaching the end. This only works if you continue to increment gBufferCurPos past the end.

1 Like