I have a bug in my audio code.
- Expected behavior: sinewave output, sweeping from 100Hz to 200Hz, resetting to 100Hz every second
- Actual behavior: sinewave output, sweeping from 100Hz to 200Hz, but then rising 100Hz on each cycle, so on the second cycle it will sweep from 200Hz to 300Hz, then from 300Hz to 400Hz, and so on...
I'm generating a 1Hz rising sawtooth wave, and scaling and offsetting it so it rises from 100 to 200 every second. I'm also printing its value, which shows that it's behaving as expected. But for some reason, if I use that value as frequency for my sinewave, the resulting sound rises 100Hz on each cycle.
Plugging a fixed frequency into my sinewave function works as expected. It's only when I use the two together that I'm getting the bug. The thing I really can't explain is that the bug is only in the output audio -- the printed values are still all fine.
I'm using miniaudio as audio backend, and it's the only dependency. It should compile without errors nor warnings on Win, Linux and Mac.
It's a single header library, you only need to include miniaudio.h, so it should be easy to replicate.
Here is my code:
/*
compiling on Win10 with GCC:
gcc -g0 test_nodep.c -o test_nodep.exe -Wall -Wextra -Wshadow -Wvla -pedantic-errors -ansi
*/
#include <stdio.h>
#include <math.h>
#include <float.h>
#include <stdint.h>
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h" /* https://github.com/mackron/miniaudio - single header file audio os backend library */
/* global variables */
int32_t DEVICE_FORMAT = ma_format_f32; /* 32-bit float */
int32_t DEVICE_CHANNELS = 1; /* mono */
int32_t DEVICE_SAMPLE_RATE = 48000;
float clock = 0;
float time = 0;
static __inline__ float tik(float interval, float len, float offset){
return (len<=0)*(fmod(time-offset, interval)==0) +
(len>0)*((fmod(time-offset, interval)>=0)&&(fmod(time-offset, interval)<=(len)));
}
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount){
float* Samples = pOutput;
ma_uint32 SampleIndex;
/* audio-callback variable definitions */
float test_saw;
float test_saw_freq = 1.f;
float i;
for(SampleIndex = 0; SampleIndex < frameCount; SampleIndex++){
test_saw = fmod(clock, (DEVICE_SAMPLE_RATE/test_saw_freq))/(DEVICE_SAMPLE_RATE/test_saw_freq); /* 1Hz rising saw, output range [0..1] */
test_saw = test_saw * 100.f + 100.f; /* shift range into [100..200] */
if(tik(.125f,0.f,0.f)){ /* this is to print the test_saw value every 1/8 of a second */
printf("== test_saw: %.2f", test_saw);
for(i=0.f;i<test_saw/10.f;i++){
printf(" ");
}
printf("%c\n", 254);
}
/* this is the output function, a sinewave, with frequency sweeping continuously from 100Hz to 200Hz */
/* f(t) = sin(2*PI * frequency + time) */
/* instead of a fixed frequency, I'm using test_saw, sweeping from 100Hz to 200Hz every second */
*Samples = (float)sin((double)(time * MA_TAU * test_saw));
/* using the same function with a fixed frequency works as expected, no problems */
/* *Samples = (float)sin((double)(time * MA_TAU * 100.f)); */
clock++;
clock*=(clock<FLT_MAX); /* continuously rising value, +1 on each sample, zeroes out when float is at its max value, to prevent float overflow */
time = clock/DEVICE_SAMPLE_RATE; /* same value, in seconds */
Samples++;
}
(void)pDevice;
(void)pInput;
}
int main(){
ma_device_config deviceConfig;
ma_device device;
/* audio output device configuration */
deviceConfig = ma_device_config_init(ma_device_type_playback); /* initialize for playback */
deviceConfig.playback.format = DEVICE_FORMAT;
deviceConfig.playback.channels = DEVICE_CHANNELS;
deviceConfig.sampleRate = DEVICE_SAMPLE_RATE;
deviceConfig.dataCallback = data_callback;
/* audio output device initialization */
if(ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS){
printf("Failed to open playback device.\n");
return -4;
}
printf("== Device Name: %s\n", device.playback.name);
printf("== Sample Rate: %u Hz\n", DEVICE_SAMPLE_RATE);
if (ma_device_start(&device) != MA_SUCCESS) {
printf("== Failed to start playback device.\n");
ma_device_uninit(&device);
return -5;
}
printf("~~~ You should hear sound now ~~~\n");
printf("== Press Enter to quit...");
getchar();
ma_device_uninit(&device); /* turn off sound */
return 0;
}