From 404a85e7ab39e26a5ac9d0fba470a53fd5cb959f Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 8 Jun 2021 11:21:37 +0200 Subject: Add old sequencer logic and input control --- src/main.c | 65 +------ src/rng.c | 15 ++ src/sequencer.c | 566 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 586 insertions(+), 60 deletions(-) create mode 100644 src/rng.c create mode 100644 src/sequencer.c (limited to 'src') diff --git a/src/main.c b/src/main.c index b14fbed..a322df2 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,7 @@ WITH REGARD TO THIS SOFTWARE. #include "filesystem.c" #include "renderer.c" +#include "sequencer.c" // // Config parameters. @@ -37,11 +38,14 @@ WITH REGARD TO THIS SOFTWARE. txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+1);\ txt_printf("FLIP: %lu ", flip_cycles);\ txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+2);\ + txt_printf("INPUT: %lu ", input_cycles);\ + txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+3);\ txt_printf("FRAME: %lu ", frame_counter);\ frame_counter++;\ } while (0) #define PROF_INIT() \ u32 frame_counter = 0;\ + u32 input_cycles = 0;\ u32 eval_cycles = 0;\ u32 flip_cycles = 0; #else @@ -50,66 +54,6 @@ WITH REGARD TO THIS SOFTWARE. #define PROF_INIT() #endif -#define TRIG_W 15 -#define TRIG_H 24 -#define TRIG_START_X 66 -#define TRIG_START_Y 90 -#define TRIG_OFFSET_X (TRIG_W + 4) -#define TRIG_OFFSET_Y (TRIG_H + 7) - -void -draw_triggers(void) { - for (size_t i = 0; i < 8; i++) { - size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; - size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; - size_t y0 = TRIG_START_Y; - size_t y1 = TRIG_START_Y + TRIG_H; - draw_rect(x0, y0, x1, y1, 1); - } - for (size_t i = 0; i < 8; i++) { - size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; - size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; - size_t y0 = TRIG_START_Y + TRIG_OFFSET_Y; - size_t y1 = TRIG_START_Y + TRIG_H + TRIG_OFFSET_Y; - draw_rect(x0, y0, x1, y1, 1); - } -} - -#define CHAN_START_X 35 -#define CHAN_START_Y 90 -#define CHAN_OFFSET_Y 12 - -void -draw_channels(void) { - // Contains 5 channel buttons: Ch. 1-4 + FM. We are only drawing the DMG - // channels for now, since FM may take some time to develop. - u32 channel_buttons[] = { - 0xff017111, 0x117101ff, 0xff008585, 0x879500ff, - 0x0f080808, 0x0808080f, 0xff01b989, 0x89b901ff, - 0xff004242, 0x434a00ff, 0x0f080909, 0x0909080f, - 0xff015d45, 0xc55d01ff, 0xff00a1a1, 0xa1a500ff, - 0x0f080a0a, 0x0a0a080f, 0xff015d45, 0xc55d01ff, - 0xff00a1a1, 0xa12500ff, 0x0f080a0a, 0x0a0b080f, - 0xff01c141, 0xc14101ff, 0xff00151c, 0x141400ff, - 0x0f080808, 0x0808080f, - }; - Tile channel_tiles[3 * 4] = {0}; - unpack_tiles(channel_buttons, channel_tiles, 3 * 4); - size_t k = 0; - for (size_t i = 0; i < 4; i++) { - size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; - draw_tile(CHAN_START_X, y, channel_tiles + k++, false); - draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, false); - draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, false); - } -} - -void -sequencer_init(void) { - draw_triggers(); - draw_channels(); -} - int main(void) { // Adjust system wait times. SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE; @@ -132,6 +76,7 @@ int main(void) { while (true) { bios_vblank_wait(); PROF(flip_buffer(), flip_cycles); + PROF(handle_sequencer_input(), input_cycles); PROF_SHOW(); } diff --git a/src/rng.c b/src/rng.c new file mode 100644 index 0000000..96760b8 --- /dev/null +++ b/src/rng.c @@ -0,0 +1,15 @@ +u16 rng_state; + +u32 hash16(u32 input, u32 key) { + u32 hash = input * key; + return ((hash >> 16) ^ hash) & 0xFFFF; +} + +u16 rng16() { + rng_state += 0xbadd; + return hash16(rng_state, 0x10de); +} + +u32 rng32() { + return (rng16() << 16) | rng16(); +} diff --git a/src/sequencer.c b/src/sequencer.c new file mode 100644 index 0000000..43e6ffc --- /dev/null +++ b/src/sequencer.c @@ -0,0 +1,566 @@ +#include "rng.c" + +// +// Globals. +// +typedef enum { + SEQ_SELECT_TRIGGER, + SEQ_SELECT_CHANNEL, + SEQ_SELECT_PARAMETER, +} SeqSelect; +static int bpm = 115; +static int step_counter = 0; +int trig_selection_loc = 0; +int param_selection_loc = 64; +int channel_selection_loc = 2; +SeqSelect current_selection = SEQ_SELECT_TRIGGER; + +// +// Wave data. +// + +// TODO: Make them u32. +static const u8 sine_wave[16] = { + 0x89, 0xBC, 0xDE, 0xEF, + 0xFE, 0xED, 0xCB, 0x98, + 0x76, 0x43, 0x21, 0x10, + 0x01, 0x12, 0x34, 0x67, +}; + +static const u8 saw_wave[16] = { + 0x01, 0x23, 0x45, 0x67, + 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x23, 0x45, 0x67, + 0x89, 0xab, 0xcd, 0xef, +}; + +static const u8 square_wave[16] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +// TODO: Split into individual trigger types. +typedef struct SeqTrigger { + bool trigger; + Note note; + u8 env_volume; + u8 env_time; + u8 env_direction; + u8 duty_cycle; + u8 sweep_number; + u8 sweep_time; + u8 sweep_direction; + u8 wave_volume; + u8 wave_mode; + u8 wave_a[16]; + u8 wave_b[16]; +} SeqTrigger; + +static SeqTrigger sequences[3][16] = { + // Synth 1. + { + {false, NOTE_C_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_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}}, + {false, NOTE_G_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_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}}, + {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}}, + {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}}, + {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}}, + }, + // 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}}, + }, + // Synth 3. + { + {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, + }, +}; + + +#define TRIG_W 15 +#define TRIG_H 24 +#define TRIG_START_X 66 +#define TRIG_START_Y 90 +#define TRIG_OFFSET_X (TRIG_W + 4) +#define TRIG_OFFSET_Y (TRIG_H + 7) + +void +draw_triggers(void) { + for (size_t i = 0; i < 8; i++) { + size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; + size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; + size_t y0 = TRIG_START_Y; + size_t y1 = TRIG_START_Y + TRIG_H; + draw_rect(x0, y0, x1, y1, 1); + } + for (size_t i = 0; i < 8; i++) { + size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; + size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; + size_t y0 = TRIG_START_Y + TRIG_OFFSET_Y; + size_t y1 = TRIG_START_Y + TRIG_H + TRIG_OFFSET_Y; + draw_rect(x0, y0, x1, y1, 1); + } +} + +#define CHAN_START_X 35 +#define CHAN_START_Y 90 +#define CHAN_OFFSET_Y 12 + +void +draw_channels(void) { + // Contains 5 channel buttons: Ch. 1-4 + FM. We are only drawing the DMG + // channels for now, since FM may take some time to develop. + u32 channel_buttons[] = { + 0xff017111, 0x117101ff, 0xff008585, 0x879500ff, + 0x0f080808, 0x0808080f, 0xff01b989, 0x89b901ff, + 0xff004242, 0x434a00ff, 0x0f080909, 0x0909080f, + 0xff015d45, 0xc55d01ff, 0xff00a1a1, 0xa1a500ff, + 0x0f080a0a, 0x0a0a080f, 0xff015d45, 0xc55d01ff, + 0xff00a1a1, 0xa12500ff, 0x0f080a0a, 0x0a0b080f, + 0xff01c141, 0xc14101ff, 0xff00151c, 0x141400ff, + 0x0f080808, 0x0808080f, + }; + Tile channel_tiles[3 * 4] = {0}; + unpack_tiles(channel_buttons, channel_tiles, 3 * 4); + size_t k = 0; + for (size_t i = 0; i < 4; i++) { + size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; + draw_tile(CHAN_START_X, y, channel_tiles + k++, false); + draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, false); + draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, false); + } +} + +void +irq_timer_0(void) { + Note active_note; + { + 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); + SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET + | sound_rates[active_note]; + } + } + { + 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); + SOUND_SQUARE2_FREQ = SOUND_FREQ_RESET + | sound_rates[active_note]; + } + } + { + SeqTrigger *trig = &sequences[2][step_counter]; + active_note = trig->note; + if (trig->trigger) { + // 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); + memcpy(SOUND_WAVE_RAM, trig->wave_a, 16); + SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); + memcpy(SOUND_WAVE_RAM, trig->wave_b, 16); + + switch (trig->wave_mode) { + case 0: { + SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) + | SOUND_WAVE_BANK_SELECT(0); + } break; + case 1: { + SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) + | SOUND_WAVE_BANK_SELECT(1); + } break; + case 2: { + SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) + | SOUND_WAVE_BANK_SELECT(0); + } break; + case 3: { + SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) + | SOUND_WAVE_BANK_SELECT(1); + } break; + } + SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; + + switch (trig->wave_volume) { + case 0: { + SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; + } break; + case 1: { + SOUND_WAVE_CTRL = SOUND_WAVE_VOL_25; + } break; + case 2: { + SOUND_WAVE_CTRL = SOUND_WAVE_VOL_50; + } break; + case 3: { + SOUND_WAVE_CTRL = SOUND_WAVE_VOL_75; + } break; + case 4: { + SOUND_WAVE_CTRL = SOUND_WAVE_VOL_100; + } break; + } + SOUND_WAVE_FREQ = SOUND_FREQ_RESET + | sound_rates[active_note]; + } + } + step_counter = (step_counter + 1) % 16; +} + +#define SEQ_N_CHANNELS 3 + +void +handle_sequencer_input(void) { + poll_keys(); + SeqTrigger *trig = &sequences[channel_selection_loc][trig_selection_loc]; + if (current_selection == SEQ_SELECT_TRIGGER) { + if (key_tap(KEY_LEFT)) { + if (trig_selection_loc == 0 || trig_selection_loc == 8) { + current_selection = SEQ_SELECT_CHANNEL; + } else { + trig_selection_loc = MAX(trig_selection_loc - 1, 0); + } + } else if (key_tap(KEY_RIGHT)) { + if (trig_selection_loc != 7) { + trig_selection_loc = MIN(trig_selection_loc + 1, 15); + } + } else if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { + trig_selection_loc = (trig_selection_loc + 8) % 16; + } else if (key_tap(KEY_B)) { + trig->trigger ^= 1; + } else if (key_tap(KEY_L)) { + if (trig->trigger) { + trig->note = MAX(trig->note - 1, NOTE_C_2); + } + } else if (key_tap(KEY_R)) { + if (trig->trigger) { + trig->note = MIN( trig->note + 1, NOTE_C_8); + } + } else if (key_tap(KEY_A)) { + // Switch to parameter selection. + current_selection = SEQ_SELECT_PARAMETER; + } + } else if (current_selection == SEQ_SELECT_PARAMETER) { + if (channel_selection_loc < 2) { + // Move through the selected synth parameters. + if (key_tap(KEY_LEFT)) { + int max_param = 6; + if (channel_selection_loc == 1) { + max_param = 3; + } + if (param_selection_loc == 0) { + param_selection_loc = max_param; + } else { + param_selection_loc = MAX(param_selection_loc - 1, 0); + } + } + if (key_tap(KEY_RIGHT)) { + int max_param = 6; + if (channel_selection_loc == 1) { + max_param = 3; + } + if (param_selection_loc == max_param) { + param_selection_loc = 0; + } else { + param_selection_loc = MIN(param_selection_loc + 1, max_param); + } + } + + // Adjust the parameters up or down. + if (key_tap(KEY_L) || key_tap(KEY_R)) { + int inc; + if (key_tap(KEY_L)) { + inc = -1; + } else { + inc = 1; + } + switch (param_selection_loc) { + case 0: { + trig->env_volume = CLAMP(trig->env_volume + inc, 0, 15); + } break; + case 1: { + trig->env_time = CLAMP(trig->env_time + inc, 0, 7); + } break; + case 2: { + trig->env_direction ^= 1; + } break; + case 3: { + trig->duty_cycle = CLAMP(trig->duty_cycle + inc, 0, 3); + } break; + case 4: { + trig->sweep_number = CLAMP(trig->sweep_number + inc, 0, 7); + } break; + case 5: { + trig->sweep_time = CLAMP(trig->sweep_time + inc, 0, 7); + } break; + case 6: { + if (trig->sweep_direction == 0) { + trig->sweep_direction = 1; + } else { + trig->sweep_direction = 0; + } + } break; + } + } + } else if (channel_selection_loc == 2) { + if (key_tap(KEY_LEFT) || key_tap(KEY_RIGHT)) { + int inc = 0; + int loc = param_selection_loc; + if (key_tap(KEY_RIGHT)) { + if (loc == 15 || loc == 31) { + inc = 17; + } else if (loc != 47 && loc != 63){ + inc = 1; + } + } else { + if (loc == 32 || loc == 48) { + inc = -17; + } else if (loc != 16 && loc != 64){ + inc = -1; + } + } + param_selection_loc = CLAMP(loc + inc, 0, 71); + } + if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { + int inc = 0; + int loc = param_selection_loc; + if (key_tap(KEY_UP)) { + if ((loc >= 16 && loc < 32) || (loc >= 48 && loc < 64)) { + inc = -16; + } else if (loc == 64) { + inc = -48; + } else if (loc == 65) { + inc = -45; + } else if (loc == 66) { + inc = -42; + } else if (loc == 67) { + inc = -39; + } else if (loc == 68) { + inc = -20; + } else if (loc == 69) { + inc = -17; + } else if (loc == 70) { + inc = -14; + } else if (loc == 71) { + inc = -11; + } + } else { + if (loc < 16 || (loc >= 32 && loc < 48)) { + inc = 16; + } else if (loc >= 16 && loc <= 19){ + inc = 48 - (loc - 16); + } else if (loc >= 20 && loc <= 23){ + inc = 45 - (loc - 20); + } else if (loc >= 24 && loc <= 27){ + inc = 42 - (loc - 24); + } else if (loc >= 28 && loc <= 31){ + inc = 39 - (loc - 28); + } else if (loc >= 48 && loc <= 51){ + inc = 20 - (loc - 48); + } else if (loc >= 52 && loc <= 55){ + inc = 17 - (loc - 52); + } else if (loc >= 56 && loc <= 59){ + inc = 14 - (loc - 56); + } else if (loc >= 60 && loc <= 63){ + inc = 11 - (loc - 60); + } + } + param_selection_loc = CLAMP(loc + inc, 0, 71); + } + if (key_tap(KEY_R) || key_tap(KEY_L)) { + int odd = param_selection_loc & 0x1; + int inc; + if (key_tap(KEY_R)) { + inc = 1; + } else { + inc = -1; + } + // Wave: AA BB CC DD ... + // ^^ + // |`- odd + // `-- even + if (param_selection_loc < 32) { + u8 byte_number = param_selection_loc >> 1; + u8 byte = sequences[2][trig_selection_loc].wave_a[byte_number]; + if (odd) { + byte = (~0xF & byte) | ((byte + inc) & 0xF); + } else { + byte = (0xF & byte) | (((byte >> 4) + inc) & 0xF) << 4; + } + sequences[2][trig_selection_loc].wave_a[byte_number] = byte; + } else if (param_selection_loc < 64){ + u8 byte_number = (param_selection_loc - 32) >> 1; + u8 byte = sequences[2][trig_selection_loc].wave_b[byte_number]; + if (odd) { + byte = (~0xF & byte) | (byte + inc); + } else { + byte = (0xF & byte) | ((byte >> 4) + inc) << 4; + } + sequences[2][trig_selection_loc].wave_b[byte_number] = byte; + } else if (param_selection_loc == 64){ + memcpy(&trig->wave_a, &sine_wave, 16); + } else if (param_selection_loc == 65){ + memcpy(&trig->wave_a, &saw_wave, 16); + } else if (param_selection_loc == 66){ + memcpy(&trig->wave_a, &square_wave, 16); + } else if (param_selection_loc == 67){ + u32 rand_wave[4] = { + rng32(), rng32(), rng32(), rng32(), + }; + memcpy(&trig->wave_a, &rand_wave, 16); + } else if (param_selection_loc == 68){ + memcpy(&trig->wave_b, &sine_wave, 16); + } else if (param_selection_loc == 69){ + memcpy(&trig->wave_b, &saw_wave, 16); + } else if (param_selection_loc == 70){ + memcpy(&trig->wave_b, &square_wave, 16); + } else if (param_selection_loc == 71){ + u32 rand_wave[4] = { + rng32(), rng32(), rng32(), rng32(), + }; + memcpy(&trig->wave_b, &rand_wave, 16); + } + } + } + + // Go back to trigger selection. + if (key_tap(KEY_A)) { + current_selection = SEQ_SELECT_TRIGGER; + } + + // Enable disable trigger. + if (key_tap(KEY_B)) { + trig->trigger ^= 1; + } + } else if (current_selection == SEQ_SELECT_CHANNEL) { + if (key_tap(KEY_RIGHT)) { + current_selection = SEQ_SELECT_TRIGGER; + trig_selection_loc = 0; + param_selection_loc = 0; + } + if (key_tap(KEY_UP)) { + if (channel_selection_loc == 0) { + channel_selection_loc = SEQ_N_CHANNELS - 1; + } else { + channel_selection_loc = MAX(channel_selection_loc - 1, 0); + } + } + if (key_tap(KEY_DOWN)) { + if (channel_selection_loc == SEQ_N_CHANNELS - 1) { + channel_selection_loc = 0; + } else { + channel_selection_loc = MIN(channel_selection_loc + 1, SEQ_N_CHANNELS); + } + } + } + + if (key_tap(KEY_START)) { + step_counter = 0; + if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) == 0) { + set_time(bpm); + } else { + TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; + SOUND_SQUARE1_CTRL = 0; + SOUND_SQUARE2_CTRL = 0; + SOUND_WAVE_CTRL = 0; + } + } + if (key_tap(KEY_SELECT)) { + TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; + SOUND_SQUARE1_CTRL = 0; + SOUND_SQUARE2_CTRL = 0; + SOUND_WAVE_CTRL = 0; + } + + if (key_tap(KEY_LEFT) + || key_tap(KEY_RIGHT) + || key_tap(KEY_UP) + || key_tap(KEY_DOWN) + || key_tap(KEY_L) + || key_tap(KEY_R) + ) { + } +} + +void +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 + // We have to operate on integer values, so the numbers have been + // precalculated to `n_ticks = 244181 / bmp` + int n_ticks = -244181 / bpm; + irs_set(IRQ_TIMER_0, irq_timer_0); + TIMER_DATA_0 = n_ticks; + TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; +} + +void +sequencer_init(void) { + // Initialize background objects and sprites. + draw_triggers(); + draw_channels(); + + // Initialize sound system. + SOUND_STATUS = SOUND_ENABLE; + SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 + | SOUND_SQUARE2 + | SOUND_WAVE, 3); + SOUND_DSOUND_MASTER = SOUND_DMG25; + set_time(bpm); +} -- cgit v1.2.1