From 0fdb142de3225dcc9f4b1eb940ec26ff869c0eb2 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Mon, 3 Apr 2023 20:56:38 +0200 Subject: Add pattern selection control --- src/sequencer.c | 221 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 154 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/sequencer.c b/src/sequencer.c index 3f477f3..511e5e4 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -6,7 +6,6 @@ // - 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 @@ -24,8 +23,6 @@ // Theme colors. #define COL_CURSOR COL_BLUE #define COL_NOTE_PRESSED COL_GREY -#define COL_CURRENT_CHANNEL COL_GREY -#define COL_CURRENT_TRIG COL_GREY #define COL_WAVE_A COL_RED #define COL_WAVE_B COL_CYAN @@ -56,6 +53,12 @@ #define R_SIDEBAR_X ((TRIG_START_X) + (TRIG_OFFSET_X) * 8 + 4) #define L_SIDEBAR_X ((CHAN_START_X) - 26) +#define PAT_START_X (L_SIDEBAR_X + 5) +#define PAT_START_Y 18 +#define PAT_W 14 +#define PAT_H 12 +#define PAT_OFFSET_Y 17 + #define SEQ_N_CHANNELS 4 // @@ -184,12 +187,15 @@ static const u32 square_wave[16] = { // Globals. // -static int bpm = 75; +const int default_bpm = 75; static int step_counter = 0; int trig_selection_loc = 0; int param_selection_loc = 0; int channel_selection_loc = 0; +int pattern_selection_loc = 1; int play_status = 0; +static int current_pattern = 0; +static int next_pattern = 0; typedef struct TriggerNote { bool active; @@ -238,7 +244,7 @@ typedef struct ChannelNoise { ChannelNoiseParams params[16]; } ChannelNoise; -static ChannelSquare ch1 = { +const ChannelSquare default_ch1 = { .notes = { {true, NOTE_C_4}, {true, NOTE_D_4}, @@ -278,7 +284,7 @@ static ChannelSquare ch1 = { .active = false, }; -static ChannelSquare ch2 = { +const ChannelSquare default_ch2 = { .notes = { {true, NOTE_C_4}, {true, NOTE_D_4}, @@ -318,7 +324,7 @@ static ChannelSquare ch2 = { .active = false, }; -static ChannelWave ch3 = { +const ChannelWave default_ch3 = { .notes = { {true, NOTE_C_4}, {true, NOTE_D_4}, @@ -358,8 +364,7 @@ static ChannelWave ch3 = { .active = false, }; -// TODO: Add default noise parameters data. -static ChannelNoise ch4 = { +const ChannelNoise default_ch4 = { .notes = { {true, NOTE_C_4}, {false, NOTE_D_4}, @@ -399,6 +404,25 @@ static ChannelNoise ch4 = { .active = true, }; +typedef struct Pattern { + int bpm; + ChannelSquare ch1; + ChannelSquare ch2; + ChannelWave ch3; + ChannelNoise ch4; +} Pattern; + +static Pattern patterns[8] = { + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, + {default_bpm, default_ch1, default_ch2, default_ch3, default_ch4}, +}; + // // Channel render functions. // @@ -413,16 +437,16 @@ draw_channels(void) { bool active = false; switch (i) { case 0: { - active = ch1.active; + active = patterns[pattern_selection_loc].ch1.active; } break; case 1: { - active = ch2.active; + active = patterns[pattern_selection_loc].ch2.active; } break; case 2: { - active = ch3.active; + active = patterns[pattern_selection_loc].ch3.active; } break; case 3: { - active = ch4.active; + active = patterns[pattern_selection_loc].ch4.active; } break; } u8 clr = active ? COL_FG : COL_GREY; @@ -454,7 +478,7 @@ clear_trigger(size_t i) { size_t x0 = TRIG_START_X + offset_x + 1; size_t x1 = TRIG_START_X + offset_x + TRIG_W - 1; size_t y0 = TRIG_START_Y + offset_y + 1; - size_t y1 = TRIG_START_Y + offset_y + TRIG_H - 1; + size_t y1 = TRIG_START_Y + offset_y + TRIG_H - 4; draw_filled_rect(x0, y0, x1, y1, COL_BG); } @@ -463,16 +487,16 @@ draw_trigger(size_t chan, size_t i) { TriggerNote trig = {0}; switch (chan) { case 0: { - trig = ch1.notes[i]; + trig = patterns[pattern_selection_loc].ch1.notes[i]; } break; case 1: { - trig = ch2.notes[i]; + trig = patterns[pattern_selection_loc].ch2.notes[i]; } break; case 2: { - trig = ch3.notes[i]; + trig = patterns[pattern_selection_loc].ch3.notes[i]; } break; case 3: { - trig = ch4.notes[i]; + trig = patterns[pattern_selection_loc].ch4.notes[i]; } break; } if (trig.active) { @@ -503,8 +527,8 @@ void draw_current_step(u8 col) { size_t offset_x = TRIG_OFFSET_X * (step_counter % 8); size_t offset_y = step_counter < 8 ? 2 : 2 + TRIG_OFFSET_Y; - size_t x0 = TRIG_START_X + 4 + offset_x; - size_t x1 = TRIG_START_X - 4 + TRIG_W + offset_x; + size_t x0 = TRIG_START_X + 3 + offset_x; + size_t x1 = TRIG_START_X - 3 + TRIG_W + offset_x; size_t y = TRIG_START_Y - 4 + TRIG_H + offset_y; draw_line(x0, y, x1, y, col); } @@ -531,28 +555,44 @@ draw_play() { void draw_pattern_buttons() { - size_t x = L_SIDEBAR_X; - size_t y = PARAMS_START_Y + 17; - txt_drawf_small("PAT", x + 5, y - 10, 4, COL_FG); + size_t x = PAT_START_X; + size_t y = PAT_START_Y; + txt_drawf_small("PAT", x, y - 10, 4, COL_FG); char pat_names[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', }; - for (size_t i = 0; i < 8; i++) { - draw_rect(x + 5, y, x + 19, y + 12, COL_GREY); - txt_drawc(pat_names[i], x + 9, y + 2, 6, COL_GREY); - y += 17; + for (int i = 0; i < 8; i++) { + int color = COL_GREY; + if (i == current_pattern) { + color = COL_FG; + } + if (i == next_pattern && current_pattern != next_pattern) { + color = COL_BLUE; + } + draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); + draw_rect(x, y, x + PAT_W, y + PAT_H, color); + txt_drawc(pat_names[i], x + 4, y + 2, 6, color); + y += PAT_OFFSET_Y; } } +void +draw_pattern_cursor(size_t i, u8 clr) { + size_t offset_x = 0; + size_t offset_y = PAT_H + i * PAT_OFFSET_Y + 2; + size_t x0 = PAT_START_X + offset_x; + size_t x1 = PAT_START_X + offset_x + PAT_W; + size_t y = PAT_START_Y + offset_y; + draw_line(x0, y, x1, y, clr); +} + void draw_stop() { size_t x = R_SIDEBAR_X; size_t y = TRIG_START_Y + 14; draw_rect(x, y, x + 24, y + 10, COL_RED); draw_filled_rect(x + 10, y + 3, x + 14, y + 7, COL_RED); - // DEBUG: ... - draw_pattern_buttons(); } void @@ -566,6 +606,7 @@ draw_bpm() { txt_drawf_small("BPM", x + 5, y - 4, 4, COL_FG); // Make sure its horizontally centered if only 2 digits + int bpm = patterns[current_pattern].bpm; if (bpm >= 100) { txt_drawf("%d", x + 3, y + 7, 6, COL_FG, bpm); } else { @@ -907,9 +948,10 @@ IWRAM_CODE void draw_parameters_wave(void) { // Draw current wave data. + Pattern *pat = &patterns[pattern_selection_loc]; { - u8 *wave_a = ch3.params[trig_selection_loc].wave_a; - u8 *wave_b = ch3.params[trig_selection_loc].wave_b; + u8 *wave_a = pat->ch3.params[trig_selection_loc].wave_a; + u8 *wave_b = pat->ch3.params[trig_selection_loc].wave_b; size_t x = PARAMS_START_X; size_t y = PARAMS_START_Y + 13; @@ -966,7 +1008,7 @@ draw_parameters_wave(void) { draw_line(x, y + 17, x + 30, y + 17, COL_FG); txt_drawf_small("mode", x + 6, y, 4, COL_FG); - switch (ch3.params[trig_selection_loc].wave_mode) { + switch (pat->ch3.params[trig_selection_loc].wave_mode) { case 0: { txt_drawf("A", x + 12, y + 7, 6, COL_FG); } break; @@ -990,7 +1032,7 @@ draw_parameters_wave(void) { draw_line(x, y + 20, x + 30, y + 20, COL_FG); txt_drawf_small("vol", x + 8, y + 3, 4, COL_FG); - switch (ch3.params[trig_selection_loc].wave_volume) { + switch (pat->ch3.params[trig_selection_loc].wave_volume) { case 0: { txt_drawf("0", x + 12, y + 10, 6, COL_FG); } break; @@ -1375,12 +1417,13 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) { void draw_parameters(void) { clear_parameters(); + Pattern *pat = &patterns[pattern_selection_loc]; switch (channel_selection_loc) { case 0: { - draw_parameters_square(&ch1.params[trig_selection_loc], true); + draw_parameters_square(&pat->ch1.params[trig_selection_loc], true); } break; case 1: { - draw_parameters_square(&ch2.params[trig_selection_loc], false); + draw_parameters_square(&pat->ch2.params[trig_selection_loc], false); } break; case 2: { draw_parameters_wave(); @@ -1392,9 +1435,14 @@ draw_parameters(void) { void irq_timer(void) { - if (ch1.active) { - TriggerNote *trig = &ch1.notes[step_counter]; - ChannelSquareParams *params = &ch1.params[step_counter]; + if (current_pattern != next_pattern && step_counter == 0) { + current_pattern = next_pattern; + draw_pattern_buttons(); + } + Pattern *pat = &patterns[current_pattern]; + if (pat->ch1.active) { + TriggerNote *trig = &pat->ch1.notes[step_counter]; + ChannelSquareParams *params = &pat->ch1.params[step_counter]; if (trig->active) { SOUND_SQUARE1_SWEEP = SOUND_SWEEP_NUMBER(params->sweep_number) | SOUND_SWEEP_DIR(params->sweep_direction) @@ -1410,9 +1458,9 @@ irq_timer(void) { SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE1_FREQ = 0; } - if (ch2.active) { - TriggerNote *trig = &ch2.notes[step_counter]; - ChannelSquareParams *params = &ch2.params[step_counter]; + if (pat->ch2.active) { + TriggerNote *trig = &pat->ch2.notes[step_counter]; + ChannelSquareParams *params = &pat->ch2.params[step_counter]; if (trig->active) { SOUND_SQUARE2_CTRL = SOUND_SQUARE_ENV_VOL(params->env_volume) | SOUND_SQUARE_ENV_TIME(params->env_time) @@ -1425,9 +1473,9 @@ irq_timer(void) { SOUND_SQUARE2_CTRL = 0; SOUND_SQUARE2_FREQ = 0; } - if (ch3.active) { - TriggerNote *trig = &ch3.notes[step_counter]; - ChannelWaveParams *params = &ch3.params[step_counter]; + if (pat->ch3.active) { + TriggerNote *trig = &pat->ch3.notes[step_counter]; + ChannelWaveParams *params = &pat->ch3.params[step_counter]; if (trig->active) { switch (params->wave_mode) { case 0: { @@ -1477,9 +1525,9 @@ 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 (pat->ch4.active) { + TriggerNote *trig = &pat->ch4.notes[step_counter]; + ChannelNoiseParams *params = &pat->ch4.params[step_counter]; SOUND_NOISE_CTRL = SOUND_NOISE_ENV_VOL(params->env_volume) | SOUND_NOISE_ENV_TIME(params->env_time) | SOUND_NOISE_ENV_DIR(params->env_direction); @@ -1533,18 +1581,19 @@ set_time(int bpm) { TriggerNote * get_current_trig(void) { + Pattern *pat = &patterns[pattern_selection_loc]; switch (channel_selection_loc) { case 0: { - return &ch1.notes[trig_selection_loc]; + return &pat->ch1.notes[trig_selection_loc]; } break; case 1: { - return &ch2.notes[trig_selection_loc]; + return &pat->ch2.notes[trig_selection_loc]; } break; case 2: { - return &ch3.notes[trig_selection_loc]; + return &pat->ch3.notes[trig_selection_loc]; } break; case 3: { - return &ch4.notes[trig_selection_loc]; + return &pat->ch4.notes[trig_selection_loc]; } break; } return NULL; @@ -1557,6 +1606,7 @@ void (*input_handler)(void); void handle_trigger_selection(void); void handle_channel_selection(void); +void handle_pattern_selection(void); void handle_param_selection_sq1(void); void handle_param_selection_sq2(void); void handle_param_selection_wave(void); @@ -1567,16 +1617,16 @@ handle_channel_selection(void) { if (key_tap(KEY_B)) { switch (channel_selection_loc) { case 0: { - ch1.active ^= 1; + patterns[pattern_selection_loc].ch1.active ^= 1; } break; case 1: { - ch2.active ^= 1; + patterns[pattern_selection_loc].ch2.active ^= 1; } break; case 2: { - ch3.active ^= 1; + patterns[pattern_selection_loc].ch3.active ^= 1; } break; case 3: { - ch4.active ^= 1; + patterns[pattern_selection_loc].ch4.active ^= 1; } break; } draw_channels(); @@ -1585,11 +1635,15 @@ handle_channel_selection(void) { trig_selection_loc = 0; param_selection_loc = 0; input_handler = handle_trigger_selection; - draw_channel_cursor(channel_selection_loc, COL_CURRENT_CHANNEL); + draw_channel_cursor(channel_selection_loc, COL_GREY); draw_trig_cursor(trig_selection_loc, COL_CURSOR); TriggerNote *trig = get_current_trig(); draw_note(trig->note, COL_NOTE_PRESSED); draw_parameters(); + } else if (key_tap(KEY_LEFT)) { + input_handler = handle_pattern_selection; + draw_channel_cursor(channel_selection_loc, COL_GREY); + draw_pattern_cursor(pattern_selection_loc, COL_CURSOR); } else if (key_tap(KEY_UP)) { draw_channel_cursor(channel_selection_loc, COL_BG); if (channel_selection_loc == 0) { @@ -1611,6 +1665,33 @@ handle_channel_selection(void) { } } +void +handle_pattern_selection(void) { + if (key_tap(KEY_B)) { + next_pattern = pattern_selection_loc; + draw_pattern_buttons(); + } + if (key_tap(KEY_RIGHT)) { + input_handler = handle_channel_selection; + draw_channel_cursor(channel_selection_loc, COL_CURSOR); + draw_pattern_cursor(pattern_selection_loc, COL_GREY); + } else if (key_tap(KEY_UP)) { + if (pattern_selection_loc > 0) { + draw_pattern_cursor(pattern_selection_loc, COL_BG); + pattern_selection_loc = pattern_selection_loc - 1; + draw_pattern_cursor(pattern_selection_loc, COL_CURSOR); + draw_triggers(); + } + } else if (key_tap(KEY_DOWN)) { + if (pattern_selection_loc < 7) { + draw_pattern_cursor(pattern_selection_loc, COL_BG); + pattern_selection_loc = pattern_selection_loc + 1; + draw_pattern_cursor(pattern_selection_loc, COL_CURSOR); + draw_triggers(); + } + } +} + void handle_param_selection_sq1(void) { // Go back to trigger selection. @@ -1674,7 +1755,8 @@ handle_param_selection_sq1(void) { } else { inc = 1; } - ChannelSquareParams *params = &ch1.params[trig_selection_loc]; + Pattern *pat = &patterns[pattern_selection_loc]; + ChannelSquareParams *params = &pat->ch1.params[trig_selection_loc]; switch (param_selection_loc) { case 0: { params->duty_cycle = CLAMP(params->duty_cycle + inc, 0, 3); @@ -1734,7 +1816,8 @@ handle_param_selection_sq2(void) { } else { inc = 1; } - ChannelSquareParams *params = &ch2.params[trig_selection_loc]; + Pattern *pat = &patterns[pattern_selection_loc]; + ChannelSquareParams *params = &pat->ch2.params[trig_selection_loc]; switch (param_selection_loc) { case 0: { params->duty_cycle = CLAMP(params->duty_cycle + inc, 0, 3); @@ -1756,6 +1839,8 @@ handle_param_selection_sq2(void) { void handle_param_selection_wave(void) { + Pattern *pat = &patterns[pattern_selection_loc]; + // Go back to trigger selection. if (key_tap(KEY_A)) { draw_params_cursor(param_selection_loc, COL_BG); @@ -1864,7 +1949,7 @@ handle_param_selection_wave(void) { 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; + u8 *byte = &pat->ch3.params[trig_selection_loc].wave_a; byte += byte_number; if (odd) { *byte = (~0xF & *byte) | ((*byte + inc) & 0xF); @@ -1874,7 +1959,7 @@ handle_param_selection_wave(void) { } 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; + u8 *byte = &pat->ch3.params[trig_selection_loc].wave_b; byte += byte_number; if (odd) { *byte = (~0xF & *byte) | ((*byte + inc) & 0xF); @@ -1883,8 +1968,8 @@ handle_param_selection_wave(void) { } } 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; + u32 *wave_a = &pat->ch3.params[trig_selection_loc].wave_a; + u32 *wave_b = &pat->ch3.params[trig_selection_loc].wave_b; switch (param_selection_loc) { case 64: { memcpy32(wave_a, sine_wave, 16); @@ -1918,10 +2003,10 @@ handle_param_selection_wave(void) { } break; } } else if (param_selection_loc == 72) { - u8 *wave_mode = &ch3.params[trig_selection_loc].wave_mode; + u8 *wave_mode = &pat->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; + u8 *wave_volume = &pat->ch3.params[trig_selection_loc].wave_volume; *wave_volume = CLAMP(*wave_volume + inc, 0, 4); } draw_parameters(); @@ -2185,7 +2270,7 @@ handle_trigger_selection(void) { } break; } draw_params_cursor(param_selection_loc, COL_CURSOR); - draw_trig_cursor(trig_selection_loc, COL_CURRENT_TRIG); + draw_trig_cursor(trig_selection_loc, COL_GREY); } } @@ -2202,7 +2287,7 @@ handle_sequencer_input(void) { } step_counter = 0; if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) == 0) { - set_time(bpm); + set_time(patterns[current_pattern].bpm); } else { draw_current_step(COL_RED); TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; @@ -2249,12 +2334,14 @@ sequencer_init(void) { // Initialize input handler. input_handler = handle_trigger_selection; draw_trig_cursor(trig_selection_loc, COL_CURSOR); - draw_channel_cursor(channel_selection_loc, COL_CURRENT_CHANNEL); + draw_channel_cursor(channel_selection_loc, COL_GREY); + draw_pattern_cursor(pattern_selection_loc, COL_GREY); draw_current_step(COL_RED); draw_parameters(); draw_bpm(); draw_play(); draw_stop(); + draw_pattern_buttons(); // Initialize sound system. SOUND_STATUS = SOUND_ENABLE; -- cgit v1.2.1