From d96f6da677bc319f7868e832f09948c94ee6a148 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Mon, 3 Apr 2023 12:29:48 +0200 Subject: Add noise channel with note support --- src/gba/gba.h | 14 +- src/sequencer.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 395 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/gba/gba.h b/src/gba/gba.h index cdb3b31..b02d745 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -558,7 +558,7 @@ sound_volume(SoundChannel channels, u8 volume) { #define SOUND_SWEEP_DIR(N) ((N) << 0x3) #define SOUND_SWEEP_TIME(N) ((N) << 0x4) -// DMG frequency bits (Square/Wave). +// DMG frequency bits (Square/Wave/NOISE). #define SOUND_FREQ_TIMED (1 << 0xE) #define SOUND_FREQ_RESET (1 << 0xF) @@ -585,6 +585,18 @@ typedef u8 WaveBank[32]; #define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90)) +// DMG noise control bits. +#define SOUND_NOISE_LENGTH(N) (((N) & 0x3F) << 0) +#define SOUND_NOISE_ENV_TIME(N) (((N) & 0x07) << 0x8) +#define SOUND_NOISE_ENV_DIR(N) (((N) & 0x01) << 0xB) +#define SOUND_NOISE_ENV_VOL(N) (((N) & 0x0F) << 0xC) + +// DMG noise freq bits. +#define SOUND_NOISE_DIV_FREQ(N) (((N) & 0x07) << 0) +#define SOUND_NOISE_COUNTER_STAGE(N) (((N) & 0x01) << 0x3) +#define SOUND_NOISE_PRESTEP_FREQ(N) (((N) & 0x0F) << 0x4) +#define SOUND_NOISE_TIMED_MODE(N) (((N) & 0x01) << 0xe) + typedef enum { NOTE_C_2 , NOTE_C_SHARP_2 , NOTE_D_2 , NOTE_D_SHARP_2 , NOTE_E_2 , NOTE_F_2 , NOTE_F_SHARP_2 , NOTE_G_2 , diff --git a/src/sequencer.c b/src/sequencer.c index 856d579..8c3f89a 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -1,6 +1,15 @@ #include "rng.c" #include "text.h" +// TODO +// - Preview sound keys +// - Copy paste trigs/notes/params +// - Different banks for storing patterns +// - Buttons on the left side for selecting different patterns +// - Pattern chaining or song mode +// - Allow control BPM +// - Finish noise channel + // // Color indexes. // @@ -150,7 +159,7 @@ static int bpm = 115; static int step_counter = 0; int trig_selection_loc = 0; int param_selection_loc = 0; -int channel_selection_loc = 0; +int channel_selection_loc = 3; typedef struct TriggerNote { bool active; @@ -186,6 +195,11 @@ typedef struct ChannelWave { ChannelWaveParams params[16]; } ChannelWave; +typedef struct ChannelNoise { + bool active; + TriggerNote notes[16]; +} ChannelNoise; + static ChannelSquare ch1 = { .notes = { {true, NOTE_C_4}, @@ -223,7 +237,7 @@ static ChannelSquare ch1 = { {8, 4, 0, 2, 0, 0, 0}, {8, 4, 0, 2, 0, 0, 0}, }, - .active = true, + .active = false, }; static ChannelSquare ch2 = { @@ -263,7 +277,7 @@ static ChannelSquare ch2 = { {8, 4, 0, 2, 0, 0, 0}, {8, 4, 0, 2, 0, 0, 0}, }, - .active = true, + .active = false, }; static ChannelWave ch3 = { @@ -303,6 +317,29 @@ static ChannelWave ch3 = { {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, }, + .active = false, +}; + +// TODO: Add default noise parameters data. +static ChannelNoise ch4 = { + .notes = { + {true, 0}, + {false, NOTE_D_4}, + {false, NOTE_E_4}, + {false, NOTE_F_4}, + {false, NOTE_G_4}, + {false, NOTE_A_4}, + {false, NOTE_B_4}, + {false, NOTE_C_5}, + {false, NOTE_C_4}, + {false, NOTE_C_4}, + {false, NOTE_C_4}, + {false, NOTE_C_4}, + {false, NOTE_C_4}, + {false, NOTE_C_4}, + {false, NOTE_C_4}, + {false, NOTE_C_4}, + }, .active = true, }; @@ -313,7 +350,7 @@ static ChannelWave ch3 = { #define TRIG_W 15 #define TRIG_H 24 #define TRIG_START_X 64 -#define TRIG_START_Y 97 +#define TRIG_START_Y 92 #define TRIG_OFFSET_X (TRIG_W + 3) #define TRIG_OFFSET_Y (TRIG_H + 7) @@ -341,6 +378,9 @@ draw_trigger(size_t chan, size_t i) { case 2: { trig = ch3.notes[i]; } break; + case 3: { + trig = ch4.notes[i]; + } break; } if (trig.active) { size_t offset_x = TRIG_OFFSET_X * (i % 8); @@ -388,7 +428,7 @@ draw_triggers(void) { #define CHAN_W 19 #define CHAN_H 8 #define CHAN_START_X 35 -#define CHAN_START_Y 97 +#define CHAN_START_Y 92 #define CHAN_OFFSET_Y 12 void @@ -409,6 +449,9 @@ draw_channels(void) { case 2: { active = ch3.active; } break; + case 3: { + active = ch4.active; + } break; } u8 clr = active ? COL_FG : COL_GREY; size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; @@ -431,7 +474,7 @@ draw_channel_cursor(size_t i, u8 clr) { #define PIANO_W 170 #define PIANO_H 20 #define PIANO_START_X 35 -#define PIANO_START_Y 72 +#define PIANO_START_Y 65 #define PIANO_NOTE_W 2 void @@ -571,7 +614,7 @@ draw_piano(void) { #define PARAMS_W 170 #define PARAMS_H 64 #define PARAMS_START_X 35 -#define PARAMS_START_Y 8 +#define PARAMS_START_Y 1 void draw_params_cursor_wave(size_t i, u8 clr) { @@ -628,6 +671,61 @@ draw_params_cursor_wave(size_t i, u8 clr) { draw_line(x, y, x + cursor_length, y, clr); } +void +draw_params_cursor_noise(size_t i, u8 clr) { + // u8 x_positions[] = { + // // 32 half bytes (Wave A). + // 0, 4, 8, 12, 16, 20, 24, 28, + // 34, 38, 42, 46, 50, 54, 58, 62, + // 0, 4, 8, 12, 16, 20, 24, 28, + // 34, 38, 42, 46, 50, 54, 58, 62, + // // 32 half bytes (Wave B). + // 70, 74, 78, 82, 86, 90, 94, 98, + // 104, 108, 112, 116, 120, 124, 128, 132, + // 70, 74, 78, 82, 86, 90, 94, 98, + // 104, 108, 112, 116, 120, 124, 128, 132, + // // Default wave A. + // 1, 18, 35, 52, + // // Default wave B. + // 71, 88, 105, 122, + // // Mode selection. + // 141, + // // Volume selection. + // 141, + // }; + // u8 y_positions[] = { + // // 32 half bytes (Wave A) + // 0, 0, 0, 0, 0, 0, 0, 0, + // 0, 0, 0, 0, 0, 0, 0, 0, + // 8, 8, 8, 8, 8, 8, 8, 8, + // 8, 8, 8, 8, 8, 8, 8, 8, + // // 32 half bytes (Wave B) + // 0, 0, 0, 0, 0, 0, 0, 0, + // 0, 0, 0, 0, 0, 0, 0, 0, + // 8, 8, 8, 8, 8, 8, 8, 8, + // 8, 8, 8, 8, 8, 8, 8, 8, + // // Default wave A. + // 20, 20, 20, 20, + // // Default wave B. + // 20, 20, 20, 20, + // // Mode selection. + // 20, + // // Volume selection. + // 0, + // }; + // size_t cursor_length = 0; + // if (i < 64) { + // cursor_length = 4; + // } else if (i < 72) { + // cursor_length = 13; + // } else { + // cursor_length = 30; + // } + // size_t x = PARAMS_START_X + x_positions[i] - 1; + // size_t y = PARAMS_START_Y + PARAMS_H - 23 + y_positions[i]; + // draw_line(x, y, x + cursor_length, y, clr); +} + void draw_params_cursor_square(size_t i, u8 clr, bool sweep) { size_t x_offset = sweep ? 0 : 30; @@ -668,6 +766,7 @@ draw_params_cursor(size_t i, u8 clr) { draw_params_cursor_wave(i, clr); } break; case 3: { + draw_params_cursor_noise(i, clr); } break; } } @@ -814,7 +913,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) { // Shape drawing. { size_t x = PARAMS_START_X + x_offset; - size_t y = PARAMS_START_Y + PARAMS_H - 43; + size_t y = PARAMS_START_Y + PARAMS_H - 44; size_t x0 = x + 2; size_t x1 = x0; @@ -904,14 +1003,14 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) { // Bounding box. { size_t x0 = PARAMS_START_X + 31 + x_offset; - size_t y0 = PARAMS_START_Y + PARAMS_H - 46; + size_t y0 = PARAMS_START_Y + PARAMS_H - 47; size_t x1 = x0 + 79; size_t y1 = y0 + 21; draw_rect(x0, y0, x1, y1, COL_CYAN); } size_t x = PARAMS_START_X + 42 + x_offset; - size_t y = PARAMS_START_Y + PARAMS_H - 43; + size_t y = PARAMS_START_Y + PARAMS_H - 44; size_t x0 = x; size_t y0 = y + 15 - params->env_volume; size_t x1 = x + 8 * params->env_time; @@ -1158,7 +1257,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) { // Labels. { size_t x = PARAMS_START_X + x_offset; - size_t y = PARAMS_START_Y + PARAMS_H - 43; + size_t y = PARAMS_START_Y + PARAMS_H - 45; txt_drawf_small("shape", x + 1, y - 12, 4, COL_FG); txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG); if (sweep) { @@ -1272,6 +1371,83 @@ irq_timer(void) { SOUND_WAVE_CTRL = 0; SOUND_WAVE_FREQ = 0; } + if (ch4.active) { + TriggerNote *trig = &ch4.notes[step_counter]; + // ChannelNoiseParams *params = &ch4.params[step_counter]; + if (trig->active) { + // switch (params->wave_mode) { + // case 0: { + // SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); + // memcpy32(SOUND_WAVE_RAM, params->wave_a, 16); + // SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) + // | SOUND_WAVE_BANK_SELECT(0); + // } break; + // case 1: { + // SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); + // memcpy32(SOUND_WAVE_RAM, params->wave_b, 16); + // SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) + // | SOUND_WAVE_BANK_SELECT(1); + // } break; + // case 2: { + // SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); + // memcpy32(SOUND_WAVE_RAM, params->wave_b, 16); + // SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); + // memcpy32(SOUND_WAVE_RAM, params->wave_a, 16); + // SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) + // | SOUND_WAVE_BANK_SELECT(0); + // } break; + // } + // SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; + + // switch (params->wave_volume) { + // case 0: { + // SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; + // } break; + // case 1: { + // SOUND_WAVE_CTRL = SOUND_WAVE_VOL_25; + // } break; + // case 2: { + // SOUND_WAVE_CTRL = SOUND_WAVE_VOL_50; + // } break; + // case 3: { + // SOUND_WAVE_CTRL = SOUND_WAVE_VOL_75; + // } break; + // case 4: { + // SOUND_WAVE_CTRL = SOUND_WAVE_VOL_100; + // } break; + // } + static const u8 div_freq[] = { + 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, + 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, + 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, + 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, + 7, 6, 5, 4, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + static const u8 pre_freq[] = { + 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, + 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, + 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + SOUND_NOISE_CTRL = SOUND_NOISE_ENV_VOL(0xF) + | SOUND_NOISE_ENV_TIME(0x2) + | SOUND_NOISE_ENV_DIR(0) + | SOUND_NOISE_LENGTH(50); + SOUND_NOISE_FREQ = SOUND_FREQ_RESET + | SOUND_NOISE_PRESTEP_FREQ(pre_freq[trig->note]) + | SOUND_NOISE_DIV_FREQ(div_freq[trig->note]) + | SOUND_NOISE_COUNTER_STAGE(0) + | SOUND_NOISE_TIMED_MODE(0); + } else { + SOUND_NOISE_FREQ = 0; + } + } else { + SOUND_NOISE_CTRL = 0; + SOUND_NOISE_FREQ = 0; + } step_counter = (step_counter + 1) % 16; } @@ -1301,6 +1477,9 @@ get_current_trig(void) { case 2: { return &ch3.notes[trig_selection_loc]; } break; + case 3: { + return &ch4.notes[trig_selection_loc]; + } break; } return NULL; } @@ -1310,13 +1489,14 @@ get_current_trig(void) { // selection. void (*input_handler)(void); -#define SEQ_N_CHANNELS 3 +#define SEQ_N_CHANNELS 4 void handle_trigger_selection(void); void handle_channel_selection(void); void handle_param_selection_sq1(void); void handle_param_selection_sq2(void); void handle_param_selection_wave(void); +void handle_param_selection_noise(void); void handle_channel_selection(void) { @@ -1331,6 +1511,9 @@ handle_channel_selection(void) { case 2: { ch3.active ^= 1; } break; + case 3: { + ch4.active ^= 1; + } break; } draw_channels(); } @@ -1682,6 +1865,181 @@ handle_param_selection_wave(void) { } } +void +handle_param_selection_noise(void) { + // Go back to trigger selection. + // if (key_tap(KEY_A)) { + // draw_params_cursor(param_selection_loc, COL_BG); + // input_handler = handle_trigger_selection; + // draw_trig_cursor(trig_selection_loc, COL_CURSOR); + // } + + // // Cursor movement. + // if (key_tap(KEY_LEFT) || key_tap(KEY_RIGHT)) { + // int inc = 0; + // int loc = param_selection_loc; + // if (key_tap(KEY_RIGHT)) { + // if (loc == 15 || loc == 31) { + // inc = 17; + // } else if (loc == 47) { + // inc = 26; + // } else if (loc == 63) { + // inc = 9; + // } else if (loc != 47 && loc != 63 && loc < 72) { + // inc = 1; + // } + // } else { + // if (loc == 32 || loc == 48) { + // inc = -17; + // } else if (loc == 73) { + // inc = -26; + // } else if (loc != 16 && loc != 64) { + // inc = -1; + // } + // } + // draw_params_cursor(param_selection_loc, COL_BG); + // param_selection_loc = CLAMP(loc + inc, 0, 73); + // draw_params_cursor(param_selection_loc, COL_CURSOR); + // } + // if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { + // int inc = 0; + // int loc = param_selection_loc; + // if (key_tap(KEY_UP)) { + // if ((loc >= 16 && loc < 32) || (loc >= 48 && loc < 64)) { + // inc = -16; + // } else if (loc == 64) { + // inc = -48; + // } else if (loc == 65) { + // inc = -45; + // } else if (loc == 66) { + // inc = -42; + // } else if (loc == 67) { + // inc = -39; + // } else if (loc == 68) { + // inc = -20; + // } else if (loc == 69) { + // inc = -17; + // } else if (loc == 70) { + // inc = -14; + // } else if (loc == 71) { + // inc = -11; + // } else if (loc == 72) { + // inc = 1; + // } else if (loc == 73) { + // inc = -1; + // } + // } else { + // if (loc < 16 || (loc >= 32 && loc < 48)) { + // inc = 16; + // } else if (loc >= 16 && loc <= 19) { + // inc = 48 - (loc - 16); + // } else if (loc >= 20 && loc <= 23) { + // inc = 45 - (loc - 20); + // } else if (loc >= 24 && loc <= 27) { + // inc = 42 - (loc - 24); + // } else if (loc >= 28 && loc <= 31) { + // inc = 39 - (loc - 28); + // } else if (loc >= 48 && loc <= 51) { + // inc = 20 - (loc - 48); + // } else if (loc >= 52 && loc <= 55) { + // inc = 17 - (loc - 52); + // } else if (loc >= 56 && loc <= 59) { + // inc = 14 - (loc - 56); + // } else if (loc >= 60 && loc <= 63) { + // inc = 11 - (loc - 60); + // } else if (loc == 72) { + // inc = 1; + // } else if (loc == 73) { + // inc = -1; + // } + // } + // draw_params_cursor(param_selection_loc, COL_BG); + // param_selection_loc = CLAMP(loc + inc, 0, 73); + // draw_params_cursor(param_selection_loc, COL_CURSOR); + // } + + // // Adjust parameter. + // if (key_tap(KEY_R) || key_tap(KEY_L)) { + // int odd = param_selection_loc & 0x1; + // int inc; + // if (key_tap(KEY_R)) { + // inc = 1; + // } else { + // inc = -1; + // } + + // // Wave: AA BB CC DD ... + // // ^^ + // // |`- odd + // // `-- even + // if (param_selection_loc < 32) { + // // Draw on wave a. + // u8 byte_number = param_selection_loc / 2; + // u8 *byte = &ch3.params[trig_selection_loc].wave_a; + // byte += byte_number; + // if (odd) { + // *byte = (~0xF & *byte) | ((*byte + inc) & 0xF); + // } else { + // *byte = (0xF & *byte) | (((*byte >> 4) + inc) & 0xF) << 4; + // } + // } else if (param_selection_loc < 64){ + // // Draw on wave b. + // u8 byte_number = (param_selection_loc - 32) / 2; + // u8 *byte = &ch3.params[trig_selection_loc].wave_b; + // byte += byte_number; + // if (odd) { + // *byte = (~0xF & *byte) | ((*byte + inc) & 0xF); + // } else { + // *byte = (0xF & *byte) | (((*byte >> 4) + inc) & 0xF) << 4; + // } + // } else if (param_selection_loc < 72) { + // // Copy default waves. + // u32 *wave_a = &ch3.params[trig_selection_loc].wave_a; + // u32 *wave_b = &ch3.params[trig_selection_loc].wave_b; + // switch (param_selection_loc) { + // case 64: { + // memcpy32(wave_a, sine_wave, 16); + // } break; + // case 65: { + // memcpy32(wave_a, saw_wave, 16); + // } break; + // case 66: { + // memcpy32(wave_a, square_wave, 16); + // } break; + // case 67: { + // u32 rand_wave[4] = { + // rng32(), rng32(), rng32(), rng32(), + // }; + // memcpy32(wave_a, rand_wave, 16); + // } break; + // case 68: { + // memcpy32(wave_b, sine_wave, 16); + // } break; + // case 69: { + // memcpy32(wave_b, saw_wave, 16); + // } break; + // case 70: { + // memcpy32(wave_b, square_wave, 16); + // } break; + // case 71: { + // u32 rand_wave[4] = { + // rng32(), rng32(), rng32(), rng32(), + // }; + // memcpy32(wave_b, rand_wave, 16); + // } break; + // } + // } else if (param_selection_loc == 72) { + // u8 *wave_mode = &ch3.params[trig_selection_loc].wave_mode; + // *wave_mode = CLAMP(*wave_mode + inc, 0, 2); + // } else if (param_selection_loc == 73) { + // u8 *wave_volume = &ch3.params[trig_selection_loc].wave_volume; + // *wave_volume = CLAMP(*wave_volume + inc, 0, 4); + // } + // draw_parameters(); + // draw_params_cursor(param_selection_loc, COL_CURSOR); + // } +} + void handle_trigger_selection(void) { TriggerNote *trig = get_current_trig(); @@ -1758,6 +2116,9 @@ handle_trigger_selection(void) { case 2: { input_handler = handle_param_selection_wave; } break; + case 3: { + input_handler = handle_param_selection_noise; + } break; } draw_params_cursor(param_selection_loc, COL_CURSOR); draw_trig_cursor(trig_selection_loc, COL_CURRENT_TRIG); @@ -1779,6 +2140,7 @@ handle_sequencer_input(void) { SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; + SOUND_NOISE_CTRL = 0; } } else if (key_tap(KEY_SELECT)) { // Play/pause. @@ -1786,6 +2148,7 @@ handle_sequencer_input(void) { SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; + SOUND_NOISE_CTRL = 0; } } @@ -1808,6 +2171,9 @@ sequencer_init(void) { TriggerNote *trig = get_current_trig(); draw_note(trig->note, COL_NOTE_PRESSED); + // Draw screen border frame. + draw_rect(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, 1); + // Initialize input handler. input_handler = handle_trigger_selection; draw_trig_cursor(trig_selection_loc, COL_CURSOR); @@ -1818,6 +2184,9 @@ sequencer_init(void) { SOUND_STATUS = SOUND_ENABLE; SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 | SOUND_SQUARE2 - | SOUND_WAVE, 3); + | SOUND_WAVE + | SOUND_NOISE, 3); SOUND_DSOUND_MASTER = SOUND_DMG25; + // SOUND_NOISE_CTRL = SOUND_NOISE_ENV_VOL(0x8) | SOUND_NOISE_ENV_TIME(0) | SOUND_NOISE_ENV_DIR(1) | 0x4; + // SOUND_NOISE_FREQ = SOUND_FREQ_RESET | SOUND_NOISE_PRESTEP_FREQ(0x7) | SOUND_NOISE_DIV_FREQ(2) | SOUND_NOISE_COUNTER_STAGE(1); } -- cgit v1.2.1