// Calculated as (44100 << 12) / 18157 for C4 static u32 pitch_table_44100[] = { 310, 329, 348, 369, 391, 414, 439, 465, 493, 522, 553, 586, 621, 658, 697, 739, 783, 829, 879, 931, 987, 1045, 1107, 1173, 1243, 1317, 1395, 1478, 1566, 1659, 1758, 1863, 1974, 2091, 2215, 2347, 2487, 2634, 2791, 2957, 3133, 3319, 3517, 3726, 3948, 4182, 4431, 4695, 4974, 5269, 5583, 5915, 6267, 6639, 7034, 7452, 7896, 8365, 8863, 9390, 9948, 10539, 11166, 11830, 12534, 13279, 14069, 14905, 15792, 16731, 17726, 18780, 19896, 21079, 22333, 23661, 25068, 26559, 28138, 29811, 31584, 33462, 35452, 37560, 39793, 42159, 44666, 47322, 50136, 53118, 56276, 59623, 63168, 66924, 70904, 75120, 79587, 84319, 89333, 94645, 100273, 106236, 112553, 119246, 126337, 133849, 141808, 150241, 159174, 168639, 178667, 189291, 200547, 212472, 225107, 238492, 252674, 267699, 283617, 300482, }; // // 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 AudioChannel { // 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; // TODO: this should be different? u8 pitch; } AudioChannel; static Audio audio; #define POLYPHONY 4 static AudioChannel channels[POLYPHONY]; void init_sound(void) { // Initialize audio buffers/channels. audio = (Audio){0}; for (size_t i = 0; i < POLYPHONY; ++i) { channels[i] = (AudioChannel){0}; } // DEBUG: testing channel 0 with square wave // channels[0].data = samples; // channels[0].length = (LEN(samples) - 1) << 12; // channels[0].pos = 0; // channels[0].inc = (44100 << 12) / AUDIO_FREQ; // channels[0].vol = 32; // channels[0].loop_length = channels[0].length; // channels[0].loop_length = 0; // 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) { AudioChannel *ch = &channels[j]; // Check if channel is active. if (ch->data == NULL || ch->pitch >= 108) { continue; } if (ch->pos + ch->inc * AUDIO_BUF_LEN >= ch->length) { // Sample is going to finish, need to consider this for looping or // stopping. for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { // Remember we are using fixed point values. mix_buffer[i] += (0x80 + (u8)ch->data[ch->pos >> 12]) * ch->vol; ch->pos += ch->inc; if (ch->pos >= ch->length) { // If looping is not active disable the channel. if (ch->loop_length == 0) { ch->data = NULL; break; } // Loop the sample. while (ch->pos >= ch->length) { ch->pos -= ch->loop_length; } } } } else { // Sample still have room to go, no need to check for looping or // end of sample. for(size_t i = 0; i < AUDIO_BUF_LEN; i++) { mix_buffer[i] += (0x80 + (u8)ch->data[ch->pos>>12]) * ch->vol; ch->pos += ch->inc; } } } // Downsample and copy to the playing buffer. for (size_t i = 0; i < AUDIO_BUF_LEN; ++i) { // >> 6 to divide off the volume, >> 2 to divide by 4 channels to // prevent overflow. audio.current_buffer[i] = mix_buffer[i] >> 8; } }