#include "rng.c" #include "text.h" #include "assets.c" #include "patterns.c" #include "save.c" #include "drawing.c" #include "clipboard.c" void set_time(int bpm); bool redraw_pattern_buttons = true; bool redraw_play_pause = true; bool redraw_trigs = true; bool redraw_channels = true; bool redraw_bank_buttons = true; bool redraw_params = true; bool redraw_bpm = true; bool redraw_piano_note = true; bool update_bpm = false; void gate_off(void) { SIO_MODE = SIO_MODE_GP | SIO_SC_OUT(1) | SIO_SD_OUT(1) | SIO_SI_OUT(0) | SIO_SO_OUT(1) | SIO_SC(0) | SIO_SD(1) | SIO_SO(0); TIMER_CTRL_1 = 0; } void gate_on(void) { SIO_MODE = SIO_MODE_GP | SIO_SC_OUT(1) | SIO_SD_OUT(1) | SIO_SI_OUT(0) | SIO_SO_OUT(1) | SIO_SC(1) | SIO_SD(1) | SIO_SO(0); int n_ticks = -244181 / 600; irs_set(IRQ_TIMER_1, gate_off); TIMER_DATA_1 = n_ticks; TIMER_CTRL_1 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; } void play_step(void) { Pattern *pat = &patterns[current_pattern]; if (current_pattern != next_pattern && step_counter == 15) { current_pattern = next_pattern; redraw_pattern_buttons = true; update_bpm = true; } 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) | SOUND_SWEEP_TIME(params->sweep_time); SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(params->env_volume) | SOUND_SQUARE_ENV_TIME(params->env_time) | SOUND_SQUARE_ENV_DIR(params->env_direction) | SOUND_SQUARE_DUTY(params->duty_cycle); SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET | sound_rates[trig->note]; } } else { SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE1_FREQ = 0; } 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) | SOUND_SQUARE_ENV_DIR(params->env_direction) | SOUND_SQUARE_DUTY(params->duty_cycle); SOUND_SQUARE2_FREQ = SOUND_FREQ_RESET | sound_rates[trig->note]; } } else { SOUND_SQUARE2_CTRL = 0; SOUND_SQUARE2_FREQ = 0; } 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: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); memcpy32(SOUND_WAVE_RAM, params->wave_a, 16); SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | SOUND_WAVE_BANK_SELECT(0); } break; case 1: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); memcpy32(SOUND_WAVE_RAM, params->wave_b, 16); SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | SOUND_WAVE_BANK_SELECT(1); } break; case 2: { SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); memcpy32(SOUND_WAVE_RAM, params->wave_b, 16); SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); memcpy32(SOUND_WAVE_RAM, params->wave_a, 16); SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) | SOUND_WAVE_BANK_SELECT(0); } break; } SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; switch (params->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[trig->note]; } } else { SOUND_WAVE_CTRL = 0; SOUND_WAVE_FREQ = 0; } if (pat->ch4.active) { TriggerNote *trig = &pat->ch4.notes[step_counter]; ChannelNoiseParams *params = &pat->ch4.params[step_counter]; if (trig->active) { static const u8 div_freq[] = { 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const u8 pre_freq[] = { 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; SOUND_NOISE_CTRL = SOUND_NOISE_ENV_VOL(params->env_volume) | SOUND_NOISE_ENV_TIME(params->env_time) | SOUND_NOISE_ENV_DIR(params->env_direction); SOUND_NOISE_FREQ = SOUND_FREQ_RESET | SOUND_NOISE_PRESTEP_FREQ(pre_freq[trig->note]) | SOUND_NOISE_DIV_FREQ(div_freq[trig->note]) | SOUND_NOISE_COUNTER_STAGE(params->bit_mode) | SOUND_NOISE_TIMED_MODE(0); } } else { SOUND_NOISE_CTRL = 0; SOUND_NOISE_FREQ = 0; } switch (settings.sync) { case SYNC_OUT_LINK_16: { gate_on(); } break; case SYNC_OUT_LINK_8: { if (step_counter % 2 == 0) { gate_on(); } } break; case SYNC_OUT_LINK_4: { if (step_counter % 4 == 0) { gate_on(); } } break; default: break; } step_counter = (step_counter + 1) % 16; redraw_piano_note = true; } 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, play_step); TIMER_DATA_0 = n_ticks; TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; } TriggerNote * get_current_trig(void) { Pattern *pat = &patterns[pattern_selection_loc]; switch (channel_selection_loc) { case 0: { return &pat->ch1.notes[trig_selection_loc]; } break; case 1: { return &pat->ch2.notes[trig_selection_loc]; } break; case 2: { return &pat->ch3.notes[trig_selection_loc]; } break; case 3: { return &pat->ch4.notes[trig_selection_loc]; } break; } return NULL; } void handle_channel_selection(void) { Pattern *pat = &patterns[pattern_selection_loc]; if (key_tap(KEY_B)) { if (key_hold(KEY_SELECT)) { clipboard_copy(); } else { switch (channel_selection_loc) { case 0: { pat->ch1.active ^= 1; } break; case 1: { pat->ch2.active ^= 1; } break; case 2: { pat->ch3.active ^= 1; } break; case 3: { pat->ch4.active ^= 1; } break; } redraw_channels = true; } } else if (key_tap(KEY_A)) { if (key_hold(KEY_SELECT)) { clipboard_paste(); } else { switch (channel_selection_loc) { case 0: { input_handler = handle_param_selection_ch1; } break; case 1: { input_handler = handle_param_selection_ch2; } break; case 2: { input_handler = handle_param_selection_ch3; } break; case 3: { input_handler = handle_param_selection_ch4; } break; } } redraw_params = true; } else if (key_tap(KEY_L)) { s32 inc = -1; if (key_hold(KEY_SELECT)) { inc = -12; } for (size_t i = 0; i < 16; i++) { TriggerNote *trig; switch (channel_selection_loc) { case 0: { trig = &pat->ch1.notes[i]; } break; case 1: { trig = &pat->ch2.notes[i]; } break; case 2: { trig = &pat->ch3.notes[i]; } break; case 3: { trig = &pat->ch4.notes[i]; } break; default: {trig = &pat->ch1.notes[i]; } break; } trig->note = MAX((s32)trig->note + inc, (s32)NOTE_C_2); } redraw_trigs = true; } else if (key_tap(KEY_R)) { s32 inc = 1; if (key_hold(KEY_SELECT)) { inc = 12; } for (size_t i = 0; i < 16; i++) { TriggerNote *trig; switch (channel_selection_loc) { case 0: { trig = &pat->ch1.notes[i]; } break; case 1: { trig = &pat->ch2.notes[i]; } break; case 2: { trig = &pat->ch3.notes[i]; } break; case 3: { trig = &pat->ch4.notes[i]; } break; default: {trig = &pat->ch1.notes[i]; } break; } trig->note = MIN((s32)trig->note + inc, (s32)NOTE_C_8 - 1); } redraw_trigs = true; } if (key_tap(KEY_RIGHT)) { trig_selection_loc = 0; param_selection_loc = 0; input_handler = handle_trigger_selection; redraw_piano_note = true; redraw_params = true; } else if (key_tap(KEY_LEFT)) { input_handler = handle_pattern_selection; redraw_params = true; } else 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); } param_selection_loc = 0; redraw_trigs = true; redraw_params = true; } else 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); } param_selection_loc = 0; redraw_trigs = true; redraw_params = true; } } void stop_playing(void) { play_status = 0; step_counter = 0; TIMER_CTRL_0 = 0; SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; SOUND_NOISE_CTRL = 0; redraw_play_pause = true; } void toggle_playing(void) { play_status ^= 1; step_counter = 0; if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) == 0) { if (current_pattern != next_pattern) { current_pattern = next_pattern; redraw_pattern_buttons = true; } set_time(patterns[current_pattern].bpm); play_step(); } else { TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; SOUND_NOISE_CTRL = 0; } redraw_play_pause = true; } void pause_playing(void) { play_status ^= 1; if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) == 0) { if (current_pattern != next_pattern && step_counter == 0) { current_pattern = next_pattern; redraw_pattern_buttons = true; } set_time(patterns[current_pattern].bpm); play_step(); } else { TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; SOUND_SQUARE1_CTRL = 0; SOUND_SQUARE2_CTRL = 0; SOUND_WAVE_CTRL = 0; SOUND_NOISE_CTRL = 0; } redraw_play_pause = true; } void select_bank(int i) { stop_playing(); clipboard.type = CLIP_EMPTY; save_bank(current_bank); metadata.current_pattern = current_pattern; metadata.current_bank = i; save_metadata(); if (current_bank != i) { load_bank(i); } current_bank = i; redraw_pattern_buttons = true; redraw_trigs = true; redraw_channels = true; redraw_bank_buttons = true; redraw_bpm = true; } void handle_right_col_selection(void) { if (key_tap(KEY_LEFT)) { if (right_col_selection_loc == R_COL_STOP) { right_col_selection_loc = R_COL_PLAY; } else { input_handler = handle_trigger_selection; switch (right_col_selection_loc) { case R_COL_BPM: { trig_selection_loc = 15; } break; default: { trig_selection_loc = 7; } break; } redraw_params = true; } } else if (key_tap(KEY_RIGHT)) { if (right_col_selection_loc == R_COL_PLAY) { right_col_selection_loc = R_COL_STOP; } else { input_handler = handle_pattern_selection; switch (right_col_selection_loc) { case R_COL_BANK_A: { pattern_selection_loc = 0; } break; case R_COL_BANK_B: { pattern_selection_loc = 1; } break; case R_COL_BANK_C: { pattern_selection_loc = 2; } break; case R_COL_BANK_D: { pattern_selection_loc = 3; } break; case R_COL_STOP: { pattern_selection_loc = 5; } break; default: { pattern_selection_loc = 6; } break; } redraw_trigs = true; } } else if (key_tap(KEY_UP)) { switch (right_col_selection_loc) { case R_COL_PLAY: case R_COL_STOP: { right_col_selection_loc = R_COL_BANK_D; } break; case R_COL_BPM: { right_col_selection_loc = R_COL_PLAY; } break; case R_COL_BANK_A: { right_col_selection_loc = R_COL_BPM; } break; default: { right_col_selection_loc++; } break; } } else if (key_tap(KEY_DOWN)) { switch (right_col_selection_loc) { case R_COL_PLAY: case R_COL_STOP: { right_col_selection_loc = R_COL_BPM; } break; case R_COL_BPM: { right_col_selection_loc = R_COL_BANK_A; } break; case R_COL_BANK_D: { right_col_selection_loc = R_COL_PLAY; } break; default: { right_col_selection_loc--; } break; } } else if (key_tap(KEY_L)) { switch (right_col_selection_loc) { case R_COL_BPM: { s32 bpm_inc = -1; if (key_pressed(KEY_SELECT)) { bpm_inc = -10; } patterns[pattern_selection_loc].bpm = CLAMP( patterns[pattern_selection_loc].bpm + bpm_inc, 10, 300); if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) != 0 && current_pattern == pattern_selection_loc) { set_time(patterns[current_pattern].bpm); } redraw_bpm = true; } break; } } else if (key_tap(KEY_R)) { switch (right_col_selection_loc) { case R_COL_BPM: { s32 bpm_inc = 1; if (key_pressed(KEY_SELECT)) { bpm_inc = 10; } patterns[pattern_selection_loc].bpm = CLAMP( patterns[pattern_selection_loc].bpm + bpm_inc, 10, 300); if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) != 0 && current_pattern == pattern_selection_loc) { set_time(patterns[current_pattern].bpm); } redraw_bpm = true; } break; } } else if (key_tap(KEY_B)) { switch (right_col_selection_loc) { case R_COL_STOP: { next_scene = SCENE_SETTINGS; } break; case R_COL_PLAY: { pause_playing(); } break; case R_COL_BANK_A: { select_bank(0); } break; case R_COL_BANK_B: { select_bank(1); } break; case R_COL_BANK_C: { select_bank(2); } break; case R_COL_BANK_D: { select_bank(3); } break; } } } void handle_pattern_selection(void) { if (key_tap(KEY_B)) { if (key_hold(KEY_SELECT)) { clipboard_copy(); } else { next_pattern = pattern_selection_loc; redraw_pattern_buttons = true; } } if (key_tap(KEY_A)) { if (key_hold(KEY_SELECT)) { clipboard_paste(); } } if (key_tap(KEY_RIGHT)) { input_handler = handle_channel_selection; redraw_params = true; } else if (key_tap(KEY_UP)) { if (pattern_selection_loc > 0) { pattern_selection_loc = pattern_selection_loc - 1; redraw_channels = true; redraw_trigs = true; redraw_bpm = true; } } else if (key_tap(KEY_DOWN)) { if (pattern_selection_loc < 7) { pattern_selection_loc = pattern_selection_loc + 1; redraw_channels = true; redraw_trigs = true; redraw_bpm = true; } } if (key_tap(KEY_LEFT)) { input_handler = handle_right_col_selection; right_col_selection_loc = R_COL_BPM; switch (pattern_selection_loc) { case 0: { right_col_selection_loc = R_COL_BANK_A; } break; case 1: { right_col_selection_loc = R_COL_BANK_B; } break; case 2: { right_col_selection_loc = R_COL_BANK_C; } break; case 3: { right_col_selection_loc = R_COL_BANK_D; } break; case 4: { right_col_selection_loc = R_COL_PLAY; } break; case 5: { right_col_selection_loc = R_COL_STOP; } break; default: { right_col_selection_loc = R_COL_BPM; } break; } } } bool set_param_selection_sq1(ChannelSquareParams *params, InputHandler return_handler) { // Go back to trigger selection. if (key_released(KEY_A)) { input_handler = return_handler; redraw_params = true; return false; } // Cursor movement. if (key_tap(KEY_LEFT) || key_tap(KEY_RIGHT)) { int inc = 0; int loc = param_selection_loc; if (key_tap(KEY_RIGHT)) { if (loc < 5) { inc = 1; } else if (loc == 6) { inc = -1; } } else { if (loc <= 5) { inc = -1; } else if (loc == 6) { inc = -2; } } param_selection_loc = CLAMP(loc + inc, 0, 6); redraw_params = true; return false; } if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { int inc = 0; int loc = param_selection_loc; if (key_tap(KEY_UP)) { if (loc == 4) { inc = 2; } else if (loc == 5) { inc = 1; } else if (loc == 6) { inc = -1; } } else { if (loc == 4) { inc = 2; } else if (loc == 5) { inc = 1; } else if (loc == 6) { inc = -1; } } param_selection_loc = CLAMP(loc + inc, 0, 6); redraw_params = true; return false; } // Adjust parameter. if (key_tap(KEY_R) || key_tap(KEY_L)) { int inc; if (key_tap(KEY_L)) { inc = -1; } else { inc = 1; } switch (param_selection_loc) { case 0: { params->duty_cycle = CLAMP(params->duty_cycle + inc, 0, 3); } break; case 1: { params->env_volume = CLAMP(params->env_volume + inc, 0, 15); } break; case 2: { params->env_direction ^= 1; } break; case 3: { params->env_time = CLAMP(params->env_time + inc, 0, 7); } break; case 4: { params->sweep_number = CLAMP(params->sweep_number + inc, 0, 7); } break; case 5: { params->sweep_time = CLAMP(params->sweep_time + inc, 0, 7); } break; case 6: { params->sweep_direction ^= 1; } break; } redraw_params = true; return true; } return false; } bool set_param_selection_sq2(ChannelSquareParams *params, InputHandler return_handler) { // Go back to trigger selection. if (key_released(KEY_A)) { input_handler = return_handler; redraw_params = true; return false; } // Cursor movement. if (key_tap(KEY_LEFT) || key_tap(KEY_RIGHT)) { int inc = 0; int loc = param_selection_loc; if (key_tap(KEY_RIGHT)) { inc = 1; } else { inc = -1; } param_selection_loc = CLAMP(loc + inc, 0, 3); redraw_params = true; return false; } // Adjust parameter. if (key_tap(KEY_R) || key_tap(KEY_L)) { int inc; if (key_tap(KEY_L)) { inc = -1; } else { inc = 1; } switch (param_selection_loc) { case 0: { params->duty_cycle = CLAMP(params->duty_cycle + inc, 0, 3); } break; case 1: { params->env_volume = CLAMP(params->env_volume + inc, 0, 15); } break; case 2: { params->env_direction ^= 1; } break; case 3: { params->env_time = CLAMP(params->env_time + inc, 0, 7); } break; } redraw_params = true; return true; } return false; } bool set_param_selection_wave(ChannelWaveParams *params, InputHandler return_handler) { // Go back to trigger selection. if (key_released(KEY_A)) { input_handler = return_handler; redraw_params = true; return false; } // Cursor movement. 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) { inc = 26; } else if (loc == 63) { inc = 9; } else if (loc != 47 && loc != 63 && loc < 72) { inc = 1; } } else { if (loc == 32 || loc == 48) { inc = -17; } else if (loc == 73) { inc = -26; } else if (loc != 16 && loc != 64) { inc = -1; } } param_selection_loc = CLAMP(loc + inc, 0, 73); redraw_params = true; return false; } 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 == 72) { inc = 1; } else if (loc == 73) { inc = -1; } } 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); } else if (loc == 72) { inc = 1; } else if (loc == 73) { inc = -1; } } param_selection_loc = CLAMP(loc + inc, 0, 73); redraw_params = true; return false; } // Adjust parameter. 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) { // Draw on wave a. u8 byte_number = param_selection_loc / 2; u8 *byte = ¶ms->wave_a; byte += byte_number; if (odd) { *byte = (~0xF & *byte) | ((*byte + inc) & 0xF); } else { *byte = (0xF & *byte) | (((*byte >> 4) + inc) & 0xF) << 4; } } else if (param_selection_loc < 64){ // Draw on wave b. u8 byte_number = (param_selection_loc - 32) / 2; u8 *byte = ¶ms->wave_b; byte += byte_number; if (odd) { *byte = (~0xF & *byte) | ((*byte + inc) & 0xF); } else { *byte = (0xF & *byte) | (((*byte >> 4) + inc) & 0xF) << 4; } } else if (param_selection_loc < 72) { // Copy default waves. u32 *wave_a = ¶ms->wave_a; u32 *wave_b = ¶ms->wave_b; switch (param_selection_loc) { case 64: { memcpy32(wave_a, sine_wave, 16); } break; case 65: { memcpy32(wave_a, saw_wave, 16); } break; case 66: { memcpy32(wave_a, square_wave, 16); } break; case 67: { u32 rand_wave[4] = { rng32(), rng32(), rng32(), rng32(), }; memcpy32(wave_a, rand_wave, 16); } break; case 68: { memcpy32(wave_b, sine_wave, 16); } break; case 69: { memcpy32(wave_b, saw_wave, 16); } break; case 70: { memcpy32(wave_b, square_wave, 16); } break; case 71: { u32 rand_wave[4] = { rng32(), rng32(), rng32(), rng32(), }; memcpy32(wave_b, rand_wave, 16); } break; } } else if (param_selection_loc == 72) { u8 *wave_mode = ¶ms->wave_mode; *wave_mode = CLAMP(*wave_mode + inc, 0, 2); } else if (param_selection_loc == 73) { u8 *wave_volume = ¶ms->wave_volume; *wave_volume = CLAMP(*wave_volume + inc, 0, 4); } redraw_params = true; return true; } return false; } bool set_param_selection_noise(ChannelNoiseParams *params, InputHandler return_handler) { // Go back to trigger selection. if (key_released(KEY_A)) { input_handler = return_handler; redraw_params = true; return false; } // Cursor movement. if (key_tap(KEY_LEFT) || key_tap(KEY_RIGHT)) { int inc = 0; int loc = param_selection_loc; if (key_tap(KEY_RIGHT)) { inc = 1; } else { inc = -1; } param_selection_loc = CLAMP(loc + inc, 0, 3); redraw_params = true; return false; } // Adjust parameter. if (key_tap(KEY_R) || key_tap(KEY_L)) { int inc; if (key_tap(KEY_L)) { inc = -1; } else { inc = 1; } switch (param_selection_loc) { case 0: { params->bit_mode = CLAMP(params->bit_mode + inc, 0, 1); } break; case 1: { params->env_volume = CLAMP(params->env_volume + inc, 0, 15); } break; case 2: { params->env_direction ^= 1; } break; case 3: { params->env_time = CLAMP(params->env_time + inc, 0, 7); } break; } redraw_params = true; return true; } return false; } void handle_param_selection_ch1() { Pattern *pat = &patterns[pattern_selection_loc]; if (set_param_selection_sq1(&ch1_params, handle_channel_selection)) { for (size_t i = 0; i < 16; i++) { pat->ch1.params[i] = ch1_params; } } } void handle_param_selection_ch2() { Pattern *pat = &patterns[pattern_selection_loc]; if (set_param_selection_sq2(&ch2_params, handle_channel_selection)) { for (size_t i = 0; i < 16; i++) { pat->ch2.params[i] = ch2_params; } } } void handle_param_selection_ch3() { Pattern *pat = &patterns[pattern_selection_loc]; if (set_param_selection_wave(&ch3_params, handle_channel_selection)) { for (size_t i = 0; i < 16; i++) { pat->ch3.params[i] = ch3_params; } } } void handle_param_selection_ch4() { Pattern *pat = &patterns[pattern_selection_loc]; if (set_param_selection_noise(&ch4_params, handle_channel_selection)) { for (size_t i = 0; i < 16; i++) { pat->ch4.params[i] = ch4_params; } } } void handle_param_selection_sq1() { ChannelSquareParams *params = &patterns[pattern_selection_loc].ch1.params[trig_selection_loc]; set_param_selection_sq1(params, handle_trigger_selection); } void handle_param_selection_sq2() { ChannelSquareParams *params = &patterns[pattern_selection_loc].ch2.params[trig_selection_loc]; set_param_selection_sq2(params, handle_trigger_selection); } void handle_param_selection_wave() { ChannelWaveParams *params = &patterns[pattern_selection_loc].ch3.params[trig_selection_loc]; set_param_selection_wave(params, handle_trigger_selection); } void handle_param_selection_noise() { ChannelNoiseParams *params = &patterns[pattern_selection_loc].ch4.params[trig_selection_loc]; set_param_selection_noise(params, handle_trigger_selection); } void handle_trigger_selection(void) { TriggerNote *trig = get_current_trig(); if (key_tap(KEY_B)) { if (key_hold(KEY_SELECT)) { clipboard_copy(); } else { // Toggle trigger. trig->active ^= 1; redraw_trigs = true; } } else if (key_tap(KEY_L)) { s32 inc = -1; if (key_hold(KEY_SELECT)) { inc = -12; } // Decrease note. if (trig->active) { trig->note = MAX((s32)trig->note + inc, (s32)NOTE_C_2); } redraw_trigs = true; redraw_piano_note = true; } else if (key_tap(KEY_R)) { s32 inc = 1; if (key_hold(KEY_SELECT)) { inc = 12; } // Increase note. if (trig->active) { trig->note = MIN((s32)trig->note + inc, (s32)NOTE_C_8 - 1); } redraw_trigs = true; redraw_piano_note = true; } // Move trigger cursor. if (key_tap(KEY_LEFT)) { if (trig_selection_loc == 0 || trig_selection_loc == 8) { // We are at the boundary, switch to channel selection. input_handler = handle_channel_selection; } else { trig_selection_loc = MAX(trig_selection_loc - 1, 0); } redraw_params = true; redraw_piano_note = true; } else if (key_tap(KEY_RIGHT)) { if (trig_selection_loc != 7 && trig_selection_loc != 15) { trig_selection_loc = MIN(trig_selection_loc + 1, 15); } else if (trig_selection_loc == 7) { input_handler = handle_right_col_selection; right_col_selection_loc = R_COL_PLAY; } else if (trig_selection_loc == 15) { right_col_selection_loc = R_COL_BPM; input_handler = handle_right_col_selection; } redraw_params = true; redraw_piano_note = true; } else if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { trig_selection_loc = (trig_selection_loc + 8) % 16; redraw_params = true; redraw_piano_note = true; } else if (key_tap(KEY_A)) { if (key_hold(KEY_SELECT)) { clipboard_paste(); } else { // Switch to parameter selection. switch (channel_selection_loc) { case 0: { input_handler = handle_param_selection_sq1; } break; case 1: { input_handler = handle_param_selection_sq2; } break; case 2: { input_handler = handle_param_selection_wave; } break; case 3: { input_handler = handle_param_selection_noise; } break; } redraw_params = true; redraw_piano_note = true; } } } void handle_sequencer_input(void) { if (key_tap(KEY_START)) { if (key_hold(KEY_SELECT)) { save_bank(current_bank); save_metadata(); return; } // Stop the sequencer or start playing from the beginning. toggle_playing(); } else if (key_hold(KEY_SELECT)) { if (input_handler == handle_param_selection_sq1 || input_handler == handle_param_selection_sq2 || input_handler == handle_param_selection_wave || input_handler == handle_param_selection_noise) { clipboard_copy(); } input_handler(); } else { input_handler(); } } void sequencer_init(void) { stop_playing(); // Load the previous bank from SRAM or initialize it if needed. sram_read(&metadata, 0, sizeof(Metadata)); if (metadata.initialized != SAVE_INIT_MAGIC) { for (size_t i = 0; i < KB(64) / 8; ++i) { SRAM[i] = 0x00; } dma_fill(&metadata, 0, sizeof(Metadata), 3); metadata.initialized = SAVE_INIT_MAGIC; save_metadata(); save_bank(0); save_bank(1); save_bank(2); save_bank(3); } else { current_bank = metadata.current_bank; current_pattern = metadata.current_pattern; next_pattern = metadata.current_pattern; pattern_selection_loc = current_pattern; settings = metadata.settings; load_bank(metadata.current_bank); } swap_palette(settings.theme); // Initialize input handler. input_handler = handle_trigger_selection; // Initialize sound system. SOUND_STATUS = SOUND_ENABLE; SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 | SOUND_SQUARE2 | SOUND_WAVE | SOUND_NOISE, 3); SOUND_DSOUND_MASTER = SOUND_DMG100; }