From 400b50baed8125a482895fa7534526c40cfd97af Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Mon, 3 May 2021 15:53:48 +0200 Subject: Add sweep in UI and parameter controls --- src/common.h | 9 +- src/main.c | 5 - src/sequencer.c | 440 +++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 376 insertions(+), 78 deletions(-) diff --git a/src/common.h b/src/common.h index b688711..d78f11a 100644 --- a/src/common.h +++ b/src/common.h @@ -105,7 +105,7 @@ rgb15(u32 red, u32 green, u32 blue ) { #define COLOR_RED rgb15(31, 0, 12) #define COLOR_BLUE rgb15(2, 15, 30) #define COLOR_CYAN rgb15(0, 30, 30) -#define COLOR_GREY rgb15(4, 4, 4) +#define COLOR_GREY rgb15(12, 12, 12) #define COLOR_BLACK rgb15(0, 0, 0) #define COLOR_WHITE rgb15(28, 28, 28) @@ -594,9 +594,14 @@ sound_volume(SoundChannel channels, u8 volume) { #define SOUND_SQUARE_LEN(N) (N) #define SOUND_SQUARE_DUTY(N) ((N) << 0x6) #define SOUND_SQUARE_ENV_TIME(N) ((N) << 0x8) -#define SOUND_SQUARE_ENV_INC(N) ((N) << 0xB) +#define SOUND_SQUARE_ENV_DIR(N) ((N) << 0xB) #define SOUND_SQUARE_ENV_VOL(N) ((N) << 0xC) +// DMG square 1 sweep control bits. +#define SOUND_SWEEP_NUMBER(N) (N) +#define SOUND_SWEEP_DIR(N) ((N) << 0x3) +#define SOUND_SWEEP_TIME(N) ((N) << 0x4) + // DMG square frequency bits. #define SOUND_SQUARE_TIMED (1 << 0xE) #define SOUND_SQUARE_RESET (1 << 0xF) diff --git a/src/main.c b/src/main.c index d64208e..b93abe6 100644 --- a/src/main.c +++ b/src/main.c @@ -20,11 +20,6 @@ int main(void) { // sequential. DISP_CTRL = DISP_ENABLE_SPRITES | DISP_MODE_0 | DISP_BG_0; - // 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(); // Initialize text engine. diff --git a/src/sequencer.c b/src/sequencer.c index 5db1b5d..b55ceca 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -101,7 +101,7 @@ u32 sprite_env_label_volume[] = { 0xaaeaaaaa, 0xae000000, 0x0e020602, 0x0e000000, }; -u32 sprite_env_label_time[] = { +u32 sprite_env_sweep_label_time[] = { 0x00000000, 0x00000000, 0xee444444, 0xe4000000, 0xea2e6a2a, 0xea000000, 0x00000000, 0x00000000, }; @@ -116,6 +116,16 @@ u32 sprite_env_label_env[] = { 0x62a2a2a2, 0xee000000, 0xee2a6e22, 0xe2000000, }; +u32 sprite_sweep_number_label[] = { + 0x60a0a0a0, 0xa0000000, 0xaaeaaaaa, 0xae000000, + 0xee2a662a, 0xee000000, 0x0e0a060a, 0x0a000000, +}; + +u32 sprite_sweep_label[] = { + 0x8040c000, 0xc0000000, 0xa5a4a5b5, 0xa8000000, + 0xbb889988, 0xbb000000, 0x03020300, 0x00000000, +}; + u32 sprite_duty_cycle_label[] = { 0x00000000, 0x00000080, 0xa6aaaaaa, 0xe60000ab, 0xaea4e444, 0x4400008b, 0x00000000, 0x00000003, @@ -195,6 +205,25 @@ u32 sprite_env_sweep_direction[] = { 0xb2b2b2b2, 0xb2ba9400, 0x0d0f0e0c, 0x0c0c0c00, }; +u32 sprite_sweep_time_number[] = { + 0x7c646464, 0x64647c00, 0x18181818, 0x18181800, + 0x7c60607c, 0x0c0c7c00, 0x7c606078, 0x60607c00, + 0x60706864, 0x7c606000, 0x7c0c0c7c, 0x60607c00, + 0x7c0c0c7c, 0x4c4c7c00, 0x7c606030, 0x180c0c00, +}; + +u32 sprite_synth_param_selector[] = { + 0x07010100, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xe0808000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000101, 0x07000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00008080, 0xe0000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + + typedef struct SeqTrigger { bool trigger; Note note; @@ -202,29 +231,32 @@ typedef struct SeqTrigger { u8 env_time; u8 env_direction; u8 duty_cycle; + u8 sweep_number; + u8 sweep_time; + u8 sweep_direction; // TODO: ... } SeqTrigger; static SeqTrigger sequence_synth[] = { - {true , NOTE_D_4 , 0, 0, 0, 0}, - {true , NOTE_F_4 , 1, 1, 0, 0}, - {true , NOTE_A_4 , 2, 2, 0, 1}, - {true , NOTE_C_5 , 3, 3, 0, 1}, - - {true , NOTE_D_4 , 4, 4, 1, 2}, - {false , NOTE_C_SHARP_4 , 5, 5, 1, 2}, - {false , NOTE_D_4 , 6, 6, 1, 3}, - {false , NOTE_D_4 , 7, 7, 1, 3}, - - {true , NOTE_D_4 , 8, 0, 0, 0}, - {true , NOTE_F_4 , 9, 1, 0, 0}, - {true , NOTE_A_4 , 10, 2, 0, 1}, - {true , NOTE_C_5 , 11, 3, 0, 1}, - - {true , NOTE_D_4 , 12, 4, 1, 2}, - {false , NOTE_D_4 , 13, 5, 1, 2}, - {true , NOTE_A_4 , 14, 6, 1, 3}, - {false , NOTE_A_5 , 15, 7, 1, 3}, + {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 int bpm = 120; @@ -236,11 +268,15 @@ irq_timer_0(void) { SeqTrigger *trig = &sequence_synth[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_INC(trig->env_direction) + | SOUND_SQUARE_ENV_DIR(trig->env_direction) | SOUND_SQUARE_DUTY(trig->duty_cycle); - SOUND_SQUARE1_FREQ = SOUND_SQUARE_RESET | sound_rates[active_note]; + SOUND_SQUARE1_FREQ = SOUND_SQUARE_RESET + | sound_rates[active_note]; } step_counter = (step_counter + 1) % 16; } @@ -250,7 +286,7 @@ set_time(int bpm) { // The number of ticks of a 1024 cycle clock in a step based on the BPM can // be calculated as: // X bpm -> 60000 / 4 / bpm = Y ms = Ye-3 s - // Y ms -> Ye-3 / 59.99e-9 /1024 = Z ticks + // Y ms -> Ye-3 / 59.99e-9 / 1024 = Z ticks // We have to operate on integer values, so the numbers have been // precalculated to `n_ticks = 244181 / bmp` int n_ticks = -244181 / bpm; @@ -291,6 +327,14 @@ set_time(int bpm) { // - 040-040 envelope: envelope label. // - 041-041 duty-cycle label. // - 042-042 duty-cycle indicator. +// - 043-043 sweep: number label. +// - 044-044 sweep: number indicator. +// - 045-045 sweep: time label. +// - 046-046 sweep: time indicator. +// - 047-047 sweep: direction label. +// - 048-048 sweep: direction indicator. +// - 049-049 sweep: sweep label. +// - 050-050 synth parameter selector. // @@ -300,9 +344,11 @@ set_time(int bpm) { #define SEQ_TRIG_DIST 20 #define SEQ_ENV_POS_X 10 #define SEQ_ENV_POS_Y 10 -#define SEQ_ENV_DIST 34 +#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; @@ -316,12 +362,27 @@ typedef struct SeqSprite { u16 obj_attr_2; } SeqSprite; -int trig_selection_loc = 1; -SeqSprite seq_sprites[43] = {0}; +typedef enum { + SEQ_SELECT_TRIGGER, + SEQ_SELECT_PARAMETER, +} SeqSelect; + +int trig_selection_loc = 0; +int param_selection_loc = 0; +SeqSelect current_selection = SEQ_SELECT_TRIGGER; + +SeqSprite seq_sprites[51] = {0}; void init_sequencer_sprites(void) { + // Load palette. + init_sprite_pal(0, COLOR_WHITE); + init_sprite_pal(16, COLOR_CYAN); + init_sprite_pal(32, COLOR_RED); + init_sprite_pal(48, COLOR_GREY); + init_sprites(0); + // Sprite note names. size_t sprite_id = load_packed_sprite_data(&sprite_note_names, 2, 73); for (size_t i = 0; i < 16; ++i) { @@ -396,7 +457,7 @@ init_sequencer_sprites(void) { seq_sprites[34].base_tile = base_tile; seq_sprites[34].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[34].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[34].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[34].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_volume, 4, 16); @@ -408,10 +469,10 @@ init_sequencer_sprites(void) { seq_sprites[35].base_tile = base_tile; seq_sprites[35].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[35].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[35].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[35].obj_attr_2 = base_tile; } - sprite_id = load_packed_sprite_data(&sprite_env_label_time, 4, 1); + sprite_id = load_packed_sprite_data(&sprite_env_sweep_label_time, 4, 1); { int x = SEQ_ENV_POS_X + SEQ_ENV_DIST; int y = SEQ_ENV_POS_Y; @@ -420,7 +481,17 @@ init_sequencer_sprites(void) { seq_sprites[36].base_tile = base_tile; seq_sprites[36].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[36].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[36].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[36].obj_attr_2 = base_tile; + } + { + int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST; + int y = SEQ_SWEEP_POS_Y; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[45].id = obj_counter++; + seq_sprites[45].base_tile = base_tile; + seq_sprites[45].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[45].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[45].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_time, 4, 8); @@ -432,7 +503,7 @@ init_sequencer_sprites(void) { seq_sprites[37].base_tile = base_tile; seq_sprites[37].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[37].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[37].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[37].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_label_direction, 4, 1); @@ -444,7 +515,17 @@ init_sequencer_sprites(void) { seq_sprites[38].base_tile = base_tile; seq_sprites[38].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[38].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[38].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[38].obj_attr_2 = base_tile; + } + { + int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST * 2; + int y = SEQ_SWEEP_POS_Y; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[47].id = obj_counter++; + seq_sprites[47].base_tile = base_tile; + seq_sprites[47].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[47].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[47].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_sweep_direction, 4, 2); @@ -456,19 +537,29 @@ init_sequencer_sprites(void) { seq_sprites[39].base_tile = base_tile; seq_sprites[39].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[39].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[39].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[39].obj_attr_2 = base_tile; + } + { + int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST * 2; + int y = SEQ_SWEEP_POS_Y + 8; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[48].id = obj_counter++; + seq_sprites[48].base_tile = base_tile; + seq_sprites[48].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[48].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[48].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_env_label_env, 4, 1); { int x = SEQ_ENV_POS_X + SEQ_ENV_DIST; - int y = SEQ_ENV_POS_Y - 8; + int y = SEQ_ENV_POS_Y - 10; int base_tile = sprites[sprite_id].tile_start; seq_sprites[40].id = obj_counter++; seq_sprites[40].base_tile = base_tile; seq_sprites[40].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[40].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[40].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[40].obj_attr_2 = base_tile; } sprite_id = load_packed_sprite_data(&sprite_duty_cycle_label, 8, 1); @@ -480,8 +571,9 @@ init_sequencer_sprites(void) { seq_sprites[41].base_tile = base_tile; seq_sprites[41].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[41].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); - seq_sprites[41].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[41].obj_attr_2 = base_tile; } + sprite_id = load_packed_sprite_data(&sprite_duty_cycle, 4, 4); { int x = SEQ_DUTYCYCLE_POS_X; @@ -491,7 +583,65 @@ init_sequencer_sprites(void) { seq_sprites[42].base_tile = base_tile; seq_sprites[42].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); seq_sprites[42].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); - seq_sprites[42].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + seq_sprites[42].obj_attr_2 = base_tile; + } + + sprite_id = load_packed_sprite_data(&sprite_sweep_number_label, 4, 1); + { + int x = SEQ_SWEEP_POS_X; + int y = SEQ_SWEEP_POS_Y; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[43].id = obj_counter++; + seq_sprites[43].base_tile = base_tile; + seq_sprites[43].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[43].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[43].obj_attr_2 = base_tile; + } + + sprite_id = load_packed_sprite_data(&sprite_sweep_time_number, 1, 8); + { + int x = SEQ_SWEEP_POS_X + 12; + int y = SEQ_SWEEP_POS_Y + 8; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[44].id = obj_counter++; + seq_sprites[44].base_tile = base_tile; + seq_sprites[44].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); + seq_sprites[44].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); + seq_sprites[44].obj_attr_2 = base_tile; + } + { + int x = SEQ_SWEEP_POS_X + 12 + SEQ_ENV_DIST; + int y = SEQ_SWEEP_POS_Y + 8; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[46].id = obj_counter++; + seq_sprites[46].base_tile = base_tile; + seq_sprites[46].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); + seq_sprites[46].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); + seq_sprites[46].obj_attr_2 = base_tile; + } + + sprite_id = load_packed_sprite_data(&sprite_sweep_label, 4, 1); + { + int x = SEQ_SWEEP_POS_X + SEQ_ENV_DIST; + int y = SEQ_SWEEP_POS_Y - 10; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[49].id = obj_counter++; + seq_sprites[49].base_tile = base_tile; + seq_sprites[49].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); + seq_sprites[49].obj_attr_1 = OBJ_SIZE_MID | OBJ_X_COORD(x); + seq_sprites[49].obj_attr_2 = base_tile; + } + + sprite_id = load_packed_sprite_data(&sprite_synth_param_selector, 16, 1); + { + int x = SEQ_ENV_POS_X + 1; + int y = SEQ_ENV_POS_Y - 3; + int base_tile = sprites[sprite_id].tile_start; + seq_sprites[50].id = obj_counter++; + seq_sprites[50].base_tile = base_tile; + seq_sprites[50].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); + 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); } } @@ -534,79 +684,227 @@ update_sequencer_sprites(void) { 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); + int base_tile = seq_sprites[33].base_tile; + if (current_selection == SEQ_SELECT_TRIGGER) { + seq_sprites[33].obj_attr_2 = base_tile | OBJ_PAL_BANK(2); + } else { + seq_sprites[33].obj_attr_2 = base_tile | OBJ_PAL_BANK(3); + } } - // 36: Envelope initial volume + // 36: Envelope initial volume. { size_t tile_diff = sequence_synth[trig_selection_loc].env_volume * 4; size_t base_tile = seq_sprites[35].base_tile; size_t tile = base_tile + tile_diff; - seq_sprites[35].obj_attr_2 = tile | OBJ_PAL_BANK(2); + seq_sprites[35].obj_attr_2 = tile; } - // 38: Envelope initial volume + // 38: Envelope time. { size_t tile_diff = sequence_synth[trig_selection_loc].env_time * 4; size_t base_tile = seq_sprites[37].base_tile; size_t tile = base_tile + tile_diff; - seq_sprites[37].obj_attr_2 = tile | OBJ_PAL_BANK(2); + seq_sprites[37].obj_attr_2 = tile; } - // 40: Envelope initial volume + // 40: Envelope direction. { - size_t tile_diff = sequence_synth[trig_selection_loc].env_direction * 4; + size_t tile_diff = 0; + if (sequence_synth[trig_selection_loc].env_direction == 0) { + tile_diff = 4; + } size_t base_tile = seq_sprites[39].base_tile; size_t tile = base_tile + tile_diff; - seq_sprites[39].obj_attr_2 = tile | OBJ_PAL_BANK(2); + seq_sprites[39].obj_attr_2 = tile; } - // 43: Envelope initial volume + // 43: Duty cycle. { size_t tile_diff = sequence_synth[trig_selection_loc].duty_cycle * 4; size_t base_tile = seq_sprites[42].base_tile; size_t tile = base_tile + tile_diff; - seq_sprites[42].obj_attr_2 = tile | OBJ_PAL_BANK(2); + seq_sprites[42].obj_attr_2 = tile; + } + + // 45: Sweep number. + { + size_t tile_diff = sequence_synth[trig_selection_loc].sweep_number; + size_t base_tile = seq_sprites[44].base_tile; + size_t tile = base_tile + tile_diff; + seq_sprites[44].obj_attr_2 = tile; + } + + // 47: Sweep time. + { + size_t tile_diff = sequence_synth[trig_selection_loc].sweep_time; + size_t base_tile = seq_sprites[46].base_tile; + size_t tile = base_tile + tile_diff; + seq_sprites[46].obj_attr_2 = tile; + } + + // 49: Sweep direction. + { + size_t tile_diff = sequence_synth[trig_selection_loc].sweep_direction * 4; + size_t base_tile = seq_sprites[48].base_tile; + size_t tile = base_tile + tile_diff; + seq_sprites[48].obj_attr_2 = tile; + } + + // 51: Parameter selector. + { + int x = SEQ_ENV_POS_X + 1 + (param_selection_loc % 7) * SEQ_ENV_DIST; + int y = SEQ_ENV_POS_Y - 2; + if (param_selection_loc >= 8) { + y += 32; + x -= 8 * SEQ_TRIG_DIST; + } + int hidden = 0; + if (current_selection != SEQ_SELECT_PARAMETER) { + hidden = OBJ_HIDDEN; + } + 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); } } 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 (current_selection == SEQ_SELECT_TRIGGER) { + 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_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); + } + + // Switch to parameter selection. + if (key_pressed(KEY_A)) { + current_selection = SEQ_SELECT_PARAMETER; + } + } else if (current_selection == SEQ_SELECT_PARAMETER) { + // Move through the selected synth parameters. + if (key_pressed(KEY_LEFT)) { + if (param_selection_loc == 0) { + param_selection_loc = 6; + } else { + param_selection_loc = CLAMP(param_selection_loc - 1, 0, 6); + } + } + if (key_pressed(KEY_RIGHT)) { + if (param_selection_loc == 6) { + param_selection_loc = 0; + } else { + param_selection_loc = CLAMP(param_selection_loc + 1, 0, 6); + } + } + + // Adjust the parameters up or down. + if (key_pressed(KEY_L)) { + switch (param_selection_loc) { + case 0: { + sequence_synth[trig_selection_loc].env_volume = CLAMP( + sequence_synth[trig_selection_loc].env_volume - 1, 0, 15); + } break; + case 1: { + sequence_synth[trig_selection_loc].env_time = CLAMP( + sequence_synth[trig_selection_loc].env_time - 1, 0, 7); + } break; + case 2: { + sequence_synth[trig_selection_loc].env_direction ^= 1; + } break; + case 3: { + sequence_synth[trig_selection_loc].duty_cycle = CLAMP( + sequence_synth[trig_selection_loc].duty_cycle - 1, 0, 3); + } break; + case 4: { + sequence_synth[trig_selection_loc].sweep_number = CLAMP( + sequence_synth[trig_selection_loc].sweep_number - 1, 0, 7); + } break; + case 5: { + sequence_synth[trig_selection_loc].sweep_time = CLAMP( + sequence_synth[trig_selection_loc].sweep_time - 1, 0, 7); + } break; + case 6: { + if (sequence_synth[trig_selection_loc].sweep_direction == 0) { + sequence_synth[trig_selection_loc].sweep_direction = 1; + } else { + sequence_synth[trig_selection_loc].sweep_direction = 0; + } + } break; + } + } + if (key_pressed(KEY_R)) { + switch (param_selection_loc) { + case 0: { + sequence_synth[trig_selection_loc].env_volume = CLAMP( + sequence_synth[trig_selection_loc].env_volume + 1, 0, 15); + } break; + case 1: { + sequence_synth[trig_selection_loc].env_time = CLAMP( + sequence_synth[trig_selection_loc].env_time + 1, 0, 7); + } break; + case 2: { + sequence_synth[trig_selection_loc].env_direction ^= 1; + } break; + case 3: { + sequence_synth[trig_selection_loc].duty_cycle = CLAMP( + sequence_synth[trig_selection_loc].duty_cycle + 1, 0, 3); + } break; + case 4: { + sequence_synth[trig_selection_loc].sweep_number = CLAMP( + sequence_synth[trig_selection_loc].sweep_number + 1, 0, 7); + } break; + case 5: { + sequence_synth[trig_selection_loc].sweep_time = CLAMP( + sequence_synth[trig_selection_loc].sweep_time + 1, 0, 7); + } break; + case 6: { + sequence_synth[trig_selection_loc].sweep_direction ^= 1; + } break; + } + } + + // Go back to trigger selection. + if (key_pressed(KEY_A)) { + current_selection = SEQ_SELECT_TRIGGER; } - } - 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 ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) == 0) { + set_time(bpm); + } else { + TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; + SOUND_SQUARE1_CTRL = 0; + } } if (key_pressed(KEY_SELECT)) { TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; + SOUND_SQUARE1_CTRL = 0; } } -- cgit v1.2.1