From 20b1b448b7a1c8e22cb2e0f2d6294533f7a220af Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Wed, 12 Jul 2023 18:24:46 +0200 Subject: Add a notification system and use it for save/copy/paste events --- src/clipboard.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/drawing.c | 26 +++++++++++++++++++++++- src/globals.c | 11 +++++++++++ src/main.c | 6 +++++- src/sequencer.c | 25 +++++++++++++++++++++++ 5 files changed, 127 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/clipboard.c b/src/clipboard.c index eba28ae..edb9b31 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -35,16 +35,20 @@ clipboard_paste(void) { 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]; + send_notif("PASTED NOTE & PARAMS"); } 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]; + send_notif("PASTED NOTE & PARAMS"); } break; case 2: { pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; case 3: { pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; } } break; @@ -56,16 +60,20 @@ clipboard_paste(void) { 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; + send_notif("PASTED NOTE & PARAMS"); } 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]; + send_notif("PASTED NOTE & PARAMS"); } break; case 2: { pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; case 3: { pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; } } break; @@ -73,16 +81,20 @@ clipboard_paste(void) { switch (channel_selection_loc) { case 0: { pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; case 1: { pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } 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]; + send_notif("PASTED NOTE & PARAMS"); } break; case 3: { pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; } } break; @@ -90,16 +102,20 @@ clipboard_paste(void) { switch (channel_selection_loc) { case 0: { pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; case 1: { pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } break; case 2: { pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; + send_notif("PASTED NOTE"); } 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]; + send_notif("PASTED NOTE & PARAMS"); } break; } } break; @@ -110,9 +126,11 @@ clipboard_paste(void) { switch (channel_selection_loc) { case 0: { pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + send_notif("PASTED PARAMS"); } break; case 1: { pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; + send_notif("PASTED PARAMS"); } break; } } @@ -123,40 +141,50 @@ clipboard_paste(void) { 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; + send_notif("PASTED PARAMS"); } break; case 1: { pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; + send_notif("PASTED PARAMS"); } 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]; + send_notif("PASTED PARAMS"); } 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]; + send_notif("PASTED PARAMS"); } 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]; + send_notif("PASTED PARAMS"); 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; + send_notif("PASTED PARAMS"); 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]; + send_notif("PASTED PARAMS"); 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]; + send_notif("PASTED PARAMS"); 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]; + send_notif("PASTED PARAMS"); 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]; + send_notif("PASTED PARAMS"); draw_parameters(); } else if (input_handler == handle_channel_selection && clipboard.type == CLIP_CHANNEL) { // Copy notes from a different channel OR notes and parameters @@ -168,6 +196,7 @@ clipboard_paste(void) { case 2: { pat_dst->ch3 = pat_src->ch3; } break; case 3: { pat_dst->ch4 = pat_src->ch4; } break; } + send_notif("PASTED NOTES & PARAMS"); } else { switch (clipboard.src_chan) { case 0: { @@ -177,22 +206,26 @@ clipboard_paste(void) { pat_dst->ch1.notes[i] = pat_src->ch1.notes[i]; pat_dst->ch1.params[i] = pat_src->ch1.params[i]; } + send_notif("PASTED NOTES & PARAMS"); } break; case 1: { for (size_t i = 0; i < 16; i++) { pat_dst->ch2.notes[i] = pat_src->ch1.notes[i]; pat_dst->ch2.params[i] = pat_src->ch1.params[i]; } + send_notif("PASTED NOTES & PARAMS"); } break; case 2: { for (size_t i = 0; i < 16; i++) { pat_dst->ch3.notes[i] = pat_src->ch1.notes[i]; } + send_notif("PASTED NOTES"); } break; case 3: { for (size_t i = 0; i < 16; i++) { pat_dst->ch4.notes[i] = pat_src->ch1.notes[i]; } + send_notif("PASTED NOTES"); } break; } } break; @@ -206,22 +239,26 @@ clipboard_paste(void) { pat_dst->ch1.params[i].env_direction = pat_src->ch2.params[i].env_direction; pat_dst->ch1.params[i].duty_cycle = pat_src->ch2.params[i].duty_cycle; } + send_notif("PASTED NOTES & PARAMS"); } break; case 1: { for (size_t i = 0; i < 16; i++) { pat_dst->ch2.notes[i] = pat_src->ch2.notes[i]; pat_dst->ch2.params[i] = pat_src->ch2.params[i]; } + send_notif("PASTED NOTES & PARAMS"); } break; case 2: { for (size_t i = 0; i < 16; i++) { pat_dst->ch3.notes[i] = pat_src->ch2.notes[i]; } + send_notif("PASTED NOTES"); } break; case 3: { for (size_t i = 0; i < 16; i++) { pat_dst->ch4.notes[i] = pat_src->ch2.notes[i]; } + send_notif("PASTED NOTES"); } break; } } break; @@ -231,22 +268,26 @@ clipboard_paste(void) { for (size_t i = 0; i < 16; i++) { pat_dst->ch1.notes[i] = pat_src->ch3.notes[i]; } + send_notif("PASTED NOTES"); } break; case 1: { for (size_t i = 0; i < 16; i++) { pat_dst->ch2.notes[i] = pat_src->ch3.notes[i]; } + send_notif("PASTED NOTES"); } break; case 2: { for (size_t i = 0; i < 16; i++) { pat_dst->ch3.notes[i] = pat_src->ch3.notes[i]; pat_dst->ch3.params[i] = pat_src->ch3.params[i]; } + send_notif("PASTED NOTES & PARAMS"); } break; case 3: { for (size_t i = 0; i < 16; i++) { pat_dst->ch4.notes[i] = pat_src->ch3.notes[i]; } + send_notif("PASTED NOTES"); } break; } } break; @@ -256,22 +297,26 @@ clipboard_paste(void) { for (size_t i = 0; i < 16; i++) { pat_dst->ch1.notes[i] = pat_src->ch4.notes[i]; } + send_notif("PASTED NOTES"); } break; case 1: { for (size_t i = 0; i < 16; i++) { pat_dst->ch2.notes[i] = pat_src->ch4.notes[i]; } + send_notif("PASTED NOTES"); } break; case 2: { for (size_t i = 0; i < 16; i++) { pat_dst->ch3.notes[i] = pat_src->ch4.notes[i]; } + send_notif("PASTED NOTES"); } break; case 3: { for (size_t i = 0; i < 16; i++) { pat_dst->ch4.notes[i] = pat_src->ch4.notes[i]; pat_dst->ch4.params[i] = pat_src->ch4.params[i]; } + send_notif("PASTED NOTES & PARAMS"); } break; } } break; @@ -290,6 +335,7 @@ clipboard_paste(void) { for (size_t i = 0; i < 16; i++) { pat_dst->ch1.params[i] = pat_src->ch1.params[clipboard.src_trig]; } + send_notif("PASTED PARAMS"); } if (clipboard.src_chan == 1) { ch1_params.env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; @@ -302,6 +348,7 @@ clipboard_paste(void) { pat_dst->ch1.params[i].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; pat_dst->ch1.params[i].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; } + send_notif("PASTED PARAMS"); } } } break; @@ -314,12 +361,14 @@ clipboard_paste(void) { for (size_t i = 0; i < 16; i++) { pat_dst->ch2.params[i] = pat_src->ch1.params[clipboard.src_trig]; } + send_notif("PASTED PARAMS"); } if (clipboard.src_chan == 1) { ch2_params = pat_src->ch2.params[clipboard.src_trig]; for (size_t i = 0; i < 16; i++) { pat_dst->ch2.params[i] = pat_src->ch2.params[clipboard.src_trig]; } + send_notif("PASTED PARAMS"); } } } break; @@ -332,6 +381,7 @@ clipboard_paste(void) { for (size_t i = 0; i < 16; i++) { pat_dst->ch3.params[i] = pat_src->ch3.params[clipboard.src_trig]; } + send_notif("PASTED PARAMS"); } } break; case 3: { @@ -343,6 +393,7 @@ clipboard_paste(void) { for (size_t i = 0; i < 16; i++) { pat_dst->ch4.params[i] = pat_src->ch4.params[clipboard.src_trig]; } + send_notif("PASTED PARAMS"); } } break; } @@ -352,10 +403,13 @@ clipboard_paste(void) { *pat_dst = *pat_src; draw_channels(); draw_triggers(); + send_notif("PASTED PATTERN"); } } } +void send_notif(char *msg); + void clipboard_copy(void) { if (input_handler == handle_trigger_selection) { @@ -363,33 +417,40 @@ clipboard_copy(void) { clipboard.src_pat = pattern_selection_loc; clipboard.src_chan = channel_selection_loc; clipboard.src_trig = trig_selection_loc; + send_notif("COPIED TRIGGER"); } 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; + send_notif("COPIED CH1 PARAMS"); } 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; + send_notif("COPIED CH2 PARAMS"); } 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; + send_notif("COPIED CH3 PARAMS"); } 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; + send_notif("COPIED CH4 PARAMS"); } else if (input_handler == handle_channel_selection) { clipboard.type = CLIP_CHANNEL; clipboard.src_pat = pattern_selection_loc; clipboard.src_chan = channel_selection_loc; + send_notif("COPIED CHANNEL"); } else if (input_handler == handle_pattern_selection) { clipboard.type = CLIP_PATTERN; clipboard.src_pat = pattern_selection_loc; + send_notif("COPIED PATTERN"); } } diff --git a/src/drawing.c b/src/drawing.c index 3e00e17..b488ed1 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -1246,6 +1246,30 @@ draw_notif_bar() { u8 x1 = PARAMS_START_X + PARAMS_W; u8 y1 = y0 + 10; u8 color = COL_FG; + draw_filled_rect(x0, y0, x1, y1, COL_BG); draw_rect(x0, y0, x1, y1, color); - txt_drawf_small("HELLO WORLD!", x0 + 2, y0 + 1, color); + if (notif.time > 0) { + char msg[32] = {0}; + if (notif.time <= 32) { + for (size_t i = 0; i < notif.time; i++) { + msg[i] = notif.msg[i + 32 - notif.time]; + } + } else { + for (size_t i = 0; i < 32; i++) { + msg[i] = notif.msg[i]; + } + } + txt_drawf_small(msg, x0 + 2, y0 + 1, color); + } else { + // if (notif.time > -16) { + // notif.time--; + // return; + // } + // TODO: More context dependent messages? Use instructions? + if (play_status == 0) { + txt_drawf_small("STOPPED", x0 + 2, y0 + 1, color); + } else { + txt_drawf_small("PLAYING", x0 + 2, y0 + 1, color); + } + } } diff --git a/src/globals.c b/src/globals.c index 2f4d32d..c2e2ee2 100644 --- a/src/globals.c +++ b/src/globals.c @@ -120,3 +120,14 @@ typedef enum Scene { static Scene scene = SCENE_SEQUENCER; static Scene next_scene = SCENE_SEQUENCER; + +typedef struct Notification { + // We can only fit 31 characters on the notification bar at the moment. + // Leaving an extra character for \0. + char msg[32]; + s16 time; +} Notification; + +static Notification notif = {0}; + +#define NOTIF_TIME (80 + 32) diff --git a/src/main.c b/src/main.c index 5c7d6ed..2e78b3c 100644 --- a/src/main.c +++ b/src/main.c @@ -13,7 +13,7 @@ WITH REGARD TO THIS SOFTWARE. // // UI tweaks. // - Add custom user themes -// - Notification support for feedback when doing some operations +// + Notification support for feedback when doing some operations // (copying/pasting) // - Animations for cursor movement/current step highlight. (A fade out maybe?) // - Add panning support. @@ -28,6 +28,7 @@ WITH REGARD TO THIS SOFTWARE. // - Loop? // - Undo/Redo. // - Select + up/down to queue the next pattern as we move to it? +// - Option to show help on the notification bar? // // Advanced // - Add tap tempo for BPM. @@ -172,6 +173,9 @@ update(void) { if (audio_sync_click) { play_click(); } + if (notif.time > 0) { + notif.time--; + } } int diff --git a/src/sequencer.c b/src/sequencer.c index 5120805..a22c219 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -998,12 +998,37 @@ handle_trigger_selection(void) { } } +void +send_notif(char *msg) { + if (msg == NULL) { + return; + } + + char buf[32] = {0}; + size_t n_chars = 0; + for (size_t i = 0; i < 32; i++) { + if (*msg == '\0') { + break; + } + buf[i] = *msg++; + n_chars++; + } + if (n_chars == 0) { + return; + } + notif.time = NOTIF_TIME; + for (size_t i = 0; i < 32; i++) { + notif.msg[i] = buf[i]; + } +} + void handle_sequencer_input(void) { if (key_tap(KEY_START)) { if (key_hold(KEY_SELECT)) { save_bank(current_bank); save_metadata(); + send_notif("SAVED BANK"); return; } // Stop the sequencer or start playing from the beginning. -- cgit v1.2.1