From 76be354cf2d924a8cbdd7fb4ac4f6b9c1546762f Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Wed, 5 Apr 2023 11:37:49 +0200 Subject: Add clipboard copy/paste --- src/sequencer.c | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 325 insertions(+), 8 deletions(-) (limited to 'src/sequencer.c') diff --git a/src/sequencer.c b/src/sequencer.c index 4b83f5f..6944b39 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -15,9 +15,6 @@ sram_write(u8 *src, u16 pos, u16 n_bytes) { } } -// TODO -// - Preview sound keys? -// - Copy paste trigs/notes/params void set_time(int bpm); // @@ -419,7 +416,6 @@ const ChannelNoise default_ch4 = { {false, NOTE_E_6}, {false, NOTE_E_6}, {false, NOTE_E_6}, - {false, NOTE_G_4}, }, .params = { {0xF, 0x2, 0, 0}, @@ -2602,17 +2598,338 @@ handle_trigger_selection(void) { } } +typedef enum ClipboardType { + CLIP_EMPTY, + CLIP_TRIG, + CLIP_PARAM_CH1, + CLIP_PARAM_CH2, + CLIP_PARAM_CH3, + CLIP_PARAM_CH4, + CLIP_PATTERN, + CLIP_CHANNEL, +} ClipboardType; + +typedef struct Clipboard { + ClipboardType type; + u8 src_pat; + u8 src_chan; + u8 src_trig; +} Clipboard; + +static Clipboard clipboard = {CLIP_EMPTY, 0, 0, 0}; + +void +clipboard_paste(void) { + Pattern *pat_dst = &patterns[pattern_selection_loc]; + Pattern *pat_src = &patterns[clipboard.src_pat]; + + if (input_handler == handle_trigger_selection) { + if (clipboard.type == CLIP_TRIG) { + // Copy notes or parameters when applicable. + switch (clipboard.src_chan) { + case 0: { + switch (channel_selection_loc) { + case 0: { + pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; + pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + } break; + case 1: { + pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; + pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + } break; + case 2: { + pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; + } break; + case 3: { + pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; + } break; + } + } break; + case 1: { + switch (channel_selection_loc) { + case 0: { + pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; + pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; + pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; + pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; + pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; + } break; + case 1: { + pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; + pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; + } break; + case 2: { + pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; + } break; + case 3: { + pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; + } break; + } + } break; + case 2: { + switch (channel_selection_loc) { + case 0: { + pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; + } break; + case 1: { + pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; + } break; + case 2: { + pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; + pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; + } break; + case 3: { + pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; + } break; + } + } break; + case 3: { + switch (channel_selection_loc) { + case 0: { + pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; + } break; + case 1: { + pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; + } break; + case 2: { + pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; + } break; + case 3: { + pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; + pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; + } break; + } + } break; + } + } + // Only paste the params for the respective trigger. + if (clipboard.type == CLIP_PARAM_CH1) { + switch (channel_selection_loc) { + case 0: { + pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + } break; + case 1: { + pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + } break; + } + } + if (clipboard.type == CLIP_PARAM_CH2) { + switch (channel_selection_loc) { + case 0: { + pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; + pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; + pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; + pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; + } break; + case 1: { + pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; + } break; + } + } + if (clipboard.type == CLIP_PARAM_CH3 && channel_selection_loc == clipboard.src_chan) { + pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; + } + if (clipboard.type == CLIP_PARAM_CH4 && channel_selection_loc == clipboard.src_chan) { + pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; + } + draw_triggers(); + draw_parameters(); + } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH1) { + pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + draw_parameters(); + } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH2) { + pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; + pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; + pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; + pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; + draw_parameters(); + } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH2) { + pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; + draw_parameters(); + } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH1) { + pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + draw_parameters(); + } else if (input_handler == handle_param_selection_wave && clipboard.type == CLIP_PARAM_CH3) { + pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; + draw_parameters(); + } else if (input_handler == handle_param_selection_noise && clipboard.type == CLIP_PARAM_CH4) { + pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; + draw_parameters(); + } else if (input_handler == handle_channel_selection && clipboard.type == CLIP_CHANNEL) { + // Copy notes from a different channel OR notes and parameters + // from a different pattern. + if (clipboard.src_chan == channel_selection_loc) { + switch (clipboard.src_chan) { + case 0: { pat_dst->ch1 = pat_src->ch1; } break; + case 1: { pat_dst->ch2 = pat_src->ch2; } break; + case 2: { pat_dst->ch3 = pat_src->ch3; } break; + case 3: { pat_dst->ch4 = pat_src->ch4; } break; + } + } else { + switch (clipboard.src_chan) { + case 0: { + switch (channel_selection_loc) { + case 0: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch1.notes[i] = pat_src->ch1.notes[i]; + } + } break; + case 1: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch2.notes[i] = pat_src->ch1.notes[i]; + } + } break; + case 2: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch3.notes[i] = pat_src->ch1.notes[i]; + } + } break; + case 3: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch4.notes[i] = pat_src->ch1.notes[i]; + } + } break; + } + } break; + case 1: { + switch (channel_selection_loc) { + case 0: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch1.notes[i] = pat_src->ch2.notes[i]; + } + } break; + case 1: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch2.notes[i] = pat_src->ch2.notes[i]; + } + } break; + case 2: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch3.notes[i] = pat_src->ch2.notes[i]; + } + } break; + case 3: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch4.notes[i] = pat_src->ch2.notes[i]; + } + } break; + } + } break; + case 2: { + switch (channel_selection_loc) { + case 0: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch1.notes[i] = pat_src->ch3.notes[i]; + } + } break; + case 1: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch2.notes[i] = pat_src->ch3.notes[i]; + } + } break; + case 2: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch3.notes[i] = pat_src->ch3.notes[i]; + } + } break; + case 3: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch4.notes[i] = pat_src->ch3.notes[i]; + } + } break; + } + } break; + case 3: { + switch (channel_selection_loc) { + case 0: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch1.notes[i] = pat_src->ch4.notes[i]; + } + } break; + case 1: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch2.notes[i] = pat_src->ch4.notes[i]; + } + } break; + case 2: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch3.notes[i] = pat_src->ch4.notes[i]; + } + } break; + case 3: { + for (size_t i = 0; i < 16; i++) { + pat_dst->ch4.notes[i] = pat_src->ch4.notes[i]; + } + } break; + } + } break; + } + } + draw_channels(); + draw_triggers(); + } else if (input_handler == handle_pattern_selection && clipboard.type == CLIP_PATTERN) { + // Copy an entire pattern. + if (pattern_selection_loc != clipboard.src_pat) { + *pat_dst = *pat_src; + draw_channels(); + draw_triggers(); + } + } +} + +void +clipboard_copy(void) { + if (input_handler == handle_trigger_selection) { + clipboard.type = CLIP_TRIG; + clipboard.src_pat = pattern_selection_loc; + clipboard.src_chan = channel_selection_loc; + clipboard.src_trig = trig_selection_loc; + } else if (input_handler == handle_param_selection_sq1) { + clipboard.type = CLIP_PARAM_CH1; + clipboard.src_pat = pattern_selection_loc; + clipboard.src_chan = channel_selection_loc; + clipboard.src_trig = trig_selection_loc; + } else if (input_handler == handle_param_selection_sq2) { + clipboard.type = CLIP_PARAM_CH2; + clipboard.src_pat = pattern_selection_loc; + clipboard.src_chan = channel_selection_loc; + clipboard.src_trig = trig_selection_loc; + } else if (input_handler == handle_param_selection_wave) { + clipboard.type = CLIP_PARAM_CH3; + clipboard.src_pat = pattern_selection_loc; + clipboard.src_chan = channel_selection_loc; + clipboard.src_trig = trig_selection_loc; + } else if (input_handler == handle_param_selection_noise) { + clipboard.type = CLIP_PARAM_CH4; + clipboard.src_pat = pattern_selection_loc; + clipboard.src_chan = channel_selection_loc; + clipboard.src_trig = trig_selection_loc; + } else if (input_handler == handle_channel_selection) { + clipboard.type = CLIP_CHANNEL; + clipboard.src_pat = pattern_selection_loc; + clipboard.src_chan = channel_selection_loc; + } else if (input_handler == handle_pattern_selection) { + clipboard.type = CLIP_PATTERN; + clipboard.src_pat = pattern_selection_loc; + } +} + void handle_sequencer_input(void) { poll_keys(); - input_handler(); if (key_tap(KEY_START)) { // Stop the sequencer or start playing from the beginning. toggle_playing(); - } else if (key_tap(KEY_SELECT)) { - // Play/pause. - pause_playing(); + } else if (key_hold(KEY_SELECT)) { + // Clipboard combo. + if (key_tap(KEY_A)) { + clipboard_paste(); + } else if (key_tap(KEY_B)){ + clipboard_copy(); + } + } else { + input_handler(); } } -- cgit v1.2.1