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",
"string.h": "c",
"common.h": "c",
"ppu.h": "c"
"ppu.h": "c",
"profileapi.h": "c"
}
}

View File

@ -3,13 +3,71 @@
#include <common.h>
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_duty;
float sq1_amp;
u8 sq1_duty;
u8 sq1_volume;
float sq2_freq;
float sq2_duty;
float sq2_amp;
u8 sq2_duty;
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;
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 <math.h>
#include <windows.h>
#include <portaudio.h>
@ -25,66 +26,109 @@ static int sq2_timer = 0;
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,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *time_info,
PaStreamCallbackFlags status_flags,
void *userData ) {
//printf("Audio Callback!\n");
audio_context *ctx = (audio_context *) userData;
//audio_context *ctx = (audio_context *) userData;
float *out = (float *)output_buffer;
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);
//static float last = 0;
//float val;
//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_off_time = 1/(ctx->sq2_freq/(float)SAMPLE_RATE) * (1 - 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);
//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++) {
float val = 0;
//handle audio channels
//val += 0.2 * sine[ind];
//ind += 500;
if((sq1_timer >= sq1_on_time && sq1 == 1)) {
sq1 = 0;
sq1_timer = 0;
} else if((sq1_timer >= sq1_off_time && sq1 == 0)){
sq1 = 1;
sq1_timer = 0;
if(ctx.sq1_write_head != ctx.sq1_read_head) {
if(ctx.ch1_left){
left = (float)(ctx.sq1_volume * ctx.sq1_audio_buffer[ctx.sq1_read_head])/15.0f;
}
if(ctx.ch1_right){
right = (float)(ctx.sq1_volume * ctx.sq1_audio_buffer[ctx.sq1_read_head])/15.0f;
}
ctx.sq1_read_head++;
if (ctx.sq1_read_head >= 4096) {
ctx.sq1_read_head = 0;
}
}
sq1_timer++;
val += ctx->sq1_amp * sq1;
if((sq2_timer >= sq2_on_time && sq2 == 1)) {
sq2 = 0;
sq2_timer = 0;
} else if((sq2_timer >= sq2_off_time && sq2 == 0)){
sq2 = 1;
sq2_timer = 0;
if(ctx.sq2_write_head != ctx.sq2_read_head) {
if(ctx.ch2_left){
left += (float)(ctx.sq2_volume * ctx.sq2_audio_buffer[ctx.sq2_read_head])/15.0f;
}
if(ctx.ch2_right){
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++;
val += ctx->sq2_amp * sq2;
//val += 0.2 * sine[ind2];
//ind2 += 200;
if(ind >= TABLE_SIZE) ind -= TABLE_SIZE;
if(ind2 >= TABLE_SIZE) ind2 -= TABLE_SIZE;
*out++ = val;
left *= (float)ctx.volume_left/8.0f;
right *= (float)ctx.volume_right/8.0f;
*out++ = left;
*out++ = right;
}
return paContinue;
}
void audio_init(){
mutex = CreateMutex(NULL, FALSE, NULL);
PaStreamParameters output_parameters;
PaStream *stream;
PaError err;
ctx.sq1_amp = 0.2;
ctx.sq1_volume = 1;
ctx.sq1_duty = 0.5;
ctx.sq1_freq = 200;
ctx.sq2_amp = 0;
ctx.sq2_volume = 1;
ctx.sq2_duty = 0.5;
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++) {
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");
goto error;
}
output_parameters.channelCount = 1;
output_parameters.channelCount = 2;
output_parameters.sampleFormat = paFloat32;
output_parameters.suggestedLatency = Pa_GetDeviceInfo(output_parameters.device)->defaultLowOutputLatency;
output_parameters.hostApiSpecificStreamInfo = NULL;
@ -119,10 +163,195 @@ error:
}
static int change = 1;
static u32 ticks = 0;
void audio_tick(){
ctx.sq1_freq += change;
if(ctx.sq1_freq >= 600 || ctx.sq1_freq <= 200) {
change = -change;
u32 prev_ticks = ticks;
ticks++;
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 <lcd.h>
#include <gamepad.h>
#include <audio.h>
static char serial_data[2];
@ -32,8 +33,7 @@ u8 io_read(u16 address){
}
if(BETWEEN(address, 0xFF10, 0xFF3F)) {
//ignore sound
return 0;
return audio_read(address);
}
printf("UNSUPPORTED io_read(%04X)\n", address);
@ -73,10 +73,11 @@ void io_write(u16 address, u8 value){
}
if(BETWEEN(address, 0xFF10, 0xFF3F)) {
//ignore sound
audio_write(address, value);
return;
}
printf("UNSUPPORTED io_write(%04X)\n", address);
}

View File

@ -1,15 +1,31 @@
#include <Windows.h>
#include <timer.h>
#include <interrupts.h>
#include <audio.h>
#include <profileapi.h>
static timer_context ctx = {0};
long long counts_per_cycle;
long long last;
long long now;
void timer_init() {
ctx.div = 0XAC00;
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
counts_per_cycle = freq.QuadPart / (1 << 19);
}
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;
ctx.div++;
bool timer_update = false;
@ -42,6 +58,10 @@ void timer_tick() {
if((prev_div & (1 << 12)) && (!(ctx.div & (1 << 12)))){
audio_tick();
}
if((prev_div & (1 << 1)) && (!(ctx.div & (1 << 1)))){
audio_period_tick();
}
}
void timer_write(u16 address, u8 value){