From 5497bf0faae483daf44b4909cee81da48fdc86be Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 25 May 2021 19:02:20 +0200 Subject: [WIP] Add prototype APU --- roms/audio.rom | Bin 437 -> 435 bytes src/main.c | 148 +++++++++++-------------------- src/uxn/devices/apu.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 294 insertions(+), 95 deletions(-) create mode 100644 src/uxn/devices/apu.c diff --git a/roms/audio.rom b/roms/audio.rom index 462204d..b0bae38 100644 Binary files a/roms/audio.rom and b/roms/audio.rom differ diff --git a/src/main.c b/src/main.c index 0862ca5..fc7dad1 100644 --- a/src/main.c +++ b/src/main.c @@ -20,6 +20,7 @@ WITH REGARD TO THIS SOFTWARE. #include "uxn/uxn.c" #include "uxn/devices/ppu.h" #include "uxn/devices/ppu.c" +#include "uxn/devices/apu.c" #include "text.h" @@ -56,6 +57,7 @@ static Ppu ppu; static Device *devscreen; static Device *devctrl; static Device *devmouse; +static Device *devaudio; static Mouse mouse = {SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2}; @@ -110,6 +112,36 @@ screen_talk(Device *d, u8 b0, u8 w) { } } +static void +audio_talk(Device *d, u8 b0, u8 w) { + AudioChannel *c = &apu.chan_0; + if(!w) { + if(b0 == 0x2) { + mempoke16(d->dat, 0x2, c->position); + } else if(b0 == 0x4) { + // d->dat[0x4] = apu_get_vu(c); + } + } else if(b0 == 0xf) { + // SDL_LockAudioDevice(audio_id); + c->n_samples = mempeek16(d->dat, 0xa); + c->samples = &d->mem[mempeek16(d->dat, 0xc)]; + // Transform the samples from u8 to s8. + // for (size_t i = 0; i < c->n_samples; ++i) { + // c->samples[i] = c->samples[i] + 0x80; + // } + // c->volume[0] = d->dat[0xe] >> 4; + // c->volume[1] = d->dat[0xe] & 0xf; + c->loop = !(d->dat[0xf] & 0x80); + c->pitch = d->dat[0xf] & 0x7f; + init_sound(c); + reset_sound(c); + txt_printf("note: %d \n", c->pitch); + // apu_start(c, mempeek16(d->dat, 0x8), d->dat[0xf] & 0x7f); + // SDL_UnlockAudioDevice(audio_id); + } + // txt_printf("AUDIO TALK: %d\n", d); +} + void datetime_talk(Device *d, u8 b0, u8 w) { (void)d; @@ -155,7 +187,7 @@ init_uxn(Uxn *u) { portuxn(u, 0x0, "system", system_talk); portuxn(u, 0x1, "console", console_talk); devscreen = portuxn(u, 0x2, "screen", screen_talk); - portuxn(u, 0x3, "---", nil_talk); + devaudio = portuxn(u, 0x3, "audio0", audio_talk); portuxn(u, 0x4, "---", nil_talk); portuxn(u, 0x5, "---", nil_talk); portuxn(u, 0x6, "---", nil_talk); @@ -344,75 +376,6 @@ static Uxn u; EWRAM_BSS static u8 umem[65536]; -#include "kick.c" - -typedef struct AudioChannel { - u32 *samples; - u32 n_samples; - u16 sampling_freq; - bool loop; - // TODO: u16 adsr; // attack, decay, sustain, release - // TODO: u8 pitch; // Bit 8 is the "loop" bit - // TODO: u8 volume; // VOL_LEFT | (VOL_RIGHT << 4) -} AudioChannel; - -typedef struct APU { - AudioChannel chan_0; - // u32 *samples_1; - // u32 *samples_2; - // u32 *samples_3; -} APU; - - -static APU apu = {0}; - -void -reset_sound(AudioChannel *chan) { - TIMER_CTRL_0 = 0; - TIMER_CTRL_1 = 0; - DMA_CTRL(1) = 0; - - // Set max volume, left-right sound, fifo reset and use timer 0 for - // DirectSound A. - SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A - | SOUND_DSOUND_LEFT_A - | SOUND_DSOUND_RIGHT_A - | SOUND_DSOUND_RESET_A; - - // Prepare DMA copy. - dma_transfer_copy(SOUND_FIFO_A, chan->samples, 1, 1, - DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); - - // Timer 1 used to stop playing samples. - u32 sample_duration = chan->n_samples; - TIMER_DATA_1 = 0xFFFF - sample_duration; - TIMER_CTRL_1 = TIMER_CTRL_IRQ - | TIMER_CTRL_ENABLE - | TIMER_CTRL_CASCADE; - - // Timer 0 used to stop sample playing. - TIMER_DATA_0 = 0xFFFF - CPU_FREQUENCY / chan->sampling_freq; - TIMER_CTRL_0 = TIMER_CTRL_ENABLE; -} - -void -irs_stop_sample(void) { - if (apu.chan_0.loop) { - reset_sound(&apu.chan_0); - } else { - TIMER_CTRL_0 = 0; - DMA_CTRL(1) = 0; - } -} - -void -init_sound(AudioChannel *chan) { - chan->samples = voiceraw; - chan->n_samples = LEN(voiceraw); - chan->sampling_freq = 44100; - chan->loop = true; -} - int main(void) { // Initialize filesystem. fs_init(); @@ -431,39 +394,34 @@ int main(void) { txt_init(1, TEXT_LAYER); txt_position(0,0); - txt_printf("VOICE LOADED: %lu\n", LEN(voiceraw)); - - // Enable sound. - SOUND_STATUS = SOUND_ENABLE; - - init_sound(&apu.chan_0); - reset_sound(&apu.chan_0); + // init_sound(&apu.chan_0); + // reset_sound(&apu.chan_0); // Main loop. - // int frame_counter = 0; - // evaluxn(&u, 0x0100); - // u32 flip_cycles = 0; + int frame_counter = 0; + evaluxn(&u, 0x0100); + u32 flip_cycles = 0; while(true) { bios_vblank_wait(); - // profile_start(); - // handle_input(&u); - // u32 input_cycles = profile_stop(); - // profile_start(); - // evaluxn(&u, mempeek16(devscreen->dat, 0)); - // u32 eval_cycles = profile_stop(); - // txt_position(0, 8); + profile_start(); + handle_input(&u); + u32 input_cycles = profile_stop(); + profile_start(); + evaluxn(&u, mempeek16(devscreen->dat, 0)); + u32 eval_cycles = profile_stop(); + txt_position(0, 8); // txt_printf("INPUT: %lu \n", input_cycles); // txt_printf("EVAL: %lu \n", eval_cycles); // txt_printf("FLIP: %lu \n", flip_cycles); - // profile_start(); + profile_start(); flipbuf(&ppu); - // flip_cycles = profile_stop(); - // frame_counter++; - poll_keys(); - if (key_tap(KEY_B)) { - txt_printf("TAP B\n"); - reset_sound(&apu.chan_0); - } + flip_cycles = profile_stop(); + frame_counter++; + // if (frame_counter == 120) { + // init_sound(&apu.chan_0); + // reset_sound(&apu.chan_0); + // frame_counter = 0; + // } } return 0; diff --git a/src/uxn/devices/apu.c b/src/uxn/devices/apu.c new file mode 100644 index 0000000..fbe7c3d --- /dev/null +++ b/src/uxn/devices/apu.c @@ -0,0 +1,241 @@ +#include "samples.c" + +#define SAMPLE_FREQUENCY 44100 +#define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) +#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) + +typedef struct AudioChannel { + u8 *samples; + u16 n_samples; + u16 position; + u16 sampling_freq; + u8 pitch; + bool loop; + // TODO: u16 adsr; // attack, decay, sustain, release + // TODO: u8 pitch; // Bit 8 is the "loop" bit + // TODO: u8 volume; // VOL_LEFT | (VOL_RIGHT << 4) + // u8 *addr; + // u32 count, advance, period, age, a, d, s, r; + // s8 volume[2]; + // u8 pitch, repeat; +} AudioChannel; + +typedef struct APU { + AudioChannel chan_0; + // u32 *samples_1; + // u32 *samples_2; + // u32 *samples_3; +} APU; + +static APU apu; + + +static u16 pitch_table[] = { + 12173, + 11490, + 10845, + 10237, + 9662, + 9120, + 8608, + 8125, + 7669, + 7238, + 6832, + 6448, + 6086, + 5745, + 5422, + 5118, + 4831, + 4560, + 4304, + 4062, + 3834, + 3619, + 3416, + 3224, + 3043, + 2872, + 2711, + 2559, + 2415, + 2280, + 2152, + 2031, + 1917, + 1809, + 1708, + 1612, + 1521, + 1436, + 1355, + 1279, + 1207, + 1140, + 1076, + 1015, + 958, + 904, + 854, + 806, + 760, + 718, + 677, + 639, + 603, + 570, + 538, + 507, + 479, + 452, + 427, + 403, + 380, + 359, + 338, + 319, + 301, + 285, + 269, + 253, + 239, + 226, + 213, + 201, + 190, + 179, + 169, + 159, + 150, + 142, + 134, + 126, + 119, + 113, + 106, + 100, + 95, + 89, + 84, + 79, + 75, + 71, + 67, + 63, + 59, + 56, + 53, + 50, + 47, + 44, + 42, + 39, + 37, + 35, + 33, + 31, + 29, + 28, + 26, + 25, + 23, + 22, + 21, + 19, + 18, + 17, + 16, + 15, + 14, + 14, + 13, + 12, + 0, +}; + +void +reset_sound(AudioChannel *chan) { + TIMER_CTRL_0 = 0; + TIMER_CTRL_1 = 0; + DMA_CTRL(1) = 0; + if (chan->pitch >= 108 || chan->n_samples == 0) { + return; + } + + // Set max volume, left-right sound, fifo reset and use timer 0 for + // DirectSound A. + SOUND_DSOUND_MASTER = SOUND_DSOUND_RATIO_A + | SOUND_DSOUND_LEFT_A + | SOUND_DSOUND_RIGHT_A + | SOUND_DSOUND_RESET_A; + + // Prepare DMA copy. + dma_transfer_copy(SOUND_FIFO_A, chan->samples, 1, 1, + DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); + + // Timer 1 used to stop playing samples. + u32 sample_duration = chan->n_samples; + TIMER_DATA_1 = 0xFFFF - sample_duration; + TIMER_CTRL_1 = TIMER_CTRL_IRQ + | TIMER_CTRL_ENABLE + | TIMER_CTRL_CASCADE; + + // Timer 0 used to stop sample playing. + // TIMER_DATA_0 = 0xFFFF - sound_freq[chan->pitch]; + // TIMER_DATA_0 = 0xFFFF - CPU_FREQUENCY / chan->sampling_freq / chan->pitch; + TIMER_DATA_0 = 0xFFFF - pitch_table[chan->pitch]; + TIMER_CTRL_0 = TIMER_CTRL_ENABLE; +} + +u8 square_wave[] = { + 0x00 + 0x80, 0xFF + 0x80 +}; +#include "text.h" + + +void +init_sound(AudioChannel *chan) { + // Enable sound. + SOUND_STATUS = SOUND_ENABLE; + + // chan->samples = &square_wave; + // chan->n_samples = 2; + // chan->samples = &samples; + // chan->n_samples = LEN(samples); + // chan->pitch++; + // chan->pitch = chan->pitch == LEN(pitch_table) - 1 ? 0 : chan->pitch + 1; + // chan->loop = true; + // chan->sampling_freq = pitch_table[chan->pitch]; + // txt_printf("RATE: %lu", pitch_table[chan->pitch]) +} + +void +irs_stop_sample(void) { + if (apu.chan_0.loop) { + reset_sound(&apu.chan_0); + } else { + TIMER_CTRL_0 = 0; + DMA_CTRL(1) = 0; + } +} + +// void +// apu_start(Apu *c, u16 adsr, u8 pitch) { +// // if(pitch < 108 && c->len) +// // c->advance = advances[pitch % 12] >> (8 - pitch / 12); +// // else { +// // c->advance = 0; +// // return; +// // } +// // c->a = ADSR_STEP * (adsr >> 12); +// // c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a; +// // c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d; +// // c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s; +// // c->age = 0; +// // c->i = 0; +// // if(c->len <= 0x100) /* single cycle mode */ +// // c->period = NOTE_PERIOD * 337 / 2 / c->len; +// // else /* sample repeat mode */ +// // c->period = NOTE_PERIOD; +// } -- cgit v1.2.1