From 685cc9d6bfb1c376f1231ce7c271c6ab6900b3d9 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Sun, 2 May 2021 15:44:40 +0200 Subject: Add trigger note control --- src/main.c | 26 ++------- src/sequencer.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 152 insertions(+), 46 deletions(-) diff --git a/src/main.c b/src/main.c index 08816f3..d64208e 100644 --- a/src/main.c +++ b/src/main.c @@ -22,6 +22,8 @@ int main(void) { // Initialize sprite button overlay. init_sprite_pal(0, COLOR_WHITE); + init_sprite_pal(16, COLOR_CYAN); + init_sprite_pal(32, COLOR_RED); init_sprites(0); init_sequencer_sprites(); @@ -38,31 +40,16 @@ int main(void) { SOUND_DSOUND_MASTER = SOUND_DMG100; // Initialize timer. - int bpm = 120; - while(true) { bios_vblank_wait(); poll_keys(); - if (key_hold(KEY_UP)) { - bpm += 1; - set_time(bpm); - } - if (key_hold(KEY_DOWN)) { - bpm -= 1; - set_time(bpm); - } - - if (key_pressed(KEY_START)) { - step_counter = 0; - set_time(bpm); - } - if (key_pressed(KEY_SELECT)) { - TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; - } + handle_sequencer_input(); update_sequencer_sprites(); + render_sequencer_sprites(); - // txt_position(1,6); + // DEBUG: Output + // txt_position(1,0); // txt_clear_line(); // txt_printf(" BPM: %d\n\n", bpm); @@ -70,7 +57,6 @@ int main(void) { // txt_printf(" Step: %d\n", step_counter); // txt_clear_line(); // txt_printf(" Note: %s\n", note_names[active_note]); - render_sequencer_sprites(); }; return 0; diff --git a/src/sequencer.c b/src/sequencer.c index 8ebb9af..f459d0f 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -77,6 +77,28 @@ u32 sprite_note_names[] = { 0x000000e0, 0x202020e0, 0x0000000e, 0x0a0e0a0e, }; +u32 sprite_trigger_button[] = { + 0xfe020202, 0x02020202, 0xff808080, 0x80808080, + 0x02020202, 0x02020202, 0x80808080, 0x80808080, + 0x02020202, 0x020202fe, 0x80808080, 0x808080ff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +u32 sprite_trigger_active_indicator[] = { + 0x00000000, 0x00f80000, +}; + +u32 sprite_trigger_selection[] = { + 0x0f010101, 0x00000000, 0x80000000, 0x00000000, + 0x07040404, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0101010f, 0x00000000, 0x00000080, 0x00000000, + 0x04040407, 0x00000000, 0x00000000, 0x00000000, +}; + typedef struct SeqTrigger { bool trigger; Note note; @@ -105,6 +127,7 @@ static SeqTrigger sequence_synth[] = { {false, NOTE_A_5}, }; +static int bpm = 120; static int step_counter = 0; static Note active_note; @@ -133,15 +156,15 @@ set_time(int bpm) { TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; } -static size_t note_sprites_id = 0; - // // Sequencer sprite memory. // // Currently we load all sequencer sprite memory in the VRAM: // -// - Note names: 0-73x2 tiles -// - ... +// - Note names: 73x2 tiles +// - Sprite trigger button: 8 tiles +// - Duration indicator (TODO) +// - Current step marker. // // The order of OBJs correspond to: // @@ -163,33 +186,24 @@ typedef struct SeqSprite { u16 obj_attr_2; } SeqSprite; -#define SEQ_POS_X 20 -#define SEQ_POS_Y 20 +#define SEQ_POS_X 45 +#define SEQ_POS_Y 50 +#define SEQ_TRIG_DIST 20 -// SeqSprite seq_sprites[] = { -// // 000-015: Step note names. -// { -// .x = SEQ_POS_X, -// .y = SEQ_POS_Y, -// .obj_attr_0 = OBJ_SHAPE_WIDE, -// .obj_attr_1 = OBJ_SHAPE_SMALL, -// .obj_attr_2 = 0, -// }, -// }; +int trig_selection_loc = 1; -SeqSprite seq_sprites[16] = {0}; +SeqSprite seq_sprites[34] = {0}; void -init_sequencer_sprites() { - // Load note sprites. - note_sprites_id = load_packed_sprite_data(&sprite_note_names, 2, 73); - +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_POS_X + i * 16; + int x = SEQ_POS_X + i * SEQ_TRIG_DIST; int y = SEQ_POS_Y; if (i >= 8) { y += 32; - x -= 8 * 16; + x -= 8 * SEQ_TRIG_DIST; } seq_sprites[i].x = x; seq_sprites[i].y = y; @@ -199,18 +213,61 @@ init_sequencer_sprites() { // TODO: They should start hidden until the update function changes that. // seq_sprites[i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_HIDDEN; seq_sprites[i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); + seq_sprites[i].obj_attr_2 = sprites[sprite_id].tile_start; + } + + // Trigger boxes. + sprite_id = load_packed_sprite_data(&sprite_trigger_button, 8, 1); + for (size_t i = 0; i < 16; ++i) { + int x = SEQ_POS_X + i * SEQ_TRIG_DIST; + int y = SEQ_POS_Y; + if (i >= 8) { + y += 32; + x -= 8 * SEQ_TRIG_DIST; + } + seq_sprites[i + 16].id = obj_counter++; + seq_sprites[i + 16].obj_attr_0 = OBJ_SHAPE_TALL | OBJ_Y_COORD(y); + seq_sprites[i + 16].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); + seq_sprites[i + 16].obj_attr_2 = sprites[sprite_id].tile_start; + } + + sprite_id = load_packed_sprite_data(&sprite_trigger_active_indicator, 1, 1); + // TODO: No need for a for loop. + { + int x = SEQ_POS_X; + int y = SEQ_POS_Y + 15; + seq_sprites[32].id = obj_counter++; + seq_sprites[32].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); + seq_sprites[32].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); + seq_sprites[32].obj_attr_2 = sprites[sprite_id].tile_start | OBJ_PAL_BANK(1); + } + + sprite_id = load_packed_sprite_data(&sprite_trigger_selection, 16, 1); + // TODO: No need for a for loop. + { + int x = SEQ_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST; + int y = SEQ_POS_Y - 2; + if (trig_selection_loc >= 8) { + y += 32; + x -= 8 * SEQ_TRIG_DIST; + } + seq_sprites[33].id = obj_counter++; + 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); + seq_sprites[33].obj_attr_2 = sprites[sprite_id].tile_start | OBJ_PAL_BANK(2); } } void -update_sequencer_sprites() { +update_sequencer_sprites(void) { // 000-015: Step note names. for (size_t i = 0; i < 16; ++i) { // Each note name is made of 2 8x8 tiles (16x8). size_t base_tile = sequence_synth[i].note * 2; // TODO: Show the note name if is within the duration, hide when trigger - // is off or duration of previous note was cut short. + // is off or duration of previous note was cut short. If not triggered + // but note name appears, needs to be on a different palette bank. if (!sequence_synth[i].trigger) { seq_sprites[i].obj_attr_0 |= OBJ_HIDDEN; } else { @@ -219,10 +276,73 @@ update_sequencer_sprites() { seq_sprites[i].obj_attr_2 = base_tile; } + + // 33: Sequence indicator. + { + int x = SEQ_POS_X + step_counter * SEQ_TRIG_DIST + 3; + int y = SEQ_POS_Y + 15; + if (step_counter >= 8) { + y += 32; + x -= 8 * SEQ_TRIG_DIST; + } + 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. + } + + // 34: Trigger selection. + { + int x = SEQ_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST; + int y = SEQ_POS_Y - 2; + if (trig_selection_loc >= 8) { + y += 32; + x -= 8 * SEQ_TRIG_DIST; + } + // TODO: Mask and update instead. + 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); + } +} + +void +handle_sequencer_input(void) { + if (key_pressed(KEY_LEFT)) { + if (trig_selection_loc == 0) { + trig_selection_loc = 15; + } else { + trig_selection_loc = CLAMP(trig_selection_loc - 1, 0, 15); + } + } + 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)) { + trig_selection_loc = (trig_selection_loc + 8) % 16; + } + 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); + } + + if (key_pressed(KEY_START)) { + step_counter = 0; + set_time(bpm); + } + if (key_pressed(KEY_SELECT)) { + TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; + } } void -render_sequencer_sprites() { +render_sequencer_sprites(void) { size_t len = sizeof(seq_sprites) / sizeof(SeqSprite); for (size_t i = 0; i < len; ++i) { size_t id = seq_sprites[i].id; -- cgit v1.2.1