audio is getting closer?

This commit is contained in:
Samuel Walker 2025-02-02 20:36:32 -07:00
parent 243e13b19e
commit 8493ff5fea
Signed by: piwalker
GPG Key ID: 616B1928705EA4C9
5 changed files with 355 additions and 46 deletions

View File

@ -4,6 +4,7 @@
"ppu_sm.h": "c", "ppu_sm.h": "c",
"string.h": "c", "string.h": "c",
"common.h": "c", "common.h": "c",
"ppu.h": "c" "ppu.h": "c",
"profileapi.h": "c"
} }
} }

View File

@ -3,13 +3,71 @@
#include <common.h> #include <common.h>
typedef struct { typedef struct {
bool audio_enabled;
bool ch1_left;
bool ch1_right;
bool ch2_left;
bool ch2_right;
bool ch3_left;
bool ch3_right;
bool ch4_left;
bool ch4_right;
u8 volume_left;
u8 volume_right;
float sq1_freq; float sq1_freq;
float sq1_duty; u8 sq1_duty;
float sq1_amp; u8 sq1_volume;
float sq2_freq; float sq2_freq;
float sq2_duty; u8 sq2_duty;
float sq2_amp; u8 sq2_volume;
bool ready;
u8 sq1_sample;
float sq1_value;
u16 sq1_period_reset;
u16 sq1_period_timer;
bool sq1_enable;
bool sq1_len_enable;
u8 sq1_sweep_pace;
bool sq1_sweep_direction;
u8 sq1_initial_len;
u8 sq1_len;
u8 sq1_initial_volume;
bool sq1_env_direction;
u8 sq1_env_pace;
u8 sq1_env_timer;
u8 sq1_sweep_step;
u8 sq1_audio_buffer[4096];
u32 sq1_write_head;
u32 sq1_read_head;
u8 sq2_sample;
float sq2_value;
u16 sq2_period_reset;
u16 sq2_period_timer;
bool sq2_enable;
bool sq2_len_enable;
u8 sq2_sweep_pace;
bool sq2_sweep_direction;
u8 sq2_initial_len;
u8 sq2_len;
u8 sq2_initial_volume;
bool sq2_env_direction;
u8 sq2_env_pace;
u8 sq2_env_timer;
u8 sq2_sweep_step;
u8 sq2_audio_buffer[4096];
u32 sq2_write_head;
u32 sq2_read_head;
} audio_context; } audio_context;
void audio_init(); void audio_init();
void audio_tick(); void audio_tick();
void audio_period_tick();
u8 audio_read(u16 address);
void audio_write(u16 address, u8 value);

View File

@ -1,5 +1,6 @@
#include <audio.h> #include <audio.h>
#include <math.h> #include <math.h>
#include <windows.h>
#include <portaudio.h> #include <portaudio.h>
@ -25,66 +26,109 @@ static int sq2_timer = 0;
static audio_context ctx; static audio_context ctx;
HANDLE mutex;
const u8 square_sample[8] = {
0x0,
0x0,
0x0,
0x0,
0x1,
0x1,
0x1,
0x1
};
static int audio_callback(const void* input_uffer, void *output_buffer, static int audio_callback(const void* input_uffer, void *output_buffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *time_info, const PaStreamCallbackTimeInfo *time_info,
PaStreamCallbackFlags status_flags, PaStreamCallbackFlags status_flags,
void *userData ) { void *userData ) {
//printf("Audio Callback!\n"); //printf("Audio Callback!\n");
audio_context *ctx = (audio_context *) userData; //audio_context *ctx = (audio_context *) userData;
float *out = (float *)output_buffer; float *out = (float *)output_buffer;
PaTime time = time_info->currentTime; //static float last = 0;
sq1_on_time = 1/(ctx->sq1_freq/(float)SAMPLE_RATE) * ctx->sq1_duty; //float val;
sq1_off_time = 1/(ctx->sq1_freq/(float)SAMPLE_RATE) * (1 - ctx->sq1_duty); //if(!ctx.ready) {
// val = last;
//} else {
// val = ctx.sq1_value;
// last = val;
// ctx.ready = false;
//}
//PaTime time = time_info->currentTime;
//sq1_on_time = 1/(ctx->sq1_freq/(float)SAMPLE_RATE) * ctx->sq1_duty;
//sq1_off_time = 1/(ctx->sq1_freq/(float)SAMPLE_RATE) * (1 - ctx->sq1_duty);
sq2_on_time = 1/(ctx->sq2_freq/(float)SAMPLE_RATE) * ctx->sq2_duty; //sq2_on_time = 1/(ctx->sq2_freq/(float)SAMPLE_RATE) * ctx->sq2_duty;
sq2_off_time = 1/(ctx->sq2_freq/(float)SAMPLE_RATE) * (1 - ctx->sq2_duty); //sq2_off_time = 1/(ctx->sq2_freq/(float)SAMPLE_RATE) * (1 - ctx->sq2_duty);
//ctx.sq1_period_timer++;
//if(ctx.sq1_period_timer >= 0x800) {
//ctx.sq1_period_timer = ctx.sq1_period_reset;
//ctx.sq1_sample = (ctx.sq1_sample + 1) % 8;
//ctx.sq1_value = (float)square_sample[ctx.sq1_sample];///15.0f;
//ctx.sq1_value = - ctx.sq1_value;
//}
//printf("val: %f\n", val);
float left = 0;
float right = 0;
for(int i = 0; i < framesPerBuffer; i++) { for(int i = 0; i < framesPerBuffer; i++) {
float val = 0; if(ctx.sq1_write_head != ctx.sq1_read_head) {
//handle audio channels if(ctx.ch1_left){
//val += 0.2 * sine[ind]; left = (float)(ctx.sq1_volume * ctx.sq1_audio_buffer[ctx.sq1_read_head])/15.0f;
//ind += 500; }
if((sq1_timer >= sq1_on_time && sq1 == 1)) { if(ctx.ch1_right){
sq1 = 0; right = (float)(ctx.sq1_volume * ctx.sq1_audio_buffer[ctx.sq1_read_head])/15.0f;
sq1_timer = 0; }
} else if((sq1_timer >= sq1_off_time && sq1 == 0)){ ctx.sq1_read_head++;
sq1 = 1; if (ctx.sq1_read_head >= 4096) {
sq1_timer = 0; ctx.sq1_read_head = 0;
}
} }
sq1_timer++;
val += ctx->sq1_amp * sq1;
if((sq2_timer >= sq2_on_time && sq2 == 1)) { if(ctx.sq2_write_head != ctx.sq2_read_head) {
sq2 = 0; if(ctx.ch2_left){
sq2_timer = 0; left += (float)(ctx.sq2_volume * ctx.sq2_audio_buffer[ctx.sq2_read_head])/15.0f;
} else if((sq2_timer >= sq2_off_time && sq2 == 0)){ }
sq2 = 1; if(ctx.ch2_right){
sq2_timer = 0; right += (float)(ctx.sq2_volume * ctx.sq2_audio_buffer[ctx.sq2_read_head])/15.0f;
}
ctx.sq2_read_head++;
if (ctx.sq2_read_head >= 4096) {
ctx.sq2_read_head = 0;
}
} }
sq2_timer++; left *= (float)ctx.volume_left/8.0f;
val += ctx->sq2_amp * sq2; right *= (float)ctx.volume_right/8.0f;
//val += 0.2 * sine[ind2]; *out++ = left;
//ind2 += 200; *out++ = right;
if(ind >= TABLE_SIZE) ind -= TABLE_SIZE;
if(ind2 >= TABLE_SIZE) ind2 -= TABLE_SIZE;
*out++ = val;
} }
return paContinue; return paContinue;
} }
void audio_init(){ void audio_init(){
mutex = CreateMutex(NULL, FALSE, NULL);
PaStreamParameters output_parameters; PaStreamParameters output_parameters;
PaStream *stream; PaStream *stream;
PaError err; PaError err;
ctx.sq1_amp = 0.2; ctx.sq1_volume = 1;
ctx.sq1_duty = 0.5; ctx.sq1_duty = 0.5;
ctx.sq1_freq = 200; ctx.sq1_freq = 200;
ctx.sq2_amp = 0; ctx.sq2_volume = 1;
ctx.sq2_duty = 0.5; ctx.sq2_duty = 0.5;
ctx.sq2_freq = 400; ctx.sq2_freq = 400;
ctx.sq1_period_reset = 0x100;
ctx.sq1_period_timer = 0x100;
ctx.sq1_value = -1;
ctx.sq1_write_head = 0;
ctx.sq1_read_head = 0;
ctx.sq2_read_head = 0;
ctx.sq2_write_head = 0;
for(int i = 0; i < TABLE_SIZE; i++) { for(int i = 0; i < TABLE_SIZE; i++) {
sine[i] = (float) sin(((double)i/(double)TABLE_SIZE) * M_PI * 2.); sine[i] = (float) sin(((double)i/(double)TABLE_SIZE) * M_PI * 2.);
} }
@ -96,7 +140,7 @@ void audio_init(){
fprintf(stderr, "No default audio device!\n"); fprintf(stderr, "No default audio device!\n");
goto error; goto error;
} }
output_parameters.channelCount = 1; output_parameters.channelCount = 2;
output_parameters.sampleFormat = paFloat32; output_parameters.sampleFormat = paFloat32;
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;
@ -119,10 +163,195 @@ error:
} }
static int change = 1; static int change = 1;
static u32 ticks = 0;
void audio_tick(){ void audio_tick(){
ctx.sq1_freq += change; u32 prev_ticks = ticks;
if(ctx.sq1_freq >= 600 || ctx.sq1_freq <= 200) { ticks++;
change = -change; if(!(ticks & 0b1)) {
if(ctx.sq1_len_enable) {
ctx.sq1_len++;
if(ctx.sq1_len >= 64) {
ctx.sq1_enable = false;
}
}
if(ctx.sq2_len_enable) {
ctx.sq2_len++;
if(ctx.sq2_len >= 64) {
ctx.sq2_enable = false;
printf("sq2 timer");
}
}
}
if((prev_ticks & (1 << 3)) && !(ticks & (1 << 3))) {
if(ctx.sq1_env_pace != 0){
ctx.sq1_env_timer++;
if(ctx.sq1_env_timer >= ctx.sq1_env_pace) {
ctx.sq1_volume += ctx.sq1_env_direction ? 1 : -1;
if(ctx.sq1_volume < 0)
ctx.sq1_volume = 0;
if(ctx.sq1_volume > 7)
ctx.sq1_volume = 7;
ctx.sq1_env_timer = 0;
}
}
if(ctx.sq2_env_pace != 0){
ctx.sq2_env_timer++;
if(ctx.sq2_env_timer >= ctx.sq2_env_pace) {
ctx.sq2_volume += ctx.sq2_env_direction ? 1 : -1;
if(ctx.sq2_volume < 0)
ctx.sq2_volume = 0;
if(ctx.sq2_volume > 7)
ctx.sq2_volume = 7;
ctx.sq2_env_timer = 0;
}
}
}
}
static u32 updates;
void audio_period_tick(){
updates++;
//DWORD dwaitResult = WaitForSingleObject(mutex, INFINITE);
//if(dwaitResult == WAIT_ABANDONED)
// return;
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;
//ctx.sq1_value = (float)square_sample[ctx.sq1_sample];///15.0f;
//ctx.ready = true;
}
if(ctx.sq2_period_timer >= 0x800) {
ctx.sq2_period_timer = ctx.sq2_period_reset;
ctx.sq2_sample = (ctx.sq2_sample + 1) % 8;
//ctx.sq1_value = (float)square_sample[ctx.sq1_sample];///15.0f;
//ctx.ready = true;
}
if(updates >= 24) {
updates = 0;
//u32 read = ctx.read_head;
//u32 write = ctx.write_head;
//if(read > write) {
// write += 4096;
//}
//if(write - read <= 64){
// return;
//}
if(ctx.sq1_enable){
ctx.sq1_audio_buffer[ctx.sq1_write_head++] = square_sample[ctx.sq1_sample];
if(ctx.sq1_write_head >= 4096) {
ctx.sq1_write_head = 0;
}
}
if(ctx.sq2_enable){
ctx.sq2_audio_buffer[ctx.sq2_write_head++] = square_sample[ctx.sq2_sample];
if(ctx.sq2_write_head >= 4096) {
ctx.sq2_write_head = 0;
}
}
}
//ReleaseMutex(mutex);
}
void enable_square1() {
ctx.sq1_enable = true;
if(ctx.sq1_len >= 64) {
ctx.sq1_len = ctx.sq1_initial_len;
}
ctx.sq1_volume = ctx.sq1_initial_volume;
ctx.sq1_env_timer = 0;
}
void enable_square2() {
//ctx.sq2_enable = true;
if(ctx.sq2_len >= 64) {
ctx.sq2_len = ctx.sq2_initial_len;
}
ctx.sq2_volume = ctx.sq2_initial_volume;
ctx.sq2_env_timer = 0;
}
u8 audio_read(u16 address) {
return 0x00;
}
void audio_write(u16 address, u8 value){
if(address == 0xFF26) {
ctx.audio_enabled = value & 0x80;
}
if(address == 0xFF25) {
ctx.ch4_left = value & 0b10000000;
ctx.ch3_left = value & 0b01000000;
ctx.ch2_left = value & 0b00100000;
ctx.ch1_left = value & 0b00010000;
ctx.ch4_right = value & 0b00001000;
ctx.ch3_right = value & 0b00000100;
ctx.ch2_right = value & 0b00000010;
ctx.ch1_right = value & 0b00000001;
}
if(address == 0xFF24) {
ctx.volume_left = (value >> 4) & 0b111;
if(ctx.volume_left == 0) ctx.volume_left = 1;
ctx.volume_right = value & 0b111;
if(ctx.volume_right == 0) ctx.volume_right = 1;
}
if(address == 0xFF10) {
ctx.sq1_sweep_pace = (value >> 4) & 0b111;
ctx.sq1_sweep_direction = value & 0x08;
ctx.sq1_sweep_step = value & 0b111;
}
if(address == 0xFF11) {
ctx.sq1_duty = value >> 6;
ctx.sq1_initial_len = value & 0x3F;
}
if(address == 0xFF12) {
ctx.sq1_initial_volume = value >> 4;
ctx.sq1_env_direction = value & 0x8;
ctx.sq1_env_pace = value & 0b111;
}
if(address == 0xFF13) {
ctx.sq1_period_reset = (ctx.sq1_period_reset & 0xF0) | value;
}
if(address == 0xFF14) {
ctx.sq1_period_reset = (ctx.sq1_period_reset & 0x0F) | ((value & 0b111) << 8);
ctx.sq1_len_enable = value & 0x40;
if(value & 0x80) {
enable_square1();
}
}
if(address == 0xFF16) {
ctx.sq2_duty = value >> 6;
ctx.sq2_initial_len = value & 0x3F;
}
if(address == 0xFF17) {
ctx.sq2_initial_volume = value >> 4;
ctx.sq2_env_direction = value & 0x8;
ctx.sq2_env_pace = value * 0b111;
}
if(address == 0xFF18) {
ctx.sq2_period_reset = (ctx.sq2_period_reset & 0xF0) | value;
}
if(address == 0xFF19) {
ctx.sq2_period_reset = (ctx.sq2_period_reset & 0x0F) | ((value & 0b111) << 8);
ctx.sq2_len_enable = value & 0x40;
if(value & 0x80) {
enable_square2();
}
} }
} }

View File

@ -4,6 +4,7 @@
#include <dma.h> #include <dma.h>
#include <lcd.h> #include <lcd.h>
#include <gamepad.h> #include <gamepad.h>
#include <audio.h>
static char serial_data[2]; static char serial_data[2];
@ -32,8 +33,7 @@ u8 io_read(u16 address){
} }
if(BETWEEN(address, 0xFF10, 0xFF3F)) { if(BETWEEN(address, 0xFF10, 0xFF3F)) {
//ignore sound return audio_read(address);
return 0;
} }
printf("UNSUPPORTED io_read(%04X)\n", address); printf("UNSUPPORTED io_read(%04X)\n", address);
@ -73,10 +73,11 @@ void io_write(u16 address, u8 value){
} }
if(BETWEEN(address, 0xFF10, 0xFF3F)) { if(BETWEEN(address, 0xFF10, 0xFF3F)) {
//ignore sound audio_write(address, value);
return; return;
} }
printf("UNSUPPORTED io_write(%04X)\n", address); printf("UNSUPPORTED io_write(%04X)\n", address);
} }

View File

@ -1,15 +1,31 @@
#include <Windows.h>
#include <timer.h> #include <timer.h>
#include <interrupts.h> #include <interrupts.h>
#include <audio.h> #include <audio.h>
#include <profileapi.h>
static timer_context ctx = {0}; static timer_context ctx = {0};
long long counts_per_cycle;
long long last;
long long now;
void timer_init() { void timer_init() {
ctx.div = 0XAC00; ctx.div = 0XAC00;
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
counts_per_cycle = freq.QuadPart / (1 << 19);
} }
void timer_tick() { void timer_tick() {
//while(now - last < counts_per_cycle){
// LARGE_INTEGER current;
// QueryPerformanceCounter(&current);
// now = current.QuadPart;
// //printf("Last-now: %lld, counts: %lld\n", now - last, counts_per_cycle);
//}
last = now;
u16 prev_div = ctx.div; u16 prev_div = ctx.div;
ctx.div++; ctx.div++;
bool timer_update = false; bool timer_update = false;
@ -42,6 +58,10 @@ void timer_tick() {
if((prev_div & (1 << 12)) && (!(ctx.div & (1 << 12)))){ if((prev_div & (1 << 12)) && (!(ctx.div & (1 << 12)))){
audio_tick(); audio_tick();
} }
if((prev_div & (1 << 1)) && (!(ctx.div & (1 << 1)))){
audio_period_tick();
}
} }
void timer_write(u16 address, u8 value){ void timer_write(u16 address, u8 value){