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.
OK, I’ll try.Think you
What does +1000 mean?
#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();
}
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.