Fixed audio buffer popping

This commit is contained in:
Samuel Walker 2025-02-18 17:15:17 -07:00
parent 8e5c3234ef
commit a094e5123b
3 changed files with 116 additions and 70 deletions

View File

@ -134,6 +134,7 @@ void audio_init();
void audio_tick(); void audio_tick();
void audio_period_tick(); void audio_period_tick();
void lfsr_tick(); void lfsr_tick();
void audio_sample_tick();
u8 audio_read(u16 address); u8 audio_read(u16 address);
void audio_write(u16 address, u8 value); void audio_write(u16 address, u8 value);

View File

@ -12,10 +12,11 @@
#define FRAMES_PER_BUFFER 64 #define FRAMES_PER_BUFFER 64
#define TIME_PER_SAMPLE 1.0f / (SAMPLE_RATE/1000.0f/1000.0f) #define TIME_PER_SAMPLE 1.0f / (SAMPLE_RATE/1000.0f/1000.0f)
#define TIME_PER_AUDIO_TICK 1.0f / (1048576.0f/1000.0f/1000.0f) #define TIME_PER_AUDIO_TICK 1.0f / (1048576.0f/1000.0f/1000.0f)
#define AUDIO_TICKS_PER_SAMPLE 2157281.28 / SAMPLE_RATE #define AUDIO_TICKS_PER_SAMPLE 4194304.0 / SAMPLE_RATE
#define SAMPLES_PER_AUDIO_TICK SAMPLE_RATE / 4194304.0
#define TIME_PER_WAVE_TICK 1.0f / (2104718.0f/1000.0f/1000.0f) #define TIME_PER_WAVE_TICK 1.0f / (2104718.0f/1000.0f/1000.0f)
#define LFSR_BASE_CLOCK 262144.0f #define LFSR_BASE_CLOCK 262144.0f
#define AUDIO_BUFFER_SIZE (int)(SAMPLE_RATE / 10) #define AUDIO_BUFFER_SIZE FRAMES_PER_BUFFER
static audio_context ctx; static audio_context ctx;
@ -103,14 +104,17 @@ static int audio_callback(const void* input_uffer, void *output_buffer,
//} //}
for(int i = 0; i < framesPerBuffer; i++) { for(int i = 0; i < framesPerBuffer; i++) {
//samples_occured = samples_occured + AUDIO_TICKS_PER_SAMPLE-1; //samples_occured = samples_occured + AUDIO_TICKS_PER_SAMPLE-1;
while(ctx.buffer_cnt == 0) { //while(ctx.buffer_cnt == 0) {
delay(1); // delay(1);
} //}
if(ctx.buffer_cnt == 0) { if(ctx.buffer_cnt == 0) {
//samples_occured = samples_occured - ((int)samples_occured - ctx.buffer_cnt); //samples_occured = samples_occured - ((int)samples_occured - ctx.buffer_cnt);
//ctx.sq1_read_index = ctx.sq1_write_index-1; //ctx.sq1_read_index = ctx.sq1_write_index-1;
//printf("buffer underflow\n"); //printf("buffer underflow\n");
return paContinue; continue;
//ctx.buffer_cnt++;
//ctx.sq1_read_index--;
//ctx.sq1_read_index %= AUDIO_BUFFER_SIZE;
} else { } else {
//ctx.buffer_cnt -= (u8)samples_occured; //ctx.buffer_cnt -= (u8)samples_occured;
//ctx.sq1_read_index += (u8)samples_occured; //ctx.sq1_read_index += (u8)samples_occured;
@ -218,9 +222,6 @@ static int audio_callback(const void* input_uffer, void *output_buffer,
left *= (float)left_vol/7.0f; left *= (float)left_vol/7.0f;
right *= (float)right_vol/7.0f; right *= (float)right_vol/7.0f;
//left /= 4;
//right /= 4;
left = ctx.sq1_audio_buffer[ctx.sq1_read_index]; left = ctx.sq1_audio_buffer[ctx.sq1_read_index];
right = ctx.sq2_audio_buffer[ctx.sq1_read_index]; right = ctx.sq2_audio_buffer[ctx.sq1_read_index];
ctx.sq1_read_index++; ctx.sq1_read_index++;
@ -233,8 +234,8 @@ static int audio_callback(const void* input_uffer, void *output_buffer,
if(dacs) { if(dacs) {
left_out = left - left_cap; left_out = left - left_cap;
right_out = right - right_cap; right_out = right - right_cap;
left_cap = left - left_out * 0.996f; left_cap = left - left_out * 0.999958f;
right_cap = right - right_out * 0.996f; right_cap = right - right_out * 0.999958f;
} }
@ -333,9 +334,9 @@ void audio_init(){
ctx.sq2_env_direction = false; ctx.sq2_env_direction = false;
ctx.sq2_env_pace = 0x0; ctx.sq2_env_pace = 0x0;
ctx.sq2_env_timer = 0x0; ctx.sq2_env_timer = 0x0;
ctx.sq1_write_index = 0; ctx.sq1_write_index = AUDIO_BUFFER_SIZE - 1;
ctx.sq1_read_index = 0; ctx.sq1_read_index = 0;
ctx.buffer_cnt = 0; ctx.buffer_cnt = AUDIO_BUFFER_SIZE - 1;
err = Pa_Initialize(); err = Pa_Initialize();
if (err != paNoError) goto error; if (err != paNoError) goto error;
@ -345,7 +346,7 @@ void audio_init(){
goto error; goto error;
} }
output_parameters.channelCount = 2; output_parameters.channelCount = 2;
output_parameters.sampleFormat = paFloat32; output_parameters.sampleFormat = paFloat32 | paNonInterleaved;
output_parameters.suggestedLatency = Pa_GetDeviceInfo(output_parameters.device)->defaultLowOutputLatency; output_parameters.suggestedLatency = Pa_GetDeviceInfo(output_parameters.device)->defaultLowOutputLatency;
output_parameters.hostApiSpecificStreamInfo = NULL; output_parameters.hostApiSpecificStreamInfo = NULL;
printf("default sample rate: %lf\n", Pa_GetDeviceInfo(output_parameters.device)->defaultSampleRate); printf("default sample rate: %lf\n", Pa_GetDeviceInfo(output_parameters.device)->defaultSampleRate);
@ -354,9 +355,9 @@ void audio_init(){
NULL, NULL,
&output_parameters, &output_parameters,
SAMPLE_RATE, SAMPLE_RATE,
paFramesPerBufferUnspecified, FRAMES_PER_BUFFER,
paNoFlag, paNoFlag,
audio_callback, NULL,
&ctx); &ctx);
if(err != paNoError) goto error; if(err != paNoError) goto error;
err = Pa_StartStream(stream); err = Pa_StartStream(stream);
@ -399,51 +400,10 @@ void lfsr_tick() {
} }
} }
static double cycles_needed = 0;
void audio_period_tick() { void audio_sample_tick() {
period_tick++; lfsr_tick();
u32 end = get_ticks();
samples_per_sec++;
if (end - start >= 1000) {
printf("Samples In Buffer: %ld\n", ctx.buffer_cnt);
start = end;
samples_per_sec = 0;
}
if(period_tick % 2 == 0){
ctx.sq1_period_timer++;
ctx.sq2_period_timer++;
if(ctx.sq1_period_timer >= 0x800) {
ctx.sq1_period_timer = ctx.sq1_period_reset;
ctx.sq1_sample = (ctx.sq1_sample + 1) % 8;
}
if(ctx.sq2_period_timer >= 0x800) {
ctx.sq2_period_timer = ctx.sq2_period_reset;
ctx.sq2_sample = (ctx.sq2_sample + 1) % 8;
}
}
u8 shift = 0;
if(ctx.ch3_volume == 0b10) {
shift = 1;
}
if(ctx.ch3_volume == 0b00) {
shift = 4;
}
if(ctx.ch3_volume == 0b11) {
shift = 2;
}
ctx.ch3_period_timer++;
if(ctx.ch3_period_timer >= 0x800) {
ctx.ch3_period_timer = ctx.ch3_period_reset;
ctx.ch3_sample = (ctx.ch3_sample + 1) % 32;
if((ctx.ch3_sample & 0b1) == 0b1) {
ctx.ch3_last_sample = ctx.wave_ram[ctx.ch3_sample >> 1] & 0xF;
} else {
ctx.ch3_last_sample = ctx.wave_ram[ctx.ch3_sample >> 1] >> 4;
}
}
float left = 0; float left = 0;
float right = 0; float right = 0;
float sq1_val = 0; float sq1_val = 0;
@ -490,6 +450,16 @@ void audio_period_tick() {
} }
} }
if(ctx.ch3_dac){ if(ctx.ch3_dac){
u8 shift = 0;
if(ctx.ch3_volume == 0b10) {
shift = 1;
}
if(ctx.ch3_volume == 0b00) {
shift = 4;
}
if(ctx.ch3_volume == 0b11) {
shift = 2;
}
ch3_val = -1; ch3_val = -1;
if(ctx.ch3_enable) { if(ctx.ch3_enable) {
ch3_val = (((float)(ctx.ch3_last_sample >> shift) - 7.5f)/7.5f); ch3_val = (((float)(ctx.ch3_last_sample >> shift) - 7.5f)/7.5f);
@ -557,25 +527,100 @@ void audio_period_tick() {
ctx.ch4_index = 0; ctx.ch4_index = 0;
} }
} }
smooth_left = smooth_left - (LPF_Beta * (smooth_left - left)); smooth_left = smooth_left - (LPF_Beta * (smooth_left - left));
smooth_right = smooth_right - (LPF_Beta * (smooth_right - right)); smooth_right = smooth_right - (LPF_Beta * (smooth_right - right));
cycles_needed += SAMPLES_PER_AUDIO_TICK;
if((period_tick % (int)(AUDIO_TICKS_PER_SAMPLE)) == 0){ if(cycles_needed > 1){
//printf("tick\n"); //printf("tick\n");
while(ctx.buffer_cnt == AUDIO_BUFFER_SIZE){ //if(ctx.buffer_cnt == AUDIO_BUFFER_SIZE){
//ctx.buffer_cnt--; //ctx.buffer_cnt--;
//ctx.sq1_read_index++; //ctx.sq1_read_index++;
//ctx.sq1_read_index %= AUDIO_BUFFER_SIZE; //ctx.sq1_read_index %= AUDIO_BUFFER_SIZE;
delay(10); //delay(1);
//printf("overflow\n"); //printf("overflow\n");
//return;
//}
cycles_needed -= 1;
bool dacs = ctx.ch1_dac || ctx.ch2_dac || ctx.ch3_dac || ctx.ch4_dac;
float left_out = 0;
float right_out = 0;
if(dacs) {
left_out = smooth_left - left_cap;
right_out = smooth_right - right_cap;
left_cap = smooth_left - left_out * 0.996f;
right_cap = smooth_right - right_out * 0.996f;
} }
ctx.sq1_audio_buffer[(ctx.sq1_write_index)] = smooth_left;
ctx.sq2_audio_buffer[(ctx.sq1_write_index)] = smooth_right;
left_out /= 4;
right_out /= 4;
if(left_out < -1.0f) {
left_out = -1;
}
if(left_out > 1.0f) {
left_out = 1;
}
if(right_out < -1.0f) {
right_out = -1;
}
if(right_out > 1.0f) {
right_out = 1;
}
ctx.sq1_audio_buffer[(ctx.sq1_write_index)] = left_out;
ctx.sq2_audio_buffer[(ctx.sq1_write_index)] = right_out;
//ctx.ch3_audio_buffer[(ctx.sq1_write_index)] = (((float)(ctx.ch3_last_sample >> shift)-7.5f)/7.5f); //ctx.ch3_audio_buffer[(ctx.sq1_write_index)] = (((float)(ctx.ch3_last_sample >> shift)-7.5f)/7.5f);
//ctx.ch4_audio_buffer[(ctx.sq1_write_index)] = (ctx.ch4_lfsr & 0b1) ? (float)(ctx.ch4_volume-7.5f)/7.5f : -1.0f; //ctx.ch4_audio_buffer[(ctx.sq1_write_index)] = (ctx.ch4_lfsr & 0b1) ? (float)(ctx.ch4_volume-7.5f)/7.5f : -1.0f;
ctx.sq1_write_index = (ctx.sq1_write_index + 1) % AUDIO_BUFFER_SIZE; ctx.sq1_write_index = (ctx.sq1_write_index + 1);
ctx.buffer_cnt++; //ctx.buffer_cnt++;
if(ctx.sq1_write_index == FRAMES_PER_BUFFER) {
ctx.sq1_write_index = 0;
float *data[2];
data[0] = ctx.sq1_audio_buffer;
data[1] = ctx.sq2_audio_buffer;
int err = Pa_WriteStream(stream, data, FRAMES_PER_BUFFER);
if(err != paNoError) {
fprintf(stderr, "portaudio stream error\n\tError Number: %d\n\tError Message: %s\n", err, Pa_GetErrorText(err));
}
}
}
}
bool skipped = false;
void audio_period_tick() {
period_tick++;
u32 end = get_ticks();
samples_per_sec++;
if (end - start >= 1000) {
printf("Samples In Buffer: %ld\n", ctx.buffer_cnt);
start = end;
samples_per_sec = 0;
}
if(period_tick % 2 == 0){
ctx.sq1_period_timer++;
ctx.sq2_period_timer++;
if(ctx.sq1_period_timer >= 0x800) {
ctx.sq1_period_timer = ctx.sq1_period_reset;
ctx.sq1_sample = (ctx.sq1_sample + 1) % 8;
}
if(ctx.sq2_period_timer >= 0x800) {
ctx.sq2_period_timer = ctx.sq2_period_reset;
ctx.sq2_sample = (ctx.sq2_sample + 1) % 8;
}
}
ctx.ch3_period_timer++;
if(ctx.ch3_period_timer >= 0x800) {
ctx.ch3_period_timer = ctx.ch3_period_reset;
ctx.ch3_sample = (ctx.ch3_sample + 1) % 32;
if((ctx.ch3_sample & 0b1) == 0b1) {
ctx.ch3_last_sample = ctx.wave_ram[ctx.ch3_sample >> 1] & 0xF;
} else {
ctx.ch3_last_sample = ctx.wave_ram[ctx.ch3_sample >> 1] >> 4;
}
} }
} }

View File

@ -39,7 +39,7 @@ void timer_tick() {
} }
} }
lfsr_tick(); audio_sample_tick();
if((prev_div & (1 << 12)) && (!(ctx.div & (1 << 12)))){ if((prev_div & (1 << 12)) && (!(ctx.div & (1 << 12)))){
audio_tick(); audio_tick();