#include "samples.c" #define SAMPLE_FREQUENCY 44100 #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) // FIXME: We have two structs for now, needs fixing. 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, 2, }; 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_DST_FIXED | 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; } s8 square_wave[] = { (s8)0x00 + (s8)0x80, (s8)0xFF + (s8)0x80 }; #include "text.h" // // REG_TM0D frequency buffer size // | | | // V V V // // Timer = 62610 = 65536 - (16777216 / 5734), buf = 96 // Timer = 63940 = 65536 - (16777216 / 10512), buf = 176 // Timer = 64282 = 65536 - (16777216 / 13379), buf = 224 // Timer = 64612 = 65536 - (16777216 / 18157), buf = 304 // Timer = 64738 = 65536 - (16777216 / 21024), buf = 352 // Timer = 64909 = 65536 - (16777216 / 26758), buf = 448 // Timer = 65004 = 65536 - (16777216 / 31536), buf = 528 // Timer = 65073 = 65536 - (16777216 / 36314), buf = 608 // Timer = 65118 = 65536 - (16777216 / 40137), buf = 672 // Timer = 65137 = 65536 - (16777216 / 42048), buf = 704 // // Source: https://deku.gbadev.org/program/sound1.html #define AUDIO_FREQ 18157 #define AUDIO_BUF_LEN 304 #define AUDIO_TIMER 64612 typedef struct Audio { s8 mix_buffer[AUDIO_BUF_LEN * 2]; s8 *current_buffer; u8 active_buffer; } Audio; typedef struct Channel { // Pointer to the raw data in the ROM. s8 *data; // Current position in the data (20.12 fixed-point). u32 pos; // Increment (20.12 fixed-point). u32 inc; // Volume (0-64, 1.6 fixed-point). u32 vol; // Sound length (20.12 fixed-point). u32 length; // Length of looped portion (20.12 fixed-point, 0 to disable looping). u32 loop_length; } Channel; static Audio audio; #define POLYPHONY 4 static Channel channels[POLYPHONY]; void init_sound(void) { // Initialize audio buffers/channels. audio = (Audio){0}; for (size_t i = 0; i < POLYPHONY; ++i) { channels[i] = (Channel){0}; } // DEBUG: testing channel 0 with square wave channels[0].data = samples; channels[0].inc = (8363 << 12) / AUDIO_FREQ; channels[0].length = (LEN(samples) - 1) << 12; channels[0].pos = 0; channels[0].vol = 64; channels[0].loop_length = channels[0].length; // Enable the sound chip. SOUND_STATUS = SOUND_ENABLE; // 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; // TODO: No pitch selection yet. TIMER_DATA_0 = AUDIO_TIMER; TIMER_CTRL_0 = TIMER_CTRL_ENABLE; } void sound_vsync() { // buffer 1 just got over if(audio.active_buffer == 1) { // Start playing buffer 0 dma_transfer_copy(SOUND_FIFO_A, audio.mix_buffer, 1, 1, DMA_DST_FIXED | DMA_CHUNK_32 | DMA_REFRESH | DMA_REPEAT | DMA_ENABLE); // Set the current buffer pointer to the start of buffer 1 audio.current_buffer = audio.mix_buffer + AUDIO_BUF_LEN; audio.active_buffer = 0; } else { // buffer 0 just got over // DMA points to buffer 1 already, so don't bother stopping and resetting it // Set the current buffer pointer to the start of buffer 0 audio.current_buffer = audio.mix_buffer; audio.active_buffer = 1; } } void sound_mix() { // Initialize and clear mix_buffer. s16 mix_buffer[AUDIO_BUF_LEN]; u32 fill = 0; dma_fill(mix_buffer, fill, sizeof(mix_buffer), 3); // Mix channels into the temporary buffer. for (size_t j = 0; j < POLYPHONY; ++j) { Channel *chan = &channels[j]; // Check if channel is active. if (chan->data == NULL) { continue; } for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { // Remember we are using fixed point values. mix_buffer[i] += chan->data[chan->pos >> 12] * chan->vol; chan->pos += chan->inc; if (chan->pos >= chan->length) { // If looping is not active disable the channel. if (chan->loop_length == 0) { chan->data = NULL; break; } // Loop the sample. while (chan->pos >= chan->length) { chan->pos -= chan->loop_length; } } } } // Downsample and copy to the playing buffer. for (size_t i = 0; i < AUDIO_BUF_LEN; ++i) { // >> 8 to divide off the volume, >> 2 to divide by 4 channels to // prevent overflow. audio.current_buffer[i] = mix_buffer[i] >> 8; } } 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; // }