From 76791d881a007074ec34f229d01feaef5cf03794 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Wed, 9 Jun 2021 12:59:02 +0200 Subject: Split triggers in separate channels --- src/sequencer.c | 321 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 220 insertions(+), 101 deletions(-) diff --git a/src/sequencer.c b/src/sequencer.c index 526680a..fa9b674 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -141,10 +141,12 @@ int trig_selection_loc = 0; int param_selection_loc = 64; int channel_selection_loc = 0; -// TODO: Split into individual trigger types. -typedef struct SeqTrigger { - bool trigger; +typedef struct TriggerNote { + bool active; Note note; +} TriggerNote; + +typedef struct ChannelSquareParams { u8 env_volume; u8 env_time; u8 env_direction; @@ -152,73 +154,146 @@ typedef struct SeqTrigger { u8 sweep_number; u8 sweep_time; u8 sweep_direction; +} ChannelSquareParams; + +typedef struct ChannelWaveParams { u8 wave_volume; u8 wave_mode; u8 wave_a[16]; u8 wave_b[16]; -} SeqTrigger; - -static SeqTrigger sequences[3][16] = { - // Synth 1. - { - {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {true, NOTE_D_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_E_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_F_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {true, NOTE_G_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {true, NOTE_A_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_B_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, +} ChannelWaveParams; + +typedef struct ChannelSquare { + bool active; + TriggerNote notes[16]; + ChannelSquareParams params[16]; +} ChannelSquare; + +typedef struct ChannelWave { + bool active; + TriggerNote notes[16]; + ChannelWaveParams params[16]; +} ChannelWave; + +static ChannelSquare ch1 = { + .notes = { + {true, NOTE_C_4}, + {true, NOTE_D_4}, + {true, NOTE_E_4}, + {true, NOTE_F_4}, + {true, NOTE_G_4}, + {true, NOTE_A_4}, + {true, NOTE_B_4}, + {true, NOTE_C_5}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + }, + .params = { + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, }, - // Synth 2. - { - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_B_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_A_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_G_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_F_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_E_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_D_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, - {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, + .active = true, +}; + +static ChannelSquare ch2 = { + .notes = { + {true, NOTE_C_4}, + {true, NOTE_D_4}, + {true, NOTE_E_4}, + {true, NOTE_F_4}, + {true, NOTE_G_4}, + {true, NOTE_A_4}, + {true, NOTE_B_4}, + {true, NOTE_C_5}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, }, - // Synth 3. - { - {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, - {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0}, {0}}, + .params = { + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, + {8, 4, 0, 2, 0, 0, 0}, }, + .active = true, }; -static bool active_channels[4] = {true, true, true, true}; +static ChannelWave ch3 = { + .notes = { + {true, NOTE_C_4}, + {true, NOTE_D_4}, + {true, NOTE_E_4}, + {true, NOTE_F_4}, + {true, NOTE_G_4}, + {true, NOTE_A_4}, + {true, NOTE_B_4}, + {true, NOTE_C_5}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + {true, NOTE_C_4}, + }, + .params = { + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, + }, + .active = true, +}; // // Trigger render functions. @@ -244,13 +319,25 @@ clear_trigger(size_t i) { void draw_trigger(size_t chan, size_t i) { - if (sequences[chan][i].trigger) { + TriggerNote trig = {0}; + switch (chan) { + case 0: { + trig = ch1.notes[i]; + } break; + case 1: { + trig = ch2.notes[i]; + } break; + case 2: { + trig = ch3.notes[i]; + } break; + } + if (trig.active) { size_t offset_x = TRIG_OFFSET_X * (i % 8); size_t offset_y = i < 8 ? 0 : TRIG_OFFSET_Y; size_t x = TRIG_START_X + offset_x; size_t y = TRIG_START_Y + offset_y; Tile *tiles = ASSETS_DATA; - tiles += 2 * sequences[chan][i].note; + tiles += 2 * trig.note; draw_tile(x, y, tiles, COL_FG, true); draw_tile(x + 8, y, tiles + 1, COL_FG, true); } else { @@ -301,7 +388,19 @@ draw_channels(void) { unpack_tiles(channel_buttons, channel_tiles, 3 * 4); size_t k = 0; for (size_t i = 0; i < 4; i++) { - u8 clr = active_channels[i] ? COL_FG : COL_GREY; + bool active; + switch (i) { + case 0: { + active = ch1.active; + } break; + case 1: { + active = ch2.active; + } break; + case 2: { + active = ch3.active; + } break; + } + u8 clr = active ? COL_FG : COL_GREY; size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; draw_tile(CHAN_START_X, y, channel_tiles + k++, clr, false); draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, clr, false); @@ -321,55 +420,54 @@ draw_channel_cursor(size_t i, u8 clr) { void irq_timer(void) { - Note active_note; - if (active_channels[0]) { - SeqTrigger *trig = &sequences[0][step_counter]; - active_note = trig->note; - if (trig->trigger) { - SOUND_SQUARE1_SWEEP = SOUND_SWEEP_NUMBER(trig->sweep_number) - | SOUND_SWEEP_DIR(trig->sweep_direction) - | SOUND_SWEEP_TIME(trig->sweep_time); - SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(trig->env_volume) - | SOUND_SQUARE_ENV_TIME(trig->env_time) - | SOUND_SQUARE_ENV_DIR(trig->env_direction) - | SOUND_SQUARE_DUTY(trig->duty_cycle); + if (ch1.active) { + TriggerNote *trig = &ch1.notes[step_counter]; + ChannelSquareParams *params = &ch1.params[step_counter]; + if (trig->active) { + SOUND_SQUARE1_SWEEP = SOUND_SWEEP_NUMBER(params->sweep_number) + | SOUND_SWEEP_DIR(params->sweep_direction) + | SOUND_SWEEP_TIME(params->sweep_time); + SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(params->env_volume) + | SOUND_SQUARE_ENV_TIME(params->env_time) + | SOUND_SQUARE_ENV_DIR(params->env_direction) + | SOUND_SQUARE_DUTY(params->duty_cycle); SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET - | sound_rates[active_note]; + | sound_rates[trig->note]; } } else { SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE1_FREQ = 0; } - if (active_channels[1]) { - SeqTrigger *trig = &sequences[1][step_counter]; - active_note = trig->note; - if (trig->trigger) { - SOUND_SQUARE2_CTRL = SOUND_SQUARE_ENV_VOL(trig->env_volume) - | SOUND_SQUARE_ENV_TIME(trig->env_time) - | SOUND_SQUARE_ENV_DIR(trig->env_direction) - | SOUND_SQUARE_DUTY(trig->duty_cycle); + if (ch2.active) { + TriggerNote *trig = &ch2.notes[step_counter]; + ChannelSquareParams *params = &ch2.params[step_counter]; + if (trig->active) { + SOUND_SQUARE2_CTRL = SOUND_SQUARE_ENV_VOL(params->env_volume) + | SOUND_SQUARE_ENV_TIME(params->env_time) + | SOUND_SQUARE_ENV_DIR(params->env_direction) + | SOUND_SQUARE_DUTY(params->duty_cycle); SOUND_SQUARE2_FREQ = SOUND_FREQ_RESET - | sound_rates[active_note]; + | sound_rates[trig->note]; } } else { SOUND_SQUARE2_CTRL = 0; SOUND_SQUARE2_FREQ = 0; } - if (active_channels[2]) { - SeqTrigger *trig = &sequences[2][step_counter]; - active_note = trig->note; - if (trig->trigger) { + if (ch3.active) { + TriggerNote *trig = &ch3.notes[step_counter]; + ChannelWaveParams *params = &ch3.params[step_counter]; + if (trig->active) { // Update both banks. // TODO: Actually depends on which bank is selected, no need to // update both if only one is playing. // TODO: Should we compare if previous and current wave are the // same before updating? SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); - dma_copy(SOUND_WAVE_RAM, trig->wave_a, 16, 3); + dma_copy(SOUND_WAVE_RAM, params->wave_a, 16, 3); SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); - dma_copy(SOUND_WAVE_RAM, trig->wave_b, 16, 3); + dma_copy(SOUND_WAVE_RAM, params->wave_b, 16, 3); - switch (trig->wave_mode) { + switch (params->wave_mode) { case 0: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | SOUND_WAVE_BANK_SELECT(0); @@ -389,7 +487,7 @@ irq_timer(void) { } SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; - switch (trig->wave_volume) { + switch (params->wave_volume) { case 0: { SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; } break; @@ -407,7 +505,7 @@ irq_timer(void) { } break; } SOUND_WAVE_FREQ = SOUND_FREQ_RESET - | sound_rates[active_note]; + | sound_rates[trig->note]; } } else { SOUND_WAVE_CTRL = 0; @@ -441,7 +539,17 @@ void handle_channel_cursor(void); void handle_channel_cursor(void) { if (key_tap(KEY_B)) { - active_channels[channel_selection_loc] ^= 1; + switch (channel_selection_loc) { + case 0: { + ch1.active ^= 1; + } break; + case 1: { + ch2.active ^= 1; + } break; + case 2: { + ch3.active ^= 1; + } break; + } draw_channels(); } if (key_tap(KEY_RIGHT)) { @@ -473,25 +581,36 @@ handle_channel_cursor(void) { void handle_trigger_cursor(void) { - SeqTrigger *trig = &sequences[channel_selection_loc][trig_selection_loc]; + TriggerNote *trig; + switch (channel_selection_loc) { + case 0: { + trig = &ch1.notes[trig_selection_loc]; + } break; + case 1: { + trig = &ch2.notes[trig_selection_loc]; + } break; + case 2: { + trig = &ch3.notes[trig_selection_loc]; + } break; + } if (key_tap(KEY_B)) { // Toggle trigger. - trig->trigger ^= 1; + trig->active ^= 1; draw_trigger(channel_selection_loc, trig_selection_loc); } else if (key_tap(KEY_A)) { // Switch to parameter selection. // current_selection = SEQ_SELECT_PARAMETER; } else if (key_tap(KEY_L)) { // Decrease note. - if (trig->trigger) { + if (trig->active) { trig->note = MAX(trig->note - 1, NOTE_C_2); clear_trigger(trig_selection_loc); draw_trigger(channel_selection_loc, trig_selection_loc); } } else if (key_tap(KEY_R)) { // Increase note. - if (trig->trigger) { + if (trig->active) { trig->note = MIN( trig->note + 1, NOTE_C_8); clear_trigger(trig_selection_loc); draw_trigger(channel_selection_loc, trig_selection_loc); @@ -738,7 +857,7 @@ load_note_names(void) { void sequencer_init(void) { - // TODO: Unpack non-sprite tiles directly on the VRAM. + // Unpack non-sprite tiles directly on the VRAM. load_note_names(); // Initialize background objects and sprites. -- cgit v1.2.1