From 0bf2a5a3f360f13d6195013a8e63b0519905202a Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 4 May 2021 14:40:38 +0200 Subject: Update UI to enable channel selection --- src/sequencer.c | 285 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 212 insertions(+), 73 deletions(-) diff --git a/src/sequencer.c b/src/sequencer.c index 0c6181f..ae06031 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -1,3 +1,20 @@ +// Positioning parameters. +#define SEQ_TRIG_POS_X 45 +#define SEQ_TRIG_POS_Y 50 +#define SEQ_TRIG_DIST_X 18 +#define SEQ_TRIG_DIST_Y 28 +#define SEQ_CHANNEL_POS_X SEQ_TRIG_POS_X - 32 +#define SEQ_CHANNEL_POS_Y SEQ_TRIG_POS_Y +#define SEQ_CHANNEL_DIST_Y 11 +#define SEQ_ENV_POS_X 10 +#define SEQ_ENV_POS_Y 10 +#define SEQ_ENV_DIST 32 +#define SEQ_DUTYCYCLE_POS_X SEQ_ENV_POS_X + SEQ_ENV_DIST * 3 +#define SEQ_DUTYCYCLE_POS_Y 10 - 8 +#define SEQ_SWEEP_POS_X SEQ_DUTYCYCLE_POS_X + SEQ_ENV_DIST +#define SEQ_SWEEP_POS_Y SEQ_ENV_POS_Y +#define SEQ_N_CHANNELS 2 + u32 sprite_note_names[] = { 0x000000e0, 0x202020e0, 0x0000000e, 0x080e020e, 0x00000098, 0xa8a8a898, 0x00000038, 0x203b0a39, @@ -223,6 +240,25 @@ u32 sprite_synth_param_selector[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; +u32 sprite_channels[] = { + 0xc0404040, 0x404040c0, 0xff005c44, 0xc45c00ff, + 0xff002121, 0x212500ff, 0x03020202, 0x02020203, + 0xc0404040, 0x404040c0, 0xff00aea2, 0xe2ae00ff, + 0xff005050, 0x505200ff, 0x03020202, 0x02020203, + 0xc0404040, 0x404040c0, 0xff005751, 0x715700ff, + 0xff00a8a8, 0xa8a900ff, 0x03020202, 0x02020203, + 0xc0404040, 0x404040c0, 0xff005751, 0x715700ff, + 0xff00a8a8, 0xa8c900ff, 0x03020202, 0x02020203, + 0xc0404040, 0x404040c0, 0xff007010, 0x301000ff, + 0xff000507, 0x050500ff, 0x03020202, 0x02020203, +}; + +u32 sprite_channels_selector[] = { + 0x70101000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x0e080800, 0x00000000, + 0x00101070, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x0008080e, 0x00000000, +}; typedef struct SeqTrigger { bool trigger; @@ -234,32 +270,39 @@ typedef struct SeqTrigger { u8 sweep_number; u8 sweep_time; u8 sweep_direction; - // TODO: ... + // TODO: Do we need other fields? } SeqTrigger; -static SeqTrigger sequence_synth[] = { - {true , NOTE_D_4 , 0, 0, 0, 0, 0, 0, 0}, - {true , NOTE_F_4 , 1, 1, 0, 0, 1, 1, 0}, - {true , NOTE_A_4 , 2, 2, 0, 1, 2, 2, 1}, - {true , NOTE_C_5 , 3, 3, 0, 1, 3, 3, 1}, - - {true , NOTE_D_4 , 4, 4, 1, 2, 4, 4, 0}, - {false , NOTE_C_SHARP_4 , 5, 5, 1, 2, 5, 5, 0}, - {false , NOTE_D_4 , 6, 6, 1, 3, 6, 6, 1}, - {false , NOTE_D_4 , 7, 7, 1, 3, 7, 7, 1}, - - {true , NOTE_D_4 , 8, 0, 0, 0, 7, 0, 0}, - {true , NOTE_F_4 , 9, 1, 0, 0, 6, 1, 0}, - {true , NOTE_A_4 , 10, 2, 0, 1, 5, 2, 1}, - {true , NOTE_C_5 , 11, 3, 0, 1, 4, 3, 1}, - - {true , NOTE_D_4 , 12, 4, 1, 2, 3, 4, 0}, - {false , NOTE_D_4 , 13, 5, 1, 2, 2, 5, 0}, - {true , NOTE_A_4 , 14, 6, 1, 3, 1, 6, 1}, - {false , NOTE_A_5 , 15, 7, 1, 3, 0, 7, 1}, +static SeqTrigger sequence_synth[16] = { + {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {true, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, + {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0}, }; -static int bpm = 120; +// TODO: Support for copy paste anything, contextual. If we are on trigger +// selection mode, pressing the copy key (SELECT? SELECT+B?), all synth +// parameters will be copied for that trigger. Pressing paste (SELECT+A?) will +// update the params. This only works on compatible channels (Not on the wave +// channel for example). If an entire channel is copied, all trigs in the +// sequence will be copied. We can choose to copy only one of the parameters as +// well or the note/duration. +// TODO: Allow muting and unmuting channels. Show in grey when muted. +// TODO: Show channel beat indicator if a note is being played. + +static int bpm = 180; static int step_counter = 0; static Note active_note; @@ -335,21 +378,15 @@ set_time(int bpm) { // - 048-048 sweep: direction indicator. // - 049-049 sweep: sweep label. // - 050-050 synth parameter selector. +// - 051-051 channel 1. +// - 052-052 channel 2. +// - 053-053 channel 3. +// - 054-054 channel 4. +// - 055-055 RESERVED: FM synth?. +// - 056-056 channel selector. // -// Positioning parameters. -#define SEQ_TRIG_POS_X 45 -#define SEQ_TRIG_POS_Y 50 -#define SEQ_TRIG_DIST 20 -#define SEQ_ENV_POS_X 10 -#define SEQ_ENV_POS_Y 10 -#define SEQ_ENV_DIST 32 -#define SEQ_DUTYCYCLE_POS_X SEQ_ENV_POS_X + SEQ_ENV_DIST * 3 -#define SEQ_DUTYCYCLE_POS_Y 10 - 8 -#define SEQ_SWEEP_POS_X SEQ_DUTYCYCLE_POS_X + SEQ_ENV_DIST -#define SEQ_SWEEP_POS_Y SEQ_ENV_POS_Y - size_t obj_counter = 0; typedef struct SeqSprite { @@ -365,14 +402,16 @@ typedef struct SeqSprite { typedef enum { SEQ_SELECT_TRIGGER, + SEQ_SELECT_CHANNEL, SEQ_SELECT_PARAMETER, } SeqSelect; int trig_selection_loc = 0; int param_selection_loc = 0; +int channel_selection_loc = 0; SeqSelect current_selection = SEQ_SELECT_TRIGGER; -SeqSprite seq_sprites[51] = {0}; +SeqSprite seq_sprites[57] = {0}; void init_sequencer_sprites(void) { @@ -386,12 +425,12 @@ init_sequencer_sprites(void) { // Sprite note names. size_t sprite_id = load_packed_sprite_data(&sprite_note_names, 2, 73); for (size_t i = 0; i < 16; ++i) { - int x = SEQ_TRIG_POS_X + i * SEQ_TRIG_DIST; + int x = SEQ_TRIG_POS_X + i * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y; int base_tile = sprites[sprite_id].tile_start; if (i >= 8) { - y += 32; - x -= 8 * SEQ_TRIG_DIST; + y += SEQ_TRIG_DIST_Y; + x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[i].x = x; seq_sprites[i].y = y; @@ -406,12 +445,12 @@ init_sequencer_sprites(void) { // Trigger boxes. sprite_id = load_packed_sprite_data(&sprite_trigger_button, 8, 1); for (size_t i = 0; i < 16; ++i) { - int x = SEQ_TRIG_POS_X + i * SEQ_TRIG_DIST; + int x = SEQ_TRIG_POS_X + i * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y; int base_tile = sprites[sprite_id].tile_start; if (i >= 8) { - y += 32; - x -= 8 * SEQ_TRIG_DIST; + y += 28; + x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[i + 16].id = obj_counter++; seq_sprites[i + 16].base_tile = base_tile; @@ -434,12 +473,12 @@ init_sequencer_sprites(void) { sprite_id = load_packed_sprite_data(&sprite_trigger_selection, 16, 1); { - int x = SEQ_TRIG_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST; + int x = SEQ_TRIG_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y - 2; int base_tile = sprites[sprite_id].tile_start; if (trig_selection_loc >= 8) { - y += 32; - x -= 8 * SEQ_TRIG_DIST; + y += SEQ_TRIG_DIST_Y; + x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[33].id = obj_counter++; seq_sprites[33].base_tile = base_tile; @@ -643,6 +682,72 @@ init_sequencer_sprites(void) { seq_sprites[50].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); seq_sprites[50].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); } + + sprite_id = load_packed_sprite_data(&sprite_channels, 4, 5); + { + int x = SEQ_CHANNEL_POS_X; + int y = SEQ_CHANNEL_POS_Y; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[51].id = obj_counter++; + seq_sprites[51].base_tile = base_tile; + seq_sprites[51].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[51].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[51].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); + } + { + int x = SEQ_CHANNEL_POS_X; + int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y; + int base_tile = sprites[sprite_id].tile_start + 4; + seq_sprites[52].id = obj_counter++; + seq_sprites[52].base_tile = base_tile; + seq_sprites[52].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[52].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[52].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); + } + { + int x = SEQ_CHANNEL_POS_X; + int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y * 2; + int base_tile = sprites[sprite_id].tile_start + 8; + seq_sprites[53].id = obj_counter++; + seq_sprites[53].base_tile = base_tile; + seq_sprites[53].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; + seq_sprites[53].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[53].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); + } + { + int x = SEQ_CHANNEL_POS_X; + int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y * 3; + int base_tile = sprites[sprite_id].tile_start + 12; + seq_sprites[54].id = obj_counter++; + seq_sprites[54].base_tile = base_tile; + seq_sprites[54].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; + seq_sprites[54].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[54].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); + } + { + int x = SEQ_CHANNEL_POS_X; + int y = SEQ_CHANNEL_POS_Y + SEQ_CHANNEL_DIST_Y * 4; + int base_tile = sprites[sprite_id].tile_start + 16; + seq_sprites[55].id = obj_counter++; + seq_sprites[55].base_tile = base_tile; + seq_sprites[55].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; + seq_sprites[55].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[55].obj_attr_2 = base_tile | OBJ_PAL_BANK(0); + } + + sprite_id = load_packed_sprite_data(&sprite_channels_selector, 8, 1); + { + int x = SEQ_CHANNEL_POS_X; + int y = SEQ_CHANNEL_POS_Y - 2; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[56].id = obj_counter++; + seq_sprites[56].base_tile = base_tile; + // TODO: Start hidden. + // seq_sprites[55].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y) | OBJ_HIDDEN; + seq_sprites[56].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[56].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); + seq_sprites[56].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + } } void @@ -666,11 +771,11 @@ update_sequencer_sprites(void) { // 33: Sequence indicator. { - int x = SEQ_TRIG_POS_X + step_counter * SEQ_TRIG_DIST + 3; + int x = SEQ_TRIG_POS_X + step_counter * SEQ_TRIG_DIST_X + 3; int y = SEQ_TRIG_POS_Y + 15; if (step_counter >= 8) { - y += 32; - x -= 8 * SEQ_TRIG_DIST; + y += SEQ_TRIG_DIST_Y; + x -= 8 * SEQ_TRIG_DIST_X; } seq_sprites[32].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); // TODO: Mask and update instead. seq_sprites[32].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); // TODO: Mask and update instead. @@ -678,13 +783,20 @@ update_sequencer_sprites(void) { // 34: Trigger selection. { - int x = SEQ_TRIG_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST; + int x = SEQ_TRIG_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST_X; int y = SEQ_TRIG_POS_Y - 2; if (trig_selection_loc >= 8) { - y += 32; - x -= 8 * SEQ_TRIG_DIST; + y += SEQ_TRIG_DIST_Y; + x -= 8 * SEQ_TRIG_DIST_X; + } + if (current_selection == SEQ_SELECT_TRIGGER + || current_selection == SEQ_SELECT_PARAMETER) { + seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); + } else { + seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE + | OBJ_Y_COORD(y) + | OBJ_HIDDEN; } - seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); seq_sprites[33].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); int base_tile = seq_sprites[33].base_tile; if (current_selection == SEQ_SELECT_TRIGGER) { @@ -757,10 +869,6 @@ update_sequencer_sprites(void) { { int x = SEQ_ENV_POS_X + 1 + (param_selection_loc % 7) * SEQ_ENV_DIST; int y = SEQ_ENV_POS_Y - 3; - if (param_selection_loc >= 8) { - y += 32; - x -= 8 * SEQ_TRIG_DIST; - } int hidden = 0; if (current_selection != SEQ_SELECT_PARAMETER) { hidden = OBJ_HIDDEN; @@ -768,40 +876,52 @@ update_sequencer_sprites(void) { seq_sprites[50].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y) | hidden; seq_sprites[50].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); } + + // 57: Parameter selector. + { + int x = SEQ_CHANNEL_POS_X; + int y = SEQ_CHANNEL_POS_Y - 2; + y += (channel_selection_loc % SEQ_N_CHANNELS * SEQ_CHANNEL_DIST_Y); + seq_sprites[56].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); + seq_sprites[56].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); + int base_tile = seq_sprites[56].base_tile; + if (current_selection == SEQ_SELECT_CHANNEL) { + seq_sprites[56].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + } else { + seq_sprites[56].obj_attr_2 = base_tile | OBJ_PAL_BANK(3); + } + } } void handle_sequencer_input(void) { if (current_selection == SEQ_SELECT_TRIGGER) { if (key_pressed(KEY_LEFT)) { - if (trig_selection_loc == 0) { - trig_selection_loc = 15; + if (trig_selection_loc == 0 || trig_selection_loc == 8) { + current_selection = SEQ_SELECT_CHANNEL; } else { trig_selection_loc = CLAMP(trig_selection_loc - 1, 0, 15); } - } - if (key_pressed(KEY_RIGHT)) { + } else if (key_pressed(KEY_RIGHT)) { if (trig_selection_loc == 15) { trig_selection_loc = 0; } else { trig_selection_loc = CLAMP(trig_selection_loc + 1, 0, 15); } - } - if (key_pressed(KEY_UP) || key_pressed(KEY_DOWN)) { + } else if (key_pressed(KEY_UP) || key_pressed(KEY_DOWN)) { trig_selection_loc = (trig_selection_loc + 8) % 16; - } - if (key_pressed(KEY_B)) { + } else if (key_pressed(KEY_B)) { sequence_synth[trig_selection_loc].trigger ^= 1; - } - if (key_pressed(KEY_L)) { - sequence_synth[trig_selection_loc].note = CLAMP(sequence_synth[trig_selection_loc].note - 1, NOTE_C_2, NOTE_C_8); - } - if (key_pressed(KEY_R)) { - sequence_synth[trig_selection_loc].note = CLAMP(sequence_synth[trig_selection_loc].note + 1, NOTE_C_2, NOTE_C_8); - } - - // Switch to parameter selection. - if (key_pressed(KEY_A)) { + } else if (key_pressed(KEY_L)) { + sequence_synth[trig_selection_loc].note = CLAMP( + sequence_synth[trig_selection_loc].note - 1, + NOTE_C_2, NOTE_C_8); + } else if (key_pressed(KEY_R)) { + sequence_synth[trig_selection_loc].note = CLAMP( + sequence_synth[trig_selection_loc].note + 1, + NOTE_C_2, NOTE_C_8); + } else if (key_pressed(KEY_A)) { + // Switch to parameter selection. current_selection = SEQ_SELECT_PARAMETER; } } else if (current_selection == SEQ_SELECT_PARAMETER) { @@ -896,6 +1016,25 @@ handle_sequencer_input(void) { if (key_pressed(KEY_B)) { sequence_synth[trig_selection_loc].trigger ^= 1; } + } else if (current_selection == SEQ_SELECT_CHANNEL) { + if (key_pressed(KEY_RIGHT)) { + current_selection = SEQ_SELECT_TRIGGER; + trig_selection_loc = 0; + } + if (key_pressed(KEY_UP)) { + if (channel_selection_loc == 0) { + channel_selection_loc = 3; + } else { + channel_selection_loc = CLAMP(channel_selection_loc - 1, 0, 6); + } + } + if (key_pressed(KEY_DOWN)) { + if (channel_selection_loc == 3) { + channel_selection_loc = 0; + } else { + channel_selection_loc = CLAMP(channel_selection_loc + 1, 0, 6); + } + } } if (key_pressed(KEY_START)) { -- cgit v1.2.1