audio is getting closer?
This commit is contained in:
parent
243e13b19e
commit
8493ff5fea
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -4,6 +4,7 @@
|
||||
"ppu_sm.h": "c",
|
||||
"string.h": "c",
|
||||
"common.h": "c",
|
||||
"ppu.h": "c"
|
||||
"ppu.h": "c",
|
||||
"profileapi.h": "c"
|
||||
}
|
||||
}
|
@ -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_period_tick();
|
||||
|
||||
u8 audio_read(u16 address);
|
||||
void audio_write(u16 address, u8 value);
|
303
lib/audio.c
303
lib/audio.c
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
7
lib/io.c
7
lib/io.c
@ -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);
|
||||
}
|
||||
|
||||
|
20
lib/timer.c
20
lib/timer.c
@ -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(¤t);
|
||||
// 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){
|
||||
|
Loading…
Reference in New Issue
Block a user