From 4a8fcbae8cdf581b2b0602cd89c2242ddca6b833 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 29 Jun 2019 13:58:32 +1000 Subject: [PATCH 001/469] TrigGroups were not working in ChainMode. When chaining a track that is configured to trigger another track, i.e trigGroup is set, load the machine settings for the triggered track. Still deciding whether this should be the appropriate behaviour. --- avr/cores/megacommand/MCL/GridTask.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridTask.cpp b/avr/cores/megacommand/MCL/GridTask.cpp index 2b6a7858f..9f5d41449 100644 --- a/avr/cores/megacommand/MCL/GridTask.cpp +++ b/avr/cores/megacommand/MCL/GridTask.cpp @@ -24,12 +24,9 @@ void GridTask::run() { A4Track *a4_track = (A4Track *)&empty_track; ExtTrack *ext_track = (ExtTrack *)&empty_track; #endif - int slots_changed[NUM_TRACKS]; - uint8_t slots_loaded[NUM_MD_TRACKS]; + int slots_changed[NUM_TRACKS] = {-1}; + uint8_t slots_loaded[NUM_MD_TRACKS] = {0}; - for (uint8_t i = 0; i < NUM_MD_TRACKS; i++) { - slots_loaded[i] = 0; - } bool send_ext_slots = false; bool send_md_slots = false; @@ -57,7 +54,6 @@ void GridTask::run() { GUI.removeTask(&grid_task); for (uint8_t n = 0; n < NUM_TRACKS; n++) { - slots_changed[n] = -1; if ((grid_page.active_slots[n] >= 0) && (mcl_actions.chains[n].loops > 0)) { // mark slot as changed in case next statement doesnt pass uint32_t next_transition = (uint32_t)mcl_actions.next_transitions[n] * 2; @@ -186,7 +182,6 @@ void GridTask::run() { (slots_loaded[trigGroup] == 0) && (slots_changed[n] == 0)) { md_track->load_from_mem(trigGroup); if (md_track->active == MD_TRACK_TYPE) { - bool set_level = false; switch (mcl_actions.transition_level[n]) { case 1: @@ -194,13 +189,9 @@ void GridTask::run() { md_track->machine.level = 0; break; case TRANSITION_UNMUTE: - DEBUG_PRINTLN("unmuting"); - DEBUG_PRINT(trigGroup); MD.muteTrack(trigGroup, false); break; case TRANSITION_MUTE: - DEBUG_PRINTLN("muting"); - DEBUG_PRINT(trigGroup); MD.muteTrack(trigGroup, true); break; } @@ -208,6 +199,9 @@ void GridTask::run() { &(MD.kit), set_level); md_track->place_track_in_kit(trigGroup, trigGroup, &(MD.kit), set_level); + //clear sequence on trigGroup track + //bool reset_params, clear_locks = true; + //mcl_seq.md_tracks[trigGroup].clear_track(clear_locks,reset_params); } md_track->load_from_mem(n); slots_loaded[trigGroup] = 1; From e24422ccf724376da8ada8f5cc9dd0ce2c71f79c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 28 Jul 2019 16:35:55 +1000 Subject: [PATCH 002/469] Bug fix: chain mode, incorrect transition behaviour in manual mode Arrays not initialised correctly. causing unpredictable transition behaviour --- avr/cores/megacommand/MCL/GridTask.cpp | 12 ++++++++++++ avr/cores/megacommand/WProgram.h | 2 +- avr/cores/megacommand/mcl_setup.cpp | 2 -- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridTask.cpp b/avr/cores/megacommand/MCL/GridTask.cpp index 9f5d41449..cdb161ab2 100644 --- a/avr/cores/megacommand/MCL/GridTask.cpp +++ b/avr/cores/megacommand/MCL/GridTask.cpp @@ -27,6 +27,13 @@ void GridTask::run() { int slots_changed[NUM_TRACKS] = {-1}; uint8_t slots_loaded[NUM_MD_TRACKS] = {0}; + for (uint8_t a = 0; a < NUM_TRACKS; a++) { + slots_changed[a] = -1; + } + + for (uint8_t a = 0; a < NUM_MD_TRACKS; a++) { + slots_loaded[a] = 0; + } bool send_ext_slots = false; bool send_md_slots = false; @@ -194,6 +201,8 @@ void GridTask::run() { case TRANSITION_MUTE: MD.muteTrack(trigGroup, true); break; + default: + break; } mcl_actions.md_set_machine(trigGroup, &(md_track->machine), &(MD.kit), set_level); @@ -224,6 +233,9 @@ void GridTask::run() { DEBUG_PRINT(n); MD.muteTrack(n, true); break; + default: + DEBUG_PRINTLN("default"); + break; } mcl_actions.md_set_machine(n, &(md_track->machine), &(MD.kit), set_level); diff --git a/avr/cores/megacommand/WProgram.h b/avr/cores/megacommand/WProgram.h index 5a77d8578..66370408a 100644 --- a/avr/cores/megacommand/WProgram.h +++ b/avr/cores/megacommand/WProgram.h @@ -11,7 +11,7 @@ #include "wiring_private.h" -//#define DEBUGMODE +#define DEBUGMODE #ifdef MEGACOMMAND #define SD_CS 53 //PB0 diff --git a/avr/cores/megacommand/mcl_setup.cpp b/avr/cores/megacommand/mcl_setup.cpp index 8542108f9..b3981bcb8 100644 --- a/avr/cores/megacommand/mcl_setup.cpp +++ b/avr/cores/megacommand/mcl_setup.cpp @@ -1,4 +1,3 @@ -#ifdef MCL #include "MCL.h" @@ -10,4 +9,3 @@ void setup() { void loop() { // put your main code here, to run repeatedly: } -#endif From 1c6176e86415abd1d0e5780da9f11bb3564774f1 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 28 Jul 2019 16:45:52 +1000 Subject: [PATCH 003/469] Revert "TrigGroups were not working in ChainMode." Let's disable this for now This reverts commit 4a8fcbae8cdf581b2b0602cd89c2242ddca6b833. --- avr/cores/megacommand/MCL/GridTask.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridTask.cpp b/avr/cores/megacommand/MCL/GridTask.cpp index cdb161ab2..b94b325e8 100644 --- a/avr/cores/megacommand/MCL/GridTask.cpp +++ b/avr/cores/megacommand/MCL/GridTask.cpp @@ -24,8 +24,8 @@ void GridTask::run() { A4Track *a4_track = (A4Track *)&empty_track; ExtTrack *ext_track = (ExtTrack *)&empty_track; #endif - int slots_changed[NUM_TRACKS] = {-1}; - uint8_t slots_loaded[NUM_MD_TRACKS] = {0}; + int slots_changed[NUM_TRACKS]; + uint8_t slots_loaded[NUM_MD_TRACKS]; for (uint8_t a = 0; a < NUM_TRACKS; a++) { slots_changed[a] = -1; @@ -61,6 +61,7 @@ void GridTask::run() { GUI.removeTask(&grid_task); for (uint8_t n = 0; n < NUM_TRACKS; n++) { + slots_changed[n] = -1; if ((grid_page.active_slots[n] >= 0) && (mcl_actions.chains[n].loops > 0)) { // mark slot as changed in case next statement doesnt pass uint32_t next_transition = (uint32_t)mcl_actions.next_transitions[n] * 2; @@ -189,6 +190,7 @@ void GridTask::run() { (slots_loaded[trigGroup] == 0) && (slots_changed[n] == 0)) { md_track->load_from_mem(trigGroup); if (md_track->active == MD_TRACK_TYPE) { + bool set_level = false; switch (mcl_actions.transition_level[n]) { case 1: @@ -196,9 +198,13 @@ void GridTask::run() { md_track->machine.level = 0; break; case TRANSITION_UNMUTE: + DEBUG_PRINTLN("unmuting"); + DEBUG_PRINT(trigGroup); MD.muteTrack(trigGroup, false); break; case TRANSITION_MUTE: + DEBUG_PRINTLN("muting"); + DEBUG_PRINT(trigGroup); MD.muteTrack(trigGroup, true); break; default: @@ -208,9 +214,6 @@ void GridTask::run() { &(MD.kit), set_level); md_track->place_track_in_kit(trigGroup, trigGroup, &(MD.kit), set_level); - //clear sequence on trigGroup track - //bool reset_params, clear_locks = true; - //mcl_seq.md_tracks[trigGroup].clear_track(clear_locks,reset_params); } md_track->load_from_mem(n); slots_loaded[trigGroup] = 1; From 14367df802867f175a09946d19e22615431358cb Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 14 Jun 2019 23:49:38 +1000 Subject: [PATCH 004/469] Add RAMPage, proof of concept --- avr/cores/megacommand/MCL/AuxPages.h | 3 +++ avr/cores/megacommand/MCL/MCLActions.cpp | 4 ++-- avr/cores/megacommand/MCL/MDSeqTrackData.h | 1 + avr/cores/megacommand/MCL/PageSelectPage.cpp | 7 +++++++ avr/cores/megacommand/MCL/SeqPages.cpp | 2 +- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/AuxPages.h b/avr/cores/megacommand/MCL/AuxPages.h index 388bd6f82..a7b3df9f7 100644 --- a/avr/cores/megacommand/MCL/AuxPages.h +++ b/avr/cores/megacommand/MCL/AuxPages.h @@ -6,6 +6,7 @@ #include "MCLEncoder.h" #include "MixerPage.h" #include "RoutePage.h" +#include "RAMPage.h" extern MCLEncoder mixer_param1; extern MCLEncoder mixer_param2; @@ -16,4 +17,6 @@ extern MCLEncoder route_param2; extern MixerPage mixer_page; extern RoutePage route_page; + +extern RAMPage ram_page; #endif /* AUXPAGES_H__ */ diff --git a/avr/cores/megacommand/MCL/MCLActions.cpp b/avr/cores/megacommand/MCL/MCLActions.cpp index 107b75cbe..84474f129 100644 --- a/avr/cores/megacommand/MCL/MCLActions.cpp +++ b/avr/cores/megacommand/MCL/MCLActions.cpp @@ -632,7 +632,7 @@ int MCLActions::calc_md_set_machine_latency(uint8_t track, MDMachine *machine, if ((kit_->params[track][i] != machine->params[i]) || ((i < 8) && (kit_->models[track] != machine->model))) { // (mcl_seq.md_tracks[track].is_param(i)))) { - bytes += 3; + if (machine->params[i] != 255) { bytes += 3; } } } @@ -704,7 +704,7 @@ void MCLActions::md_set_machine(uint8_t track, MDMachine *machine, MDKit *kit_, ((i < 8) && (kit_->models[track] != machine->model))) { // (mcl_seq.md_tracks[track].is_param(i)))) { // mcl_seq.md_tracks[track].params[i] = machine->params[i]; - MD.setTrackParam(track, i, machine->params[i]); + if (machine->params[i] != 255) { MD.setTrackParam(track, i, machine->params[i]); } } } } diff --git a/avr/cores/megacommand/MCL/MDSeqTrackData.h b/avr/cores/megacommand/MCL/MDSeqTrackData.h index a893dec46..b620b82fe 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrackData.h +++ b/avr/cores/megacommand/MCL/MDSeqTrackData.h @@ -16,6 +16,7 @@ class MDSeqTrackData { uint64_t lock_mask; uint8_t conditional[64]; uint8_t timing[64]; + }; #endif /* MDSEQTRACKDATA_H__ */ diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 4b86a50ac..7500b8583 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -3,6 +3,8 @@ #define MIX_PAGE 0 #define ROUTE_PAGE 1 +#define RAM_PAGE 4 +#define ROUTE_PAGE 2 #define WAVD_PAGE 8 #define SOUND 7 @@ -33,6 +35,11 @@ LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { strncpy(str, "ROUTE", 6); r_page = &route_page; break; + case RAM_PAGE: + if (str) + strncpy(str, "RAM ", 5); + r_page = &ram_page; + break; #ifdef WAV_DESIGNER case WAVD_PAGE: if (str) diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index fb9551973..a02575c46 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -1,7 +1,7 @@ #include "MCL.h" MCLEncoder seq_param1(0, 3, ENCODER_RES_SEQ); -MCLEncoder seq_param2(0, 64, ENCODER_RES_SEQ); +MCLEncoder seq_param2(0, 4, ENCODER_RES_SEQ); MCLEncoder seq_param3(0, 10, ENCODER_RES_SEQ); MCLEncoder seq_param4(0, 64, ENCODER_RES_SEQ); From 6b92122ddbb074257a6b989fff2967d02a95e454 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 15 Jun 2019 23:27:33 +1000 Subject: [PATCH 005/469] Forgot to check in RAMPage.* ?? --- avr/cores/megacommand/MCL/RAMPage.cpp | 440 ++++++++++++++++++++++++++ avr/cores/megacommand/MCL/RAMPage.h | 43 +++ 2 files changed, 483 insertions(+) create mode 100644 avr/cores/megacommand/MCL/RAMPage.cpp create mode 100644 avr/cores/megacommand/MCL/RAMPage.h diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp new file mode 100644 index 000000000..8ffbffe3e --- /dev/null +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -0,0 +1,440 @@ +#include "MCL.h" +#include "RAMPage.h" + +void RAMPage::setup() { DEBUG_PRINT_FN(); } + +void RAMPage::init() { + DEBUG_PRINT_FN(); +#ifdef OLED_DISPLAY + // classic_display = false; + oled_display.clearDisplay(); + oled_display.setFont(); +#endif + encoders[0]->cur = 100; + md_exploit.off(); +} + +void RAMPage::cleanup() { + // md_exploit.off(); +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif +} +void RAMPage::setup_sequencer(uint8_t track) { + + USE_LOCK(); + SET_LOCK(); + mcl_seq.md_tracks[track].pattern_mask = 1; + mcl_seq.md_tracks[track].length = encoders[3]->cur * 4; + CLEAR_LOCK(); +} + +void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, + uint8_t len, uint8_t rate, uint8_t pan, + uint8_t linked_track) { + MDTrackLight md_track; + + memset(&(md_track.seq_data), 0, sizeof(MDSeqTrackData)); + memset(&(md_track.machine.params), 255, 24); + + uint16_t steps = encoders[3]->cur * 4; + + md_track.active = MD_TRACK_TYPE; + md_track.machine.model = model; + md_track.machine.params[RAM_R_MLEV] = mlev; + md_track.machine.params[RAM_R_MBAL] = pan; + md_track.machine.params[RAM_R_ILEV] = 0; + md_track.machine.params[RAM_R_LEN] = encoders[3]->cur * 32 - 1; + md_track.machine.params[RAM_R_RATE] = 127; + md_track.machine.params[MODEL_AMD] = 0; + md_track.machine.params[MODEL_AMF] = 0; + md_track.machine.params[MODEL_EQF] = 64; + md_track.machine.params[MODEL_EQG] = 64; + md_track.machine.params[MODEL_FLTF] = 0; + md_track.machine.params[MODEL_FLTW] = 127; + md_track.machine.params[MODEL_FLTQ] = 0; + md_track.machine.params[MODEL_SRR] = 0; + md_track.machine.params[MODEL_DIST] = 0; + md_track.machine.params[MODEL_VOL] = 127; + md_track.machine.params[MODEL_PAN] = pan; + md_track.machine.params[MODEL_DEL] = 0; + md_track.machine.params[MODEL_REV] = 0; + md_track.machine.params[MODEL_LFOS] = 64; + md_track.machine.params[MODEL_LFOD] = 0; + md_track.machine.params[MODEL_LFOM] = 0; + md_track.machine.lfo.destinationTrack = track; + + if (linked_track == 255) { + md_track.machine.trigGroup = 255; + md_track.seq_data.pattern_mask = 0; + } else if (track > linked_track) { + md_track.machine.trigGroup = linked_track; + md_track.seq_data.pattern_mask = 1; + //oneshot + md_track.seq_data.conditional[0] = 14; + } else { + md_track.machine.trigGroup = 255; + md_track.seq_data.pattern_mask = 0; + } + + md_track.machine.muteGroup = 127; + md_track.seq_data.length = (uint8_t)steps; + md_track.chain.loops = 0; + md_track.chain.row = mcl_actions.chains[track].row; + + md_track.store_in_mem(track); + + grid_page.active_slots[track] = 0x7FFF; + mcl_actions.chains[track].row = SLOT_RAM_RECORD; + mcl_actions.chains[track].loops = 1; + uint16_t next_step = (MidiClock.div16th_counter / steps) * steps + steps; + /* + if (MD.kit.models[track] == md_track.machine.model) { + mcl_actions.send_machine[track] = 1; } else { + mcl_actions.send_machine[track] = 0; } + */ + // mcl_actions.calc_next_slot_transition(track); + mcl_actions.send_machine[track] = 0; + mcl_actions.next_transitions[track] = next_step; + mcl_actions.transition_level[track] = TRANSITION_UNMUTE; + + mcl_actions.calc_next_transition(); + + EmptyTrack empty_track; + mcl_actions.calc_latency(&empty_track); +} + +void RAMPage::reverse(uint8_t track) { + uint8_t model = (MD.kit.models[track]); + + if (model != RAM_P1_MODEL && model != RAM_P2_MODEL && model != RAM_P3_MODEL && + model != RAM_P4_MODEL) { + return; + } + if (magic == 0) { + MD.setTrackParam(track, ROM_STRT, 127); + MD.setTrackParam(track, ROM_END, 0); + } + if (magic == 1) { + MD.setTrackParam(track, ROM_STRT, 0); + MD.setTrackParam(track, ROM_END, 127); + } +} + +bool RAMPage::slice(uint8_t track, uint8_t linked_track) { + uint8_t model = (MD.kit.models[track]); + + if (grid_page.active_slots[track] != SLOT_RAM_PLAY) { + return false; + } + uint8_t slices = 1 << encoders[2]->cur; + + uint8_t sample_inc = 128 / slices; + uint8_t track_length = encoders[3]->cur * 4; + uint8_t step_inc = track_length / slices; + + mcl_seq.md_tracks[track].clear_track(); + + mcl_seq.md_tracks[track].locks_params[0] = ROM_STRT + 1; + mcl_seq.md_tracks[track].locks_params[1] = ROM_END + 1; + uint8_t mode = encoders[1]->cur; + + for (uint8_t s = 0; s < slices; s++) { + uint8_t n = s * step_inc; + + if ((linked_track < track) || (linked_track == 255)) { + SET_BIT64(mcl_seq.md_tracks[track].pattern_mask, n); + } + SET_BIT64(mcl_seq.md_tracks[track].lock_mask, n); + if (linked_track < track) { + mcl_seq.md_tracks[track].locks[0][n] = mcl_seq.md_tracks[linked_track].locks[0][n]; + mcl_seq.md_tracks[track].locks[1][n] = mcl_seq.md_tracks[linked_track].locks[1][n]; + } + else if (magic == 0) { + mcl_seq.md_tracks[track].locks[0][n] = sample_inc * s + 1; + mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (s + 1) + 1; + if (mcl_seq.md_tracks[track].locks[1][n] > 128) { + mcl_seq.md_tracks[track].locks[1][n] = 128; + } + } else { + switch (mode) { + default: + // Reverse + mcl_seq.md_tracks[track].locks[1][n] = sample_inc * s + 1; + mcl_seq.md_tracks[track].locks[0][n] = (sample_inc) * (s + 1) + 1; + if (mcl_seq.md_tracks[track].locks[0][n] > 128) { + mcl_seq.md_tracks[track].locks[0][n] = 128; + } + break; + case 6: + + mcl_seq.md_tracks[track].locks[0][n] = sample_inc * (slices - s) + 1; + mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (slices -s + 1) + 1; + if (mcl_seq.md_tracks[track].locks[1][n] > 128) { + mcl_seq.md_tracks[track].locks[1][n] = 128; + } + + break; + case 7: + + uint8_t t; + t = random(0,slices); + mcl_seq.md_tracks[track].locks[0][n] = sample_inc * (t) + 1; + mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (t + 1) + 1; + if (mcl_seq.md_tracks[track].locks[1][n] > 128) { + mcl_seq.md_tracks[track].locks[1][n] = 128; + } + + break; + case 4: + case 3: + case 2: + case 1: + case 5: + // Revers every 2nd. + // + uint8_t m = mode; + + // Random + if (m == 4) { + if (get_random_byte() > 64) { + m = s; + } else { + m = s + 1; + } + } + else if (m == 5) { + if (IS_BIT_SET64(mcl_seq.md_tracks[0].pattern_mask, n)) { + m = s; + } else { + m = m + 1; + } + } + + + + else { + while (m > slices) { + m--; + } + } + if (s % m == 0) { + mcl_seq.md_tracks[track].locks[1][n] = sample_inc * s + 1; + mcl_seq.md_tracks[track].locks[0][n] = (sample_inc) * (s + 1) + 1; + if (mcl_seq.md_tracks[track].locks[0][n] > 128) { + mcl_seq.md_tracks[track].locks[0][n] = 128; + } + } else { + mcl_seq.md_tracks[track].locks[0][n] = sample_inc * s + 1; + mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (s + 1) + 1; + if (mcl_seq.md_tracks[track].locks[1][n] > 128) { + mcl_seq.md_tracks[track].locks[1][n] = 128; + } + } + break; + } + } + } + return true; +} + +void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, + uint8_t linked_track) { + MDTrackLight md_track; + + memset(&(md_track.seq_data), 0, sizeof(MDSeqTrackData)); + memset(&(md_track.machine.params), 255, 24); + + uint16_t steps = encoders[3]->cur * 4; + + md_track.active = MD_TRACK_TYPE; + md_track.machine.model = model; + /* + md_track.machine.params[ROM_PTCH] = 64; + md_track.machine.params[ROM_DEC] = 64; + md_track.machine.params[ROM_HOLD] = 127; + md_track.machine.params[ROM_BRR] = 0; + md_track.machine.params[ROM_STRT] = 0; + md_track.machine.params[ROM_END] = 127; + md_track.machine.params[ROM_RTRG] = 0; + md_track.machine.params[ROM_RTIM] = 127; + */ + md_track.machine.params[MODEL_AMD] = 0; + md_track.machine.params[MODEL_AMF] = 0; + md_track.machine.params[MODEL_EQF] = 64; + md_track.machine.params[MODEL_EQG] = 64; + md_track.machine.params[MODEL_FLTF] = 0; + md_track.machine.params[MODEL_FLTW] = 127; + md_track.machine.params[MODEL_FLTQ] = 0; + md_track.machine.params[MODEL_SRR] = 0; + md_track.machine.params[MODEL_DIST] = 0; + md_track.machine.params[MODEL_VOL] = 127; + md_track.machine.params[MODEL_PAN] = pan; + md_track.machine.params[MODEL_DEL] = 0; + md_track.machine.params[MODEL_REV] = 0; + md_track.machine.params[MODEL_LFOS] = 64; + md_track.machine.params[MODEL_LFOD] = 0; + md_track.machine.params[MODEL_LFOM] = 0; + + if (linked_track == 255) { + md_track.machine.trigGroup = 255; + md_track.seq_data.pattern_mask = 0; + } else if (track > linked_track) { + md_track.machine.trigGroup = linked_track; + md_track.seq_data.pattern_mask = 1; + } else { + md_track.machine.trigGroup = 255; + md_track.seq_data.pattern_mask = 0; + } + md_track.machine.muteGroup = 127; + + md_track.seq_data.length = (uint8_t)steps; + uint8_t magic = encoders[1]->cur; + md_track.chain.loops = 0; + md_track.chain.row = mcl_actions.chains[track].row; + md_track.machine.params[MODEL_LFOD] = 0; + md_track.machine.lfo.destinationTrack = track; + + md_track.store_in_mem(track); + + mcl_actions.chains[track].row = SLOT_RAM_PLAY; + mcl_actions.chains[track].loops = 1; + mcl_actions.send_machine[track] = 0; + + uint16_t next_step; + uint8_t m = mcl_seq.md_tracks[track].length; + + next_step = + MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); + grid_page.active_slots[track] = 0x7FFF; + mcl_actions.transition_level[track] = TRANSITION_MUTE; + mcl_actions.next_transitions[track] = next_step; + + mcl_actions.calc_next_transition(); + + EmptyTrack empty_track; + mcl_actions.calc_latency(&empty_track); +} + +void RAMPage::setup_ram_play_mono(uint8_t track) { + magic = 0; + setup_ram_play(track, RAM_P1_MODEL, 64); +} +void RAMPage::setup_ram_play_stereo(uint8_t track) { + if (track == 15) { + return; + } + + magic = 0; + setup_ram_play(track, RAM_P1_MODEL, 0, track + 1); + setup_ram_play(track + 1, RAM_P2_MODEL, 127, track); +} + +void RAMPage::setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, + uint8_t rate) { + setup_ram_rec(track, RAM_R1_MODEL, mlev, len, rate, 63); +} + +void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, + uint8_t rate) { + if (track == 15) { + return; + } + setup_ram_rec(track, RAM_R1_MODEL, mlev, len, rate, 0, track + 1); + setup_ram_rec(track + 1, RAM_R2_MODEL, mlev, len, rate, 127, track); +} + +void RAMPage::display() { + + if (!classic_display) { +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif + } + GUI.setLine(GUI.LINE1); + uint8_t x; + // GUI.put_string_at(12,"RAM"); + GUI.put_string_at(0, "RAM "); + GUI.setLine(GUI.LINE2); + + if (encoders[0]->cur == 0) { + GUI.put_string_at(0, "MONO"); + } else { + GUI.put_string_at(0, "STER"); + } + + GUI.put_value_at(5, encoders[1]->cur); + GUI.put_value_at(9, 1 << encoders[2]->cur); + GUI.put_value_at(13, encoders[3]->cur); + /* + GUI.put_value_at1(8,msb); + GUI.put_string_at(9,"."); + GUI.put_value_at1(10,mantissa / 10); + GUI.put_value_at1(11,mantissa % 10); +*/ +#ifdef OLED_DISPLAY +#endif +} + +bool RAMPage::handleEvent(gui_event_t *event) { + if (note_interface.is_event(event)) { + uint8_t track = event->source - 128; + if (midi_active_peering.get_device(event->port) != DEVICE_MD) { + return true; + } + } + if (event->mask == EVENT_BUTTON_RELEASED) { + return true; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || + EVENT_PRESSED(event, Buttons.ENCODER2) || + EVENT_PRESSED(event, Buttons.ENCODER3) || + EVENT_PRESSED(event, Buttons.ENCODER4)) { + GUI.setPage(&grid_page); + } + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + if (encoders[0]->cur == 0) { + setup_ram_rec_mono(15, 64, 4 * encoders[3]->cur - 1, 128); + } else { + setup_ram_rec_stereo(14, 64, 4 * encoders[3]->cur - 1, 128); + } + } + + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + magic = 1; + if (encoders[0]->cur == 0) { + slice(15, 255); + } else { + slice(14, 15); + slice(15, 14); + } + } + + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + magic = 0; + if (encoders[0]->cur == 0) { + if (!slice(15, 255)) { + setup_ram_play_mono(15); + } + } else { + slice(14, 15); + if (!slice(15, 14)) { + setup_ram_play_stereo(14); + } + } + } + + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + GUI.setPage(&grid_page); + return true; + } + + return false; +} + +MCLEncoder ram_param1(0, 1, 2); +MCLEncoder ram_param2(0, 255, 2); +MCLEncoder ram_param3(0, 5, 2); +MCLEncoder ram_param4(1, 8, 2); + +RAMPage ram_page(&ram_param1, &ram_param2, &ram_param3, &ram_param4); diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h new file mode 100644 index 000000000..558558f0a --- /dev/null +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -0,0 +1,43 @@ +/* Justin Mammarella jmamma@gmail.com 2018 */ + +#ifndef RAMPAGE_H__ +#define RAMPAGE_H__ + +#include "MCLEncoder.h" +#include "GUI.h" + +#define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 +#define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 + +class RAMPage : public LightPage { +public: + RAMPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, + Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) { + } + + bool handleEvent(gui_event_t *event); + uint8_t magic; + void display(); + void setup(); + void init(); + void cleanup(); + void setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, uint8_t len, uint8_t rate, uint8_t pan, uint8_t linked_track = 255); + void setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, uint8_t rate); + void setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, uint8_t rate); + void setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, uint8_t linked_track = 255); + + void setup_ram_play_mono(uint8_t track); + void setup_ram_play_stereo(uint8_t track); + + void reverse(uint8_t track); + bool slice(uint8_t track, uint8_t linked_track); + void setup_sequencer(uint8_t track); +}; + +extern MCLEncoder ram_param1; +extern MCLEncoder ram_param2; +extern MCLEncoder ram_param3; +extern MCLEncoder ram_param4; + +#endif /* RAMPAGE_H__ */ From 8b1fca2c02182fbbc0da851c58e2e9f2423682e7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 16 Jun 2019 00:20:40 +1000 Subject: [PATCH 006/469] Add stereo track parameter linking to RAMPage --- avr/cores/megacommand/MCL/RAMPage.cpp | 48 ++++++++++++++++++++++++++- avr/cores/megacommand/MCL/RAMPage.h | 9 ++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 8ffbffe3e..5550cdeb4 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -12,6 +12,7 @@ void RAMPage::init() { #endif encoders[0]->cur = 100; md_exploit.off(); + setup_callbacks(); } void RAMPage::cleanup() { @@ -44,7 +45,7 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, md_track.machine.params[RAM_R_MLEV] = mlev; md_track.machine.params[RAM_R_MBAL] = pan; md_track.machine.params[RAM_R_ILEV] = 0; - md_track.machine.params[RAM_R_LEN] = encoders[3]->cur * 32 - 1; + md_track.machine.params[RAM_R_LEN] = encoders[3]->cur * 16 - 1; md_track.machine.params[RAM_R_RATE] = 127; md_track.machine.params[MODEL_AMD] = 0; md_track.machine.params[MODEL_AMF] = 0; @@ -375,6 +376,51 @@ void RAMPage::display() { #ifdef OLED_DISPLAY #endif } +void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { + uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); + uint8_t param = msg[1]; + uint8_t value = msg[2]; + uint8_t track; + uint8_t track_param; + // If external keyboard controlling MD pitch, send parameter updates + // to all polyphonic tracks + uint8_t param_true = 0; + + MD.parseCC(channel, param, &track, &track_param); + + if (grid_page.active_slots[track] != SLOT_RAM_PLAY) { return; } + + for (uint8_t n = 0; n < 16; n++) { + + if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (n != track)) { + MD.setTrackParam(n, track_param, value); + } + // in_sysex = 0; + } +} + +void RAMPage::setup_callbacks() { + if (midi_state) { + return; + } + Midi.addOnControlChangeCallback( + this, + (midi_callback_ptr_t)&RAMPage::onControlChangeCallback_Midi); + + midi_state = true; +} + +void RAMPage::remove_callbacks() { + if (!midi_state) { + return; + } + + Midi.removeOnControlChangeCallback( + this, + (midi_callback_ptr_t)&RAMPage::onControlChangeCallback_Midi); + + midi_state = false; +} bool RAMPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index 558558f0a..feff2b532 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -9,7 +9,7 @@ #define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 #define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 -class RAMPage : public LightPage { +class RAMPage : public LightPage, MidiCallback { public: RAMPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) @@ -17,7 +17,9 @@ class RAMPage : public LightPage { } bool handleEvent(gui_event_t *event); + bool midi_state = false; uint8_t magic; + void display(); void setup(); void init(); @@ -33,6 +35,11 @@ class RAMPage : public LightPage { void reverse(uint8_t track); bool slice(uint8_t track, uint8_t linked_track); void setup_sequencer(uint8_t track); + + void setup_callbacks(); + void remove_callbacks(); + + void onControlChangeCallback_Midi(uint8_t *msg); }; extern MCLEncoder ram_param1; From 7ae98136dad25fcb7063ea8af98abd5d1f1aa65b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 16 Jun 2019 18:29:52 +1000 Subject: [PATCH 007/469] Show record status as either queue, recording or playback --- avr/cores/megacommand/MCL/RAMPage.cpp | 109 ++++++++++++++++---------- 1 file changed, 68 insertions(+), 41 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 5550cdeb4..c60167e99 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -71,7 +71,7 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, } else if (track > linked_track) { md_track.machine.trigGroup = linked_track; md_track.seq_data.pattern_mask = 1; - //oneshot + // oneshot md_track.seq_data.conditional[0] = 14; } else { md_track.machine.trigGroup = 255; @@ -148,10 +148,11 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { } SET_BIT64(mcl_seq.md_tracks[track].lock_mask, n); if (linked_track < track) { - mcl_seq.md_tracks[track].locks[0][n] = mcl_seq.md_tracks[linked_track].locks[0][n]; - mcl_seq.md_tracks[track].locks[1][n] = mcl_seq.md_tracks[linked_track].locks[1][n]; - } - else if (magic == 0) { + mcl_seq.md_tracks[track].locks[0][n] = + mcl_seq.md_tracks[linked_track].locks[0][n]; + mcl_seq.md_tracks[track].locks[1][n] = + mcl_seq.md_tracks[linked_track].locks[1][n]; + } else if (magic == 0) { mcl_seq.md_tracks[track].locks[0][n] = sample_inc * s + 1; mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (s + 1) + 1; if (mcl_seq.md_tracks[track].locks[1][n] > 128) { @@ -168,25 +169,26 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { } break; case 6: - - mcl_seq.md_tracks[track].locks[0][n] = sample_inc * (slices - s) + 1; - mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (slices -s + 1) + 1; - if (mcl_seq.md_tracks[track].locks[1][n] > 128) { - mcl_seq.md_tracks[track].locks[1][n] = 128; - } - break; + mcl_seq.md_tracks[track].locks[0][n] = sample_inc * (slices - s) + 1; + mcl_seq.md_tracks[track].locks[1][n] = + (sample_inc) * (slices - s + 1) + 1; + if (mcl_seq.md_tracks[track].locks[1][n] > 128) { + mcl_seq.md_tracks[track].locks[1][n] = 128; + } + + break; case 7: - uint8_t t; - t = random(0,slices); - mcl_seq.md_tracks[track].locks[0][n] = sample_inc * (t) + 1; - mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (t + 1) + 1; - if (mcl_seq.md_tracks[track].locks[1][n] > 128) { - mcl_seq.md_tracks[track].locks[1][n] = 128; - } + uint8_t t; + t = random(0, slices); + mcl_seq.md_tracks[track].locks[0][n] = sample_inc * (t) + 1; + mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (t + 1) + 1; + if (mcl_seq.md_tracks[track].locks[1][n] > 128) { + mcl_seq.md_tracks[track].locks[1][n] = 128; + } - break; + break; case 4: case 3: case 2: @@ -203,8 +205,7 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { } else { m = s + 1; } - } - else if (m == 5) { + } else if (m == 5) { if (IS_BIT_SET64(mcl_seq.md_tracks[0].pattern_mask, n)) { m = s; } else { @@ -212,8 +213,6 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { } } - - else { while (m > slices) { m--; @@ -306,7 +305,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, uint8_t m = mcl_seq.md_tracks[track].length; next_step = - MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); + MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); grid_page.active_slots[track] = 0x7FFF; mcl_actions.transition_level[track] = TRANSITION_MUTE; mcl_actions.next_transitions[track] = next_step; @@ -352,10 +351,38 @@ void RAMPage::display() { oled_display.clearDisplay(); #endif } + GUI.clearLines(); GUI.setLine(GUI.LINE1); uint8_t x; // GUI.put_string_at(12,"RAM"); - GUI.put_string_at(0, "RAM "); + GUI.put_string_at(0, "RAM"); + uint8_t record_state = 0; + + for (uint8_t n = NUM_MD_TRACKS - 1; n > 0 && record_state == 0; n--) { + if (mcl_actions.chains[n].row == SLOT_RAM_RECORD || mcl_actions.chains[n].row == SLOT_RAM_PLAY) { + record_state = 1; + } + else if ((grid_page.active_slots[n] == SLOT_RAM_RECORD) && + (mcl_seq.md_tracks[n].oneshot_mask == 0)) { + record_state = 2; + } + else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY)) { + record_state = 3; + } + // in_sysex = 0; + } + switch (record_state) { + case 1: + GUI.put_string_at(5, "[Queue]"); + break; + case 2: + GUI.put_string_at(5, "[Recording]"); + break; + case 3: + GUI.put_string_at(5, "[Playback]"); + break; + } + GUI.setLine(GUI.LINE2); if (encoders[0]->cur == 0) { @@ -367,11 +394,11 @@ void RAMPage::display() { GUI.put_value_at(5, encoders[1]->cur); GUI.put_value_at(9, 1 << encoders[2]->cur); GUI.put_value_at(13, encoders[3]->cur); - /* - GUI.put_value_at1(8,msb); - GUI.put_string_at(9,"."); - GUI.put_value_at1(10,mantissa / 10); - GUI.put_value_at1(11,mantissa % 10); +/* +GUI.put_value_at1(8,msb); +GUI.put_string_at(9,"."); +GUI.put_value_at1(10,mantissa / 10); +GUI.put_value_at1(11,mantissa % 10); */ #ifdef OLED_DISPLAY #endif @@ -388,14 +415,16 @@ void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { MD.parseCC(channel, param, &track, &track_param); - if (grid_page.active_slots[track] != SLOT_RAM_PLAY) { return; } + if (grid_page.active_slots[track] != SLOT_RAM_PLAY) { + return; + } - for (uint8_t n = 0; n < 16; n++) { + for (uint8_t n = 0; n < 16; n++) { - if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (n != track)) { - MD.setTrackParam(n, track_param, value); - } - // in_sysex = 0; + if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (n != track)) { + MD.setTrackParam(n, track_param, value); + } + // in_sysex = 0; } } @@ -404,8 +433,7 @@ void RAMPage::setup_callbacks() { return; } Midi.addOnControlChangeCallback( - this, - (midi_callback_ptr_t)&RAMPage::onControlChangeCallback_Midi); + this, (midi_callback_ptr_t)&RAMPage::onControlChangeCallback_Midi); midi_state = true; } @@ -416,8 +444,7 @@ void RAMPage::remove_callbacks() { } Midi.removeOnControlChangeCallback( - this, - (midi_callback_ptr_t)&RAMPage::onControlChangeCallback_Midi); + this, (midi_callback_ptr_t)&RAMPage::onControlChangeCallback_Midi); midi_state = false; } From 32dd96ba391d252f879a1c448b9b5a15a250abe5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 16 Jun 2019 22:28:15 +1000 Subject: [PATCH 008/469] Show record/playback state. fix mono mode --- avr/cores/megacommand/MCL/RAMPage.cpp | 54 ++++++++++++++++----------- avr/cores/megacommand/MCL/RAMPage.h | 1 + 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index c60167e99..bf4d9e2eb 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -1,6 +1,11 @@ #include "MCL.h" #include "RAMPage.h" +#define STATE_NOSTATE 0 +#define STATE_QUEUE 1 +#define STATE_RECORD 2 +#define STATE_PLAY 3 + void RAMPage::setup() { DEBUG_PRINT_FN(); } void RAMPage::init() { @@ -39,7 +44,7 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, memset(&(md_track.machine.params), 255, 24); uint16_t steps = encoders[3]->cur * 4; - + rec_state = STATE_QUEUE; md_track.active = MD_TRACK_TYPE; md_track.machine.model = model; md_track.machine.params[RAM_R_MLEV] = mlev; @@ -67,7 +72,8 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, if (linked_track == 255) { md_track.machine.trigGroup = 255; - md_track.seq_data.pattern_mask = 0; + md_track.seq_data.pattern_mask = 1; + md_track.seq_data.conditional[0] = 14; } else if (track > linked_track) { md_track.machine.trigGroup = linked_track; md_track.seq_data.pattern_mask = 1; @@ -247,6 +253,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, uint16_t steps = encoders[3]->cur * 4; + rec_state = STATE_QUEUE; md_track.active = MD_TRACK_TYPE; md_track.machine.model = model; /* @@ -278,7 +285,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, if (linked_track == 255) { md_track.machine.trigGroup = 255; - md_track.seq_data.pattern_mask = 0; + md_track.seq_data.pattern_mask = 1; } else if (track > linked_track) { md_track.machine.trigGroup = linked_track; md_track.seq_data.pattern_mask = 1; @@ -356,31 +363,34 @@ void RAMPage::display() { uint8_t x; // GUI.put_string_at(12,"RAM"); GUI.put_string_at(0, "RAM"); - uint8_t record_state = 0; - - for (uint8_t n = NUM_MD_TRACKS - 1; n > 0 && record_state == 0; n--) { - if (mcl_actions.chains[n].row == SLOT_RAM_RECORD || mcl_actions.chains[n].row == SLOT_RAM_PLAY) { - record_state = 1; - } - else if ((grid_page.active_slots[n] == SLOT_RAM_RECORD) && - (mcl_seq.md_tracks[n].oneshot_mask == 0)) { - record_state = 2; - } - else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY)) { - record_state = 3; + uint8_t break_loop = 0; + for (uint8_t n = NUM_MD_TRACKS - 1; n > 0 && break_loop == 0; n--) { + if ((grid_page.active_slots[n] == SLOT_RAM_RECORD) && + (mcl_seq.md_tracks[n].step_count == 0)) { + if (rec_state == STATE_QUEUE) { + rec_state = STATE_RECORD; + } else if ((rec_state == STATE_RECORD) && + (mcl_seq.md_tracks[n].oneshot_mask != 0)) { + rec_state = STATE_NOSTATE; + } + break_loop = 1; + } else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && + (mcl_seq.md_tracks[n].step_count == 0)) { + rec_state = STATE_PLAY; + break_loop = 1; } // in_sysex = 0; } - switch (record_state) { - case 1: + switch (rec_state) { + case STATE_QUEUE: GUI.put_string_at(5, "[Queue]"); - break; - case 2: + break; + case STATE_RECORD: GUI.put_string_at(5, "[Recording]"); - break; - case 3: + break; + case STATE_PLAY: GUI.put_string_at(5, "[Playback]"); - break; + break; } GUI.setLine(GUI.LINE2); diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index feff2b532..3fe8409d6 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -19,6 +19,7 @@ class RAMPage : public LightPage, MidiCallback { bool handleEvent(gui_event_t *event); bool midi_state = false; uint8_t magic; + uint8_t rec_state; void display(); void setup(); From 96e746192acbdfb8a801f0c8aebd6d681dbd501d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 16 Jun 2019 22:58:08 +1000 Subject: [PATCH 009/469] Improvements to record and playback.. Apparently len = 127 is slightly too short for 8 bars. len = 64 records 4 bars perfectly. reverse now works seemlessly. --- avr/cores/megacommand/MCL/RAMPage.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index bf4d9e2eb..6ffe434f0 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -50,7 +50,8 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, md_track.machine.params[RAM_R_MLEV] = mlev; md_track.machine.params[RAM_R_MBAL] = pan; md_track.machine.params[RAM_R_ILEV] = 0; - md_track.machine.params[RAM_R_LEN] = encoders[3]->cur * 16 - 1; + md_track.machine.params[RAM_R_LEN] = encoders[3]->cur * 16; + if (md_track.machine.params[RAM_R_LEN] > 127) { md_track.machine.params[RAM_R_LEN] = 127; } md_track.machine.params[RAM_R_RATE] = 127; md_track.machine.params[MODEL_AMD] = 0; md_track.machine.params[MODEL_AMF] = 0; @@ -139,8 +140,9 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { uint8_t sample_inc = 128 / slices; uint8_t track_length = encoders[3]->cur * 4; uint8_t step_inc = track_length / slices; - - mcl_seq.md_tracks[track].clear_track(); + bool clear_locks = true; + bool send_params = false; + mcl_seq.md_tracks[track].clear_track(clear_locks, send_params); mcl_seq.md_tracks[track].locks_params[0] = ROM_STRT + 1; mcl_seq.md_tracks[track].locks_params[1] = ROM_END + 1; From 692774ba85f057e0b8bb1543fc96869dd54f81ee Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 18 Jun 2019 23:12:58 +1000 Subject: [PATCH 010/469] Deprecate MutePage !Routing is now used for muting. I've given up on obtaining the MD Mute state. The audio routing functionality is an alternative to muting and works well. Removing the MutePage simplifies the firmware and consolidates audio manipulation to 2 pages, (MixerPage and RoutePage)). Tracks can be "audio-muted' from the MixerPage by holding "Write" and selecting tracks from the MixerPage. --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 7500b8583..65ea7c019 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -2,9 +2,8 @@ #include "PageSelectPage.h" #define MIX_PAGE 0 -#define ROUTE_PAGE 1 #define RAM_PAGE 4 -#define ROUTE_PAGE 2 +#define ROUTE_PAGE 1 #define WAVD_PAGE 8 #define SOUND 7 From 9b89e753f036d7ad99c6f85425911daadb63ec66 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 19 Jun 2019 12:37:52 +1000 Subject: [PATCH 011/469] remove references to MutePage --- avr/cores/megacommand/MCL/AuxPages.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index 81700bd21..1be626882 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -7,5 +7,6 @@ extern MCLEncoder mixer_param4(0, 127); extern MCLEncoder route_param1(2,5); extern MCLEncoder route_param2(0,6); + MixerPage mixer_page(&mixer_param1, &mixer_param2, &mixer_param3, &mixer_param4); RoutePage route_page(&route_param1, &route_param2, &route_param2); From 2891a92f520c5fcac32653ebc2cc2c1c8e67a1ad Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 14 Jun 2019 23:49:38 +1000 Subject: [PATCH 012/469] Add RAMPage, proof of concept --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 65ea7c019..19b6cc5a5 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -4,6 +4,8 @@ #define MIX_PAGE 0 #define RAM_PAGE 4 #define ROUTE_PAGE 1 +#define RAM_PAGE 4 +#define ROUTE_PAGE 2 #define WAVD_PAGE 8 #define SOUND 7 From 9f678b888c3e45446bf09c53df4397573cfcedce Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 19 Jun 2019 17:07:22 +1000 Subject: [PATCH 013/469] fix PageSelect text for RAM --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 19b6cc5a5..b748e3a45 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -38,7 +38,7 @@ LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { break; case RAM_PAGE: if (str) - strncpy(str, "RAM ", 5); + strncpy(str, "RAM", 4); r_page = &ram_page; break; #ifdef WAV_DESIGNER From f08b2531f2c984f1a39d927b7c2fd5cad953ddb0 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 22 Jun 2019 13:44:39 +1000 Subject: [PATCH 014/469] Fix bad merge --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index b748e3a45..3732e889f 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -2,10 +2,8 @@ #include "PageSelectPage.h" #define MIX_PAGE 0 -#define RAM_PAGE 4 #define ROUTE_PAGE 1 #define RAM_PAGE 4 -#define ROUTE_PAGE 2 #define WAVD_PAGE 8 #define SOUND 7 From 29de2a5b47844384d54d30170720c0374e1e425e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 22 Jun 2019 19:04:07 +1000 Subject: [PATCH 015/469] Panning stereo tracks now adjusts width and lev --- avr/cores/megacommand/MCL/RAMPage.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 6ffe434f0..ac16e6fff 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -316,7 +316,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, next_step = MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); grid_page.active_slots[track] = 0x7FFF; - mcl_actions.transition_level[track] = TRANSITION_MUTE; + //mcl_actions.transition_level[track] = TRANSITION_MUTE; mcl_actions.next_transitions[track] = next_step; mcl_actions.calc_next_transition(); @@ -434,7 +434,24 @@ void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { for (uint8_t n = 0; n < 16; n++) { if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (n != track)) { - MD.setTrackParam(n, track_param, value); + if (track_param == MODEL_PAN) { + if (n < track) { + MD.setTrackParam(n, track_param,127 - value); + //Pan law. + uint8_t lev = ((float)(value - 64) / (float)64) * (float)27 + 100; + MD.setTrackParam(track, MODEL_VOL, lev); + MD.setTrackParam(n, MODEL_VOL, lev); + } + else { + MD.setTrackParam(n, track_param,63 + (64 - value)); + uint8_t lev = ((float)(64 - value) / (float)64) * (float)27 + 100; + MD.setTrackParam(track, MODEL_VOL, lev); + MD.setTrackParam(n, MODEL_VOL, lev); + } + } + else { + MD.setTrackParam(n, track_param, value); + } } // in_sysex = 0; } @@ -510,7 +527,7 @@ bool RAMPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { - GUI.setPage(&grid_page); + GUI.setPage(&page_select_page); return true; } From ccc1110aa9d76e471483c42d5b5a0ea6bbd770c8 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 8 Aug 2019 22:36:59 +1000 Subject: [PATCH 016/469] Copy/paste revamp, adds clipboard func. Copy/paste multiple rows using selection rectangle Encoder 3 = width Encoder 4 = height Copied data is written to a temporary file on the SDCard. This means slot data can be copied between projects. --- avr/cores/megacommand/CommonTools/helpers.c | 5 + avr/cores/megacommand/CommonTools/helpers.h | 2 +- avr/cores/megacommand/MCL/GridPage.cpp | 85 ++++++---- avr/cores/megacommand/MCL/GridPages.cpp | 4 +- avr/cores/megacommand/MCL/MCL.h | 1 + avr/cores/megacommand/MCL/MCLClipBoard.cpp | 162 ++++++++++++++++++++ avr/cores/megacommand/MCL/MCLClipBoard.h | 30 ++++ 7 files changed, 254 insertions(+), 35 deletions(-) create mode 100644 avr/cores/megacommand/MCL/MCLClipBoard.cpp create mode 100644 avr/cores/megacommand/MCL/MCLClipBoard.h diff --git a/avr/cores/megacommand/CommonTools/helpers.c b/avr/cores/megacommand/CommonTools/helpers.c index 79681116c..1c1af1059 100644 --- a/avr/cores/megacommand/CommonTools/helpers.c +++ b/avr/cores/megacommand/CommonTools/helpers.c @@ -379,6 +379,11 @@ uint16_t clock_diff(uint16_t old_clock, uint16_t new_clock) { * @{ **/ +//Determine if co-ordinate x,y is within rectangular area +bool in_area(int x, int y, int x2, int y2, int w, int h) { + return (x >= x2) && (x <= x2 + w) && (y >= y2) && (y <= y2 + h); +} + /** * Map x from the range in_min - in_max to the range out_min - out_max. * diff --git a/avr/cores/megacommand/CommonTools/helpers.h b/avr/cores/megacommand/CommonTools/helpers.h index d8ce273e7..3ca8edbf8 100644 --- a/avr/cores/megacommand/CommonTools/helpers.h +++ b/avr/cores/megacommand/CommonTools/helpers.h @@ -131,7 +131,7 @@ extern const uint32_t _bvmasks32[]; //#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) #endif //long map(long x, long in_min, long in_max, long out_min, long out_max); - +bool in_area(int x, int y, int x2, int y2, int w, int h); uint8_t u_limit_value(uint8_t value, int8_t encoder, uint8_t min, uint8_t max); int limit_value(int value, int encoder, int min, int max); uint8_t interpolate_8(uint8_t start, uint8_t end, uint8_t amount); diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index c4f088a43..d8f59b33c 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -40,12 +40,30 @@ void GridPage::loop() { int8_t diff, new_val; #ifdef OLED_DISPLAY if (show_slot_menu) { - grid_slot_page.loop(); + + if (encoders[3]->hasChanged()) { + if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { + load_slot_models(); + reload_slot_models = true; + } + + } + grid_slot_page.loop(); return; } else { } - - if (encoders[0]->hasChanged()) { + /* + if (encoders[2]->hasChanged()) { + diff = encoders[2]->cur - encoders[2]->old; + if (cur_col + encoders[2]->cur > MAX_VISIBLE_COLS - 1) { + encoders[0]->cur += diff; + encoders[0]->old = encoders[0]->cur; + } + } +*/ + encoders[2]->cur = 1; + encoders[3]->cur = 1; + if (encoders[0]->hasChanged()) { diff = encoders[0]->cur - encoders[0]->old; new_val = cur_col + diff; if (new_val > MAX_VISIBLE_COLS - 1) { @@ -57,7 +75,7 @@ void GridPage::loop() { cur_col = new_val; } - if (encoders[1]->hasChanged()) { + if (encoders[1]->hasChanged()) { diff = encoders[1]->cur - encoders[1]->old; new_val = cur_row + diff; @@ -178,8 +196,13 @@ uint8_t GridPage::getCol() { return param1.cur; } void GridPage::load_slot_models() { #ifdef OLED_DISPLAY + uint8_t row_shift = 0; + if ((cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1)) { + row_shift = cur_row + encoders[3]->cur - MAX_VISIBLE_ROWS; + } + for (uint8_t n = 0; n < MAX_VISIBLE_ROWS; n++) { - row_headers[n].read(getRow() - cur_row + n); + row_headers[n].read(getRow() - cur_row + n + row_shift); } #else @@ -371,15 +394,25 @@ void GridPage::display_grid() { char str[3]; PGM_P tmp; encoders[1]->handler = NULL; - // oled_display.setFont(&Org_01); + uint8_t row_shift = 0; + uint8_t col_shift = 0; + if (show_slot_menu) { + if (cur_col + encoders[2]->cur > MAX_VISIBLE_COLS - 1) { + col_shift = cur_col + encoders[2]->cur - MAX_VISIBLE_COLS; + } + + if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { + row_shift = cur_row + encoders[3]->cur - MAX_VISIBLE_ROWS; + } + } for (uint8_t y = 0; y < MAX_VISIBLE_ROWS; y++) { - if ((y == cur_row)) { + if ((y + row_shift == cur_row)) { oled_display.setCursor(x_offset - 5, y_offset + y * 8); oled_display.print(">"); } oled_display.setCursor(x_offset, y_offset + y * 8); - for (uint8_t x = 0; x < MAX_VISIBLE_COLS; x++) { + for (uint8_t x = col_shift; x < MAX_VISIBLE_COLS + col_shift; x++) { uint8_t track_type = row_headers[y].track_type[x + getCol() - cur_col]; uint8_t model = row_headers[y].model[x + getCol() - cur_col]; if (track_type == MD_TRACK_TYPE) { @@ -394,11 +427,7 @@ void GridPage::display_grid() { str[0] = 'M'; str[1] = (x + getCol() - cur_col - 16) + '0'; } - - if (((slot_apply > 0) && - ((x <= cur_col + slot_apply - 1) && (x > cur_col)) || - (x == cur_col)) && - (y == cur_row)) { + if (in_area(x,y + row_shift, cur_col, cur_row, encoders[2]->cur - 1, encoders[3]->cur - 1)) { oled_display.fillRect(oled_display.getCursorX() - 1, oled_display.getCursorY() - 6, 9, 7, WHITE); oled_display.setTextColor(BLACK, WHITE); @@ -568,6 +597,11 @@ void GridPage::apply_slot_changes() { encoders[0] = ¶m1; encoders[1] = ¶m2; uint8_t count; + EmptyTrack temp_track; + MDTrack *md_track = (MDTrack *)&temp_track; + A4Track *a4_track = (A4Track *)&temp_track; + ExtTrack *ext_track = (ExtTrack *)&temp_track; + void (*row_func)() = grid_slot_page.menu.get_row_function(grid_slot_page.encoders[1]->cur); if (row_func != NULL) { @@ -580,28 +614,13 @@ void GridPage::apply_slot_changes() { count = slot_apply; } if (slot_copy == 1) { - slot_buffer_mask = 0; - slot_buffer_row = getRow(); + + mcl_clipboard.copy(getCol(), getRow(), encoders[2]->cur, encoders[3]->cur); + } else if (slot_paste == 1) { - uint8_t first = 255; - uint8_t n = 0; - uint8_t count = 0; - for (n = 0; n < GRID_WIDTH; n++) { - if (IS_BIT_SET32(slot_buffer_mask, n)) { - if (first == 255) { first = n; } - count++; - } - } - bool destination_same = false; - if (count == 1) { destination_same = true; } - for (n = 0; n + first < GRID_WIDTH && getCol() + n < GRID_WIDTH; n++) { - if (IS_BIT_SET32(slot_buffer_mask, n + first)) { - grid.copy_slot(n + first, slot_buffer_row, getCol() + n, getRow(), destination_same); - } - } - row_headers[cur_row].write(getRow()); + mcl_clipboard.paste(getCol(), getRow()); } if (slot_paste != 1) { MDTrack md_track; @@ -702,6 +721,8 @@ bool GridPage::handleEvent(gui_event_t *event) { encoders[0] = &grid_slot_param1; encoders[1] = &grid_slot_param2; grid_slot_page.init(); + encoders[2]->cur = 1; + encoders[3]->cur = 1; slot_apply = 0; merge_md = 0; return true; diff --git a/avr/cores/megacommand/MCL/GridPages.cpp b/avr/cores/megacommand/MCL/GridPages.cpp index 822795a5e..c4bec5cf0 100644 --- a/avr/cores/megacommand/MCL/GridPages.cpp +++ b/avr/cores/megacommand/MCL/GridPages.cpp @@ -9,8 +9,8 @@ MCLEncoder param1(GRID_WIDTH - 4, 0, ENCODER_RES_GRID); MCLEncoder param2(GRID_LENGTH - 1, 0 , ENCODER_RES_GRID); #endif -MCLEncoder param3(0, 127, ENCODER_RES_GRID); -MCLEncoder param4(0, 127, ENCODER_RES_GRID); +MCLEncoder param3(GRID_WIDTH, 1, ENCODER_RES_GRID); +MCLEncoder param4(GRID_LENGTH, 1, ENCODER_RES_GRID); GridPage grid_page(¶m1, ¶m2, ¶m3, ¶m4); diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index 4db6641bd..ad24ab910 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -39,6 +39,7 @@ #include "MCLActions.h" #include "MCLGUI.h" #include "MCLMemory.h" +#include "MCLClipBoard.h" #include "MCLSeq.h" #include "MDExploit.h" #include "MDSound.h" diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp new file mode 100644 index 000000000..da48ba370 --- /dev/null +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -0,0 +1,162 @@ +/* Copyright 2018, Justin Mammarella jmamma@gmail.com */ + +#include "MCL.h" +#include "MCLClipBoard.h" +#include "Shared.h" + +#define FILENAME_CLIPBOARD "clipboard.tmp" + +bool MCLClipBoard::init() { + DEBUG_PRINTLN("Creating clipboard"); + bool ret = file.createContiguous(FILENAME_CLIPBOARD, + (uint32_t)GRID_SLOT_BYTES + + (uint32_t)GRID_SLOT_BYTES * + (uint32_t)GRID_LENGTH * + (uint32_t)(GRID_WIDTH + 1)); + if (ret) { + file.close(); + } else { + DEBUG_PRINTLN("failed to create contiguous file"); + return false; + } +} + +bool MCLClipBoard::open() { + if (!file.open(FILENAME_CLIPBOARD, O_RDWR)) { + init(); + return file.open(FILENAME_CLIPBOARD, O_RDWR); + } +} +bool MCLClipBoard::close() { return file.close(); } + +bool MCLClipBoard::copy(int col, int row, int w, int h) { + t_col = col; + t_row = row; + t_w = w; + t_h = h; + if (!open()) { + DEBUG_PRINTLN("error could not open clipboard"); + return; + } + EmptyTrack temp_track; + int32_t offset; + bool ret; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + offset = grid.get_slot_offset(x + col, y + row); + ret = proj.file.seekSet(offset); + ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &proj.file); + if (ret) { + + offset = grid.get_slot_offset(x, y); + ret = file.seekSet(offset); + ret = mcl_sd.write_data(&temp_track, sizeof(temp_track), &file); + } + } + } + close(); +} +bool MCLClipBoard::paste(int col, int row) { + if (!open()) { + DEBUG_PRINTLN("error could not open clipboard"); + return; + } + + bool destination_same = (col == t_col); + if (t_w == 1) { + destination_same = true; + } + EmptyTrack temp_track; + MDTrack *md_track = (MDTrack *)&temp_track; + A4Track *a4_track = (A4Track *)&temp_track; + ExtTrack *ext_track = (ExtTrack *)&temp_track; + + int32_t offset; + bool ret; + + GridRowHeader header; + for (int y = 0; y < t_h; y++) { + if (y + row < GRID_LENGTH) { + header.read(y + row); + for (int x = 0; x < t_w; x++) { + + offset = grid.get_slot_offset(x, y); + ret = file.seekSet(offset); + ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); + uint8_t s_col = x + t_col; + uint8_t d_col = x + col; + if (x + col < GRID_WIDTH) { + switch (temp_track.active) { + case EMPTY_TRACK_TYPE: + header.update_model(x + col, EMPTY_TRACK_TYPE, DEVICE_NULL); + break; + + case EXT_TRACK_TYPE: + if (x + col >= NUM_MD_TRACKS) { + header.update_model(x + col, x + col, EXT_TRACK_TYPE); + offset = grid.get_slot_offset(x + col, y + row); + ret = proj.file.seekSet(offset); + ret = mcl_sd.write_data(ext_track, sizeof(ExtTrack), &proj.file); + } + break; + + case A4_TRACK_TYPE: + if (x + col >= NUM_MD_TRACKS) { + header.update_model(x + col, x + col, A4_TRACK_TYPE); + offset = grid.get_slot_offset(x + col, y + row); + ret = proj.file.seekSet(offset); + ret = mcl_sd.write_data(a4_track, sizeof(A4Track), &proj.file); + } + break; + + case MD_TRACK_TYPE: + if (x + col < NUM_MD_TRACKS) { + header.update_model(x + col, md_track->machine.model, + MD_TRACK_TYPE); + if ((destination_same)) { + if (md_track->machine.trigGroup == s_col) { + md_track->machine.trigGroup = 255; + } + if (md_track->machine.muteGroup == s_col) { + md_track->machine.muteGroup = 255; + } + if (md_track->machine.lfo.destinationTrack == s_col) { + md_track->machine.lfo.destinationTrack = d_col; + } + } else { + int lfo_dest = md_track->machine.lfo.destinationTrack - s_col; + int trig_dest = md_track->machine.trigGroup - s_col; + int mute_dest = md_track->machine.muteGroup - s_col; + if (range_check(d_col + lfo_dest, 0, 15)) { + md_track->machine.lfo.destinationTrack = d_col + lfo_dest; + } else { + md_track->machine.lfo.destinationTrack = 255; + } + if (range_check(d_col + trig_dest, 0, 15)) { + md_track->machine.trigGroup = d_col + trig_dest; + } else { + md_track->machine.trigGroup = 255; + } + if (range_check(d_col + mute_dest, 0, 15)) { + md_track->machine.muteGroup = d_col + mute_dest; + } else { + md_track->machine.muteGroup = 255; + } + } + offset = grid.get_slot_offset(x + col, y + row); + ret = proj.file.seekSet(offset); + ret = mcl_sd.write_data(md_track, sizeof(MDTrack), &proj.file); + } + break; + default: + break; + } + } + } + header.write(y + row); + } + } + close(); +} + +MCLClipBoard mcl_clipboard; diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.h b/avr/cores/megacommand/MCL/MCLClipBoard.h new file mode 100644 index 000000000..e175bd8ee --- /dev/null +++ b/avr/cores/megacommand/MCL/MCLClipBoard.h @@ -0,0 +1,30 @@ +/* Copyright 2018, Justin Mammarella jmamma@gmail.com */ + +#ifndef MCLCLIPBOARD_H__ +#define MCLCLIPBOARD_H__ +#include "Grid.h" +#include "SdFat.h" +#include "Shared.h" + +#define FILENAME_CLIPBOARD "clipboard.tmp" + +class MCLClipBoard { +public: + int t_col; + int t_row; + int t_w; + int t_h; + File file; + + bool init(); + bool open(); + bool close(); + + bool copy(int col, int row, int w, int h); + bool paste(int col, int row); + +}; + +extern MCLClipBoard mcl_clipboard; + +#endif /* MCLCLIPBOARD_H__ */ From ee6c2ec243469eb8a99ef0cd2f420ac40d7462f5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 9 Aug 2019 14:58:16 +1000 Subject: [PATCH 017/469] Slot update and clear should now work for multiple rows Row name will be copied if destination row is empty, or row to be completely replaced. For HD44780 display, encoders 3 + 4 will have no effect, the apply option will be visible in slot menu to apply changes to multiple slots on a single row --- avr/cores/megacommand/MCL/GridPage.cpp | 122 ++++++++-------- avr/cores/megacommand/MCL/GridPages.h | 6 + avr/cores/megacommand/MCL/MCLClipBoard.cpp | 159 +++++++++++---------- avr/cores/megacommand/MCL/MCLClipBoard.h | 4 +- 4 files changed, 150 insertions(+), 141 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index d8f59b33c..046950f31 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -41,14 +41,13 @@ void GridPage::loop() { #ifdef OLED_DISPLAY if (show_slot_menu) { - if (encoders[3]->hasChanged()) { - if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { - load_slot_models(); - reload_slot_models = true; + if (encoders[3]->hasChanged()) { + if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { + load_slot_models(); + reload_slot_models = true; + } } - - } - grid_slot_page.loop(); + grid_slot_page.loop(); return; } else { } @@ -61,9 +60,9 @@ void GridPage::loop() { } } */ - encoders[2]->cur = 1; - encoders[3]->cur = 1; - if (encoders[0]->hasChanged()) { + encoders[2]->cur = 1; + encoders[3]->cur = 1; + if (encoders[0]->hasChanged()) { diff = encoders[0]->cur - encoders[0]->old; new_val = cur_col + diff; if (new_val > MAX_VISIBLE_COLS - 1) { @@ -75,7 +74,7 @@ void GridPage::loop() { cur_col = new_val; } - if (encoders[1]->hasChanged()) { + if (encoders[1]->hasChanged()) { diff = encoders[1]->cur - encoders[1]->old; new_val = cur_row + diff; @@ -198,7 +197,7 @@ void GridPage::load_slot_models() { #ifdef OLED_DISPLAY uint8_t row_shift = 0; if ((cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1)) { - row_shift = cur_row + encoders[3]->cur - MAX_VISIBLE_ROWS; + row_shift = cur_row + encoders[3]->cur - MAX_VISIBLE_ROWS; } for (uint8_t n = 0; n < MAX_VISIBLE_ROWS; n++) { @@ -397,13 +396,13 @@ void GridPage::display_grid() { uint8_t row_shift = 0; uint8_t col_shift = 0; if (show_slot_menu) { - if (cur_col + encoders[2]->cur > MAX_VISIBLE_COLS - 1) { - col_shift = cur_col + encoders[2]->cur - MAX_VISIBLE_COLS; - } + if (cur_col + encoders[2]->cur > MAX_VISIBLE_COLS - 1) { + col_shift = cur_col + encoders[2]->cur - MAX_VISIBLE_COLS; + } - if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { - row_shift = cur_row + encoders[3]->cur - MAX_VISIBLE_ROWS; - } + if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { + row_shift = cur_row + encoders[3]->cur - MAX_VISIBLE_ROWS; + } } for (uint8_t y = 0; y < MAX_VISIBLE_ROWS; y++) { if ((y + row_shift == cur_row)) { @@ -412,7 +411,7 @@ void GridPage::display_grid() { } oled_display.setCursor(x_offset, y_offset + y * 8); - for (uint8_t x = col_shift; x < MAX_VISIBLE_COLS + col_shift; x++) { + for (uint8_t x = col_shift; x < MAX_VISIBLE_COLS + col_shift; x++) { uint8_t track_type = row_headers[y].track_type[x + getCol() - cur_col]; uint8_t model = row_headers[y].model[x + getCol() - cur_col]; if (track_type == MD_TRACK_TYPE) { @@ -427,7 +426,8 @@ void GridPage::display_grid() { str[0] = 'M'; str[1] = (x + getCol() - cur_col - 16) + '0'; } - if (in_area(x,y + row_shift, cur_col, cur_row, encoders[2]->cur - 1, encoders[3]->cur - 1)) { + if (in_area(x, y + row_shift, cur_col, cur_row, encoders[2]->cur - 1, + encoders[3]->cur - 1)) { oled_display.fillRect(oled_display.getCursorX() - 1, oled_display.getCursorY() - 6, 9, 7, WHITE); oled_display.setTextColor(BLACK, WHITE); @@ -596,8 +596,11 @@ void GridPage::apply_slot_changes() { show_slot_menu = false; encoders[0] = ¶m1; encoders[1] = ¶m2; - uint8_t count; - EmptyTrack temp_track; + + uint8_t width; + uint8_t height; + + EmptyTrack temp_track; MDTrack *md_track = (MDTrack *)&temp_track; A4Track *a4_track = (A4Track *)&temp_track; ExtTrack *ext_track = (ExtTrack *)&temp_track; @@ -608,67 +611,56 @@ void GridPage::apply_slot_changes() { (*row_func)(); return; } +#ifndef OLED_DISPLAY if (slot_apply == 0) { - count = 1; + width = 1; } else { - count = slot_apply; + width = slot_apply; } + height = 1; +#else + width = encoders[2]->cur; + height = encoders[3]->cur; +#endif if (slot_copy == 1) { - mcl_clipboard.copy(getCol(), getRow(), encoders[2]->cur, encoders[3]->cur); + mcl_clipboard.copy(getCol(), getRow(), width, height); } else if (slot_paste == 1) { - mcl_clipboard.paste(getCol(), getRow()); - } - if (slot_paste != 1) { - MDTrack md_track; - MDSeqTrack md_seq_track; - for (uint8_t track = 0; track < count && track + getCol() < NUM_TRACKS; - track++) { - if (slot_clear == 1) { - // Delete slot(s) - grid.clear_slot(track + getCol(), getRow()); - row_headers[cur_row].update_model(track + getCol(), EMPTY_TRACK_TYPE, - DEVICE_NULL); - if (row_headers[cur_row].is_empty()) { - char *str_tmp = "\0"; - strcpy(row_headers[cur_row].name, str_tmp); - row_headers[cur_row].write(getRow()); - } - reload_slot_models = true; - proj.file.sync(); - } else if (slot_copy == 1) { - // Mark slot for copy - SET_BIT32(slot_buffer_mask, track + getCol()); - } else { - // Save slot chain data - slot.active = row_headers[cur_row].track_type[track + getCol()]; - // if (slot.active != EMPTY_TRACK_TYPE) { - slot.store_track_in_grid(track + getCol(), getRow()); - proj.file.sync(); - } - /* - Merge sequencer data. Deprecate, now controlled in save page. + mcl_clipboard.paste(getCol(), getRow()); + } + else { + GridRowHeader header; - if ((merge_md > 0) && (slot.active != EMPTY_TRACK_TYPE)) { - md_track.load_track_from_grid(track + getCol(), getRow()); + for (uint8_t y = 0; y < height && y + getRow() < GRID_LENGTH; y++) { + header.read(y + getRow()); - memcpy(&(md_seq_track), &(md_track.seq_data), sizeof(MDSeqTrackData)); - md_seq_track.merge_from_md(&md_track); - md_track.clear_track(); - memcpy(&(md_track.seq_data), &(md_seq_track), sizeof(MDSeqTrackData)); - md_track.store_track_in_grid(track + getCol(), getRow()); + for (uint8_t x = 0; x < width && x + getCol() < GRID_WIDTH; x++) { + if (slot_clear == 1) { + // Delete slot(s) + grid.clear_slot(x + getCol(), y + getRow()); + header.update_model(x + getCol(), EMPTY_TRACK_TYPE, DEVICE_NULL); + } else { + // Save slot chain data + slot.active = header.track_type[x + getCol()]; + slot.store_track_in_grid(x + getCol(), y + getRow()); + } + } + // If all slots are deleted then clear the row name + if (header.is_empty()) { + char *str_tmp = "\0"; + strcpy(header.name, str_tmp); + header.write(y + getRow()); } - */ } } if ((slot_clear == 1) || (slot_paste == 1)) { + proj.file.sync(); load_slot_models(); } - proj.file.sync(); slot_apply = 0; slot_clear = 0; slot_copy = 0; diff --git a/avr/cores/megacommand/MCL/GridPages.h b/avr/cores/megacommand/MCL/GridPages.h index d2a41dfb0..db59bcfec 100644 --- a/avr/cores/megacommand/MCL/GridPages.h +++ b/avr/cores/megacommand/MCL/GridPages.h @@ -47,12 +47,18 @@ extern GridTrack slot; const menu_t slot_menu_layout PROGMEM = { "Slot", + #ifndef OLED_DISPLAY 8, + #else + 7, + #endif { {"CHAIN:", 1, 4, 3, (uint8_t *) &mcl_cfg.chain_mode, (Page*) NULL, (void*)NULL, {{1, "AUT"},{2,"MAN"},{3,"RND"}}}, {"LOOP: ", 0, 64, 0, (uint8_t *) &slot.chain.loops, (Page*) NULL, (void*)NULL, {}}, {"ROW: ", 0, 128, 0, (uint8_t*) &slot.chain.row, (Page*) NULL, (void*)NULL, {}}, + #ifndef OLED_DISPAY {"APPLY:", 1, 21, 1, (uint8_t *) &grid_page.slot_apply, (Page*) NULL, (void*)NULL, {{0," "}}}, + #endif {"CLEAR:", 0, 2, 2, (uint8_t *) &grid_page.slot_clear, (Page*) NULL, (void*)NULL, {{0,"--"},{1, "YES"}}}, {"COPY:", 0, 2, 2, (uint8_t *) &grid_page.slot_copy, (Page*) NULL, (void*)NULL, {{0,"--"},{1, "YES"}}}, {"PASTE:", 0, 2, 2, (uint8_t *) &grid_page.slot_paste, (Page*) NULL, (void*)NULL,{{0,"--"},{1, "YES"}}}, diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index da48ba370..ba8f25644 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -29,7 +29,7 @@ bool MCLClipBoard::open() { } bool MCLClipBoard::close() { return file.close(); } -bool MCLClipBoard::copy(int col, int row, int w, int h) { +bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h) { t_col = col; t_row = row; t_w = w; @@ -41,7 +41,14 @@ bool MCLClipBoard::copy(int col, int row, int w, int h) { EmptyTrack temp_track; int32_t offset; bool ret; + GridRowHeader header; + for (int y = 0; y < h; y++) { + header.read(y + row); + offset = grid.get_header_offset(y + row); + ret = file.seekSet(offset); + ret = mcl_sd.write_data((uint8_t *)(&header), sizeof(GridRowHeader), &file); + for (int x = 0; x < w; x++) { offset = grid.get_slot_offset(x + col, y + row); ret = proj.file.seekSet(offset); @@ -56,7 +63,7 @@ bool MCLClipBoard::copy(int col, int row, int w, int h) { } close(); } -bool MCLClipBoard::paste(int col, int row) { +bool MCLClipBoard::paste(uint16_t col, uint16_t row) { if (!open()) { DEBUG_PRINTLN("error could not open clipboard"); return; @@ -75,86 +82,90 @@ bool MCLClipBoard::paste(int col, int row) { bool ret; GridRowHeader header; - for (int y = 0; y < t_h; y++) { - if (y + row < GRID_LENGTH) { - header.read(y + row); - for (int x = 0; x < t_w; x++) { + GridRowHeader header_copy; + for (int y = 0; y < t_h && y + row < GRID_LENGTH; y++) { + header.read(y + row); - offset = grid.get_slot_offset(x, y); - ret = file.seekSet(offset); - ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); - uint8_t s_col = x + t_col; - uint8_t d_col = x + col; - if (x + col < GRID_WIDTH) { - switch (temp_track.active) { - case EMPTY_TRACK_TYPE: - header.update_model(x + col, EMPTY_TRACK_TYPE, DEVICE_NULL); - break; - - case EXT_TRACK_TYPE: - if (x + col >= NUM_MD_TRACKS) { - header.update_model(x + col, x + col, EXT_TRACK_TYPE); - offset = grid.get_slot_offset(x + col, y + row); - ret = proj.file.seekSet(offset); - ret = mcl_sd.write_data(ext_track, sizeof(ExtTrack), &proj.file); + if ((strlen(header.name) == 0) || (t_w == GRID_WIDTH && col == 0)) { + offset = grid.get_header_offset(y + row); + ret = file.seekSet(offset); + ret = mcl_sd.read_data((uint8_t *)(&header_copy), sizeof(GridRowHeader), + &file); + strcpy(&(header.name[0]), &(header_copy.name[0])); + } + for (int x = 0; x < t_w && x + col < GRID_WIDTH; x++) { + + offset = grid.get_slot_offset(x, y); + ret = file.seekSet(offset); + ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); + uint8_t s_col = x + t_col; + uint8_t d_col = x + col; + switch (temp_track.active) { + case EMPTY_TRACK_TYPE: + header.update_model(x + col, EMPTY_TRACK_TYPE, DEVICE_NULL); + break; + + case EXT_TRACK_TYPE: + if (x + col >= NUM_MD_TRACKS) { + header.update_model(x + col, x + col, EXT_TRACK_TYPE); + offset = grid.get_slot_offset(x + col, y + row); + ret = proj.file.seekSet(offset); + ret = mcl_sd.write_data(ext_track, sizeof(ExtTrack), &proj.file); + } + break; + + case A4_TRACK_TYPE: + if (x + col >= NUM_MD_TRACKS) { + header.update_model(x + col, x + col, A4_TRACK_TYPE); + offset = grid.get_slot_offset(x + col, y + row); + ret = proj.file.seekSet(offset); + ret = mcl_sd.write_data(a4_track, sizeof(A4Track), &proj.file); + } + break; + + case MD_TRACK_TYPE: + if (x + col < NUM_MD_TRACKS) { + header.update_model(x + col, md_track->machine.model, MD_TRACK_TYPE); + if ((destination_same)) { + if (md_track->machine.trigGroup == s_col) { + md_track->machine.trigGroup = 255; + } + if (md_track->machine.muteGroup == s_col) { + md_track->machine.muteGroup = 255; + } + if (md_track->machine.lfo.destinationTrack == s_col) { + md_track->machine.lfo.destinationTrack = d_col; + } + } else { + int lfo_dest = md_track->machine.lfo.destinationTrack - s_col; + int trig_dest = md_track->machine.trigGroup - s_col; + int mute_dest = md_track->machine.muteGroup - s_col; + if (range_check(d_col + lfo_dest, 0, 15)) { + md_track->machine.lfo.destinationTrack = d_col + lfo_dest; + } else { + md_track->machine.lfo.destinationTrack = 255; } - break; - - case A4_TRACK_TYPE: - if (x + col >= NUM_MD_TRACKS) { - header.update_model(x + col, x + col, A4_TRACK_TYPE); - offset = grid.get_slot_offset(x + col, y + row); - ret = proj.file.seekSet(offset); - ret = mcl_sd.write_data(a4_track, sizeof(A4Track), &proj.file); + if (range_check(d_col + trig_dest, 0, 15)) { + md_track->machine.trigGroup = d_col + trig_dest; + } else { + md_track->machine.trigGroup = 255; } - break; - - case MD_TRACK_TYPE: - if (x + col < NUM_MD_TRACKS) { - header.update_model(x + col, md_track->machine.model, - MD_TRACK_TYPE); - if ((destination_same)) { - if (md_track->machine.trigGroup == s_col) { - md_track->machine.trigGroup = 255; - } - if (md_track->machine.muteGroup == s_col) { - md_track->machine.muteGroup = 255; - } - if (md_track->machine.lfo.destinationTrack == s_col) { - md_track->machine.lfo.destinationTrack = d_col; - } - } else { - int lfo_dest = md_track->machine.lfo.destinationTrack - s_col; - int trig_dest = md_track->machine.trigGroup - s_col; - int mute_dest = md_track->machine.muteGroup - s_col; - if (range_check(d_col + lfo_dest, 0, 15)) { - md_track->machine.lfo.destinationTrack = d_col + lfo_dest; - } else { - md_track->machine.lfo.destinationTrack = 255; - } - if (range_check(d_col + trig_dest, 0, 15)) { - md_track->machine.trigGroup = d_col + trig_dest; - } else { - md_track->machine.trigGroup = 255; - } - if (range_check(d_col + mute_dest, 0, 15)) { - md_track->machine.muteGroup = d_col + mute_dest; - } else { - md_track->machine.muteGroup = 255; - } - } - offset = grid.get_slot_offset(x + col, y + row); - ret = proj.file.seekSet(offset); - ret = mcl_sd.write_data(md_track, sizeof(MDTrack), &proj.file); + if (range_check(d_col + mute_dest, 0, 15)) { + md_track->machine.muteGroup = d_col + mute_dest; + } else { + md_track->machine.muteGroup = 255; } - break; - default: - break; } + offset = grid.get_slot_offset(x + col, y + row); + ret = proj.file.seekSet(offset); + ret = mcl_sd.write_data(md_track, sizeof(MDTrack), &proj.file); } + break; + default: + break; } - header.write(y + row); } + header.write(y + row); } close(); } diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.h b/avr/cores/megacommand/MCL/MCLClipBoard.h index e175bd8ee..60f7ab9fa 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.h +++ b/avr/cores/megacommand/MCL/MCLClipBoard.h @@ -20,8 +20,8 @@ class MCLClipBoard { bool open(); bool close(); - bool copy(int col, int row, int w, int h); - bool paste(int col, int row); + bool copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h); + bool paste(uint16_t col, uint16_t row); }; From b83e42332a83d4892c34464f531d0f838acc2ec9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 9 Aug 2019 15:26:15 +1000 Subject: [PATCH 018/469] Fixes for row name copying and proj.file.sync --- avr/cores/megacommand/MCL/GridPage.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 046950f31..32833f406 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -622,6 +622,10 @@ void GridPage::apply_slot_changes() { width = encoders[2]->cur; height = encoders[3]->cur; #endif + + uint8_t slot_update = 0; + if (slot_copy + slot_paste + slot_clear == 0) { slot_update = 1; } + if (slot_copy == 1) { mcl_clipboard.copy(getCol(), getRow(), width, height); @@ -642,21 +646,21 @@ void GridPage::apply_slot_changes() { // Delete slot(s) grid.clear_slot(x + getCol(), y + getRow()); header.update_model(x + getCol(), EMPTY_TRACK_TYPE, DEVICE_NULL); - } else { + } else if (slot_update == 1) { // Save slot chain data slot.active = header.track_type[x + getCol()]; slot.store_track_in_grid(x + getCol(), y + getRow()); } } // If all slots are deleted then clear the row name - if (header.is_empty()) { + if (header.is_empty() && (slot_clear == 1)) { char *str_tmp = "\0"; strcpy(header.name, str_tmp); header.write(y + getRow()); } } } - if ((slot_clear == 1) || (slot_paste == 1)) { + if ((slot_clear == 1) || (slot_paste == 1) || (slot_update == 1)) { proj.file.sync(); load_slot_models(); } From 47bd5271322f99a102c14ec8fd8456ec3d277373 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 9 Aug 2019 19:47:05 +1000 Subject: [PATCH 019/469] Typo --- avr/cores/megacommand/MCL/GridPages.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/GridPages.h b/avr/cores/megacommand/MCL/GridPages.h index db59bcfec..4a7ec1d3a 100644 --- a/avr/cores/megacommand/MCL/GridPages.h +++ b/avr/cores/megacommand/MCL/GridPages.h @@ -56,7 +56,7 @@ const menu_t slot_menu_layout PROGMEM = { {"CHAIN:", 1, 4, 3, (uint8_t *) &mcl_cfg.chain_mode, (Page*) NULL, (void*)NULL, {{1, "AUT"},{2,"MAN"},{3,"RND"}}}, {"LOOP: ", 0, 64, 0, (uint8_t *) &slot.chain.loops, (Page*) NULL, (void*)NULL, {}}, {"ROW: ", 0, 128, 0, (uint8_t*) &slot.chain.row, (Page*) NULL, (void*)NULL, {}}, - #ifndef OLED_DISPAY + #ifndef OLED_DISPLAY {"APPLY:", 1, 21, 1, (uint8_t *) &grid_page.slot_apply, (Page*) NULL, (void*)NULL, {{0," "}}}, #endif {"CLEAR:", 0, 2, 2, (uint8_t *) &grid_page.slot_clear, (Page*) NULL, (void*)NULL, {{0,"--"},{1, "YES"}}}, From 9a73408c3717ef74c086bc1057e2e654b0e4b7eb Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 9 Aug 2019 21:09:34 +1000 Subject: [PATCH 020/469] Row name now copies correctly --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index ba8f25644..6945405f2 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -30,6 +30,7 @@ bool MCLClipBoard::open() { bool MCLClipBoard::close() { return file.close(); } bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h) { + DEBUG_PRINT_FN(); t_col = col; t_row = row; t_w = w; @@ -48,7 +49,7 @@ bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h) { offset = grid.get_header_offset(y + row); ret = file.seekSet(offset); ret = mcl_sd.write_data((uint8_t *)(&header), sizeof(GridRowHeader), &file); - + DEBUG_PRINTLN(header.name); for (int x = 0; x < w; x++) { offset = grid.get_slot_offset(x + col, y + row); ret = proj.file.seekSet(offset); @@ -64,6 +65,7 @@ bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h) { close(); } bool MCLClipBoard::paste(uint16_t col, uint16_t row) { + DEBUG_PRINT_FN(); if (!open()) { DEBUG_PRINTLN("error could not open clipboard"); return; @@ -86,11 +88,12 @@ bool MCLClipBoard::paste(uint16_t col, uint16_t row) { for (int y = 0; y < t_h && y + row < GRID_LENGTH; y++) { header.read(y + row); - if ((strlen(header.name) == 0) || (t_w == GRID_WIDTH && col == 0)) { - offset = grid.get_header_offset(y + row); + if ((strlen(header.name) == 0) || (!header.active) || (t_w == GRID_WIDTH && col == 0)) { + offset = grid.get_header_offset(y + t_row); ret = file.seekSet(offset); ret = mcl_sd.read_data((uint8_t *)(&header_copy), sizeof(GridRowHeader), - &file); + &file); + header.active = true; strcpy(&(header.name[0]), &(header_copy.name[0])); } for (int x = 0; x < t_w && x + col < GRID_WIDTH; x++) { From 7f29b7377fab7c4b476a88edc56957a990796064 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 9 Aug 2019 21:27:20 +1000 Subject: [PATCH 021/469] fix is_empty, row names now clear when row is empty --- avr/cores/megacommand/MCL/GridRowHeader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/GridRowHeader.cpp b/avr/cores/megacommand/MCL/GridRowHeader.cpp index dfd4ca3c3..073ccfaf3 100644 --- a/avr/cores/megacommand/MCL/GridRowHeader.cpp +++ b/avr/cores/megacommand/MCL/GridRowHeader.cpp @@ -47,7 +47,7 @@ bool GridRowHeader::is_empty() { } DEBUG_PRINTLN(track_type[x]); } - return (count == GRID_WIDTH - 1); + return (count == GRID_WIDTH); } void GridRowHeader::init() { From a2481075b9b1bc58fcffcbb3a100ab80efcd4a5f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 10 Aug 2019 10:53:56 +1000 Subject: [PATCH 022/469] Deactivate row on delete --- avr/cores/megacommand/MCL/GridPage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 32833f406..bbda4506c 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -654,7 +654,9 @@ void GridPage::apply_slot_changes() { } // If all slots are deleted then clear the row name if (header.is_empty() && (slot_clear == 1)) { + DEBUG_PRINTLN("clearing row name"); char *str_tmp = "\0"; + header.active = false; strcpy(header.name, str_tmp); header.write(y + getRow()); } From 6d0df7f3900c9845bc42ce740ba743198fad0c1f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 10 Aug 2019 10:58:41 +1000 Subject: [PATCH 023/469] Better than e24422 --- avr/cores/megacommand/MCL/GridTask.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridTask.cpp b/avr/cores/megacommand/MCL/GridTask.cpp index b94b325e8..36c8b2258 100644 --- a/avr/cores/megacommand/MCL/GridTask.cpp +++ b/avr/cores/megacommand/MCL/GridTask.cpp @@ -25,15 +25,7 @@ void GridTask::run() { ExtTrack *ext_track = (ExtTrack *)&empty_track; #endif int slots_changed[NUM_TRACKS]; - uint8_t slots_loaded[NUM_MD_TRACKS]; - - for (uint8_t a = 0; a < NUM_TRACKS; a++) { - slots_changed[a] = -1; - } - - for (uint8_t a = 0; a < NUM_MD_TRACKS; a++) { - slots_loaded[a] = 0; - } + uint8_t slots_loaded[NUM_MD_TRACKS] = { 0 }; bool send_ext_slots = false; bool send_md_slots = false; From 57615e42e9abe98aab9a540500b82ef0108eaae3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 13 Aug 2019 18:57:36 +1000 Subject: [PATCH 024/469] Limit row to 1 when performing slot update. This ignores the rectangular selection and prevents accidently updating mutliple row chain settings. --- avr/cores/megacommand/MCL/GridPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index bbda4506c..4631e2050 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -624,7 +624,7 @@ void GridPage::apply_slot_changes() { #endif uint8_t slot_update = 0; - if (slot_copy + slot_paste + slot_clear == 0) { slot_update = 1; } + if (slot_copy + slot_paste + slot_clear == 0) { slot_update = 1; height = 1; } if (slot_copy == 1) { From ae65e4efd07ee90d1ed06b1651fb9fb435162631 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 13 Aug 2019 19:12:05 +1000 Subject: [PATCH 025/469] Only update slots if row or loops has changed. This will prevent inadvertently updating chain settings for multiple slots. --- avr/cores/megacommand/MCL/GridPage.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 4631e2050..954a93b08 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -605,6 +605,9 @@ void GridPage::apply_slot_changes() { A4Track *a4_track = (A4Track *)&temp_track; ExtTrack *ext_track = (ExtTrack *)&temp_track; + GridTrack temp_slot; + temp_slot.load_track_from_grid(getCol(), getRow()); + void (*row_func)() = grid_slot_page.menu.get_row_function(grid_slot_page.encoders[1]->cur); if (row_func != NULL) { @@ -624,7 +627,14 @@ void GridPage::apply_slot_changes() { #endif uint8_t slot_update = 0; - if (slot_copy + slot_paste + slot_clear == 0) { slot_update = 1; height = 1; } + + if (slot_copy + slot_paste + slot_clear == 0) { + if ((temp_slot.chain.row != slot.chain.row) || + (temp_slot.chain.loops != slot.chain.loops)) { + slot_update = 1; + } + height = 1; + } if (slot_copy == 1) { @@ -634,8 +644,7 @@ void GridPage::apply_slot_changes() { else if (slot_paste == 1) { mcl_clipboard.paste(getCol(), getRow()); - } - else { + } else { GridRowHeader header; for (uint8_t y = 0; y < height && y + getRow() < GRID_LENGTH; y++) { From 6aed99a6c63248a3dde013ff024799403a7902f4 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 13 Oct 2019 01:27:15 +0800 Subject: [PATCH 026/469] sddrive wip --- .ccls | 55 ++++++-- avr/cores/megacommand/MCL/MCLMenus.h | 4 +- avr/cores/megacommand/MCL/SDDrivePage.cpp | 149 ++++++++++++++++++++++ avr/cores/megacommand/MCL/SDDrivePage.h | 24 ++++ 4 files changed, 221 insertions(+), 11 deletions(-) create mode 100644 avr/cores/megacommand/MCL/SDDrivePage.cpp create mode 100644 avr/cores/megacommand/MCL/SDDrivePage.h diff --git a/.ccls b/.ccls index ffa042b83..99f2d8f70 100644 --- a/.ccls +++ b/.ccls @@ -1,18 +1,53 @@ -%g++ --Wall --Wextra -%h -x c++-header -%hh -x c++-header +clang++ %c -std=gnu11 -%cpp -pedantic %cpp -std=c++11 -%cpp -stdlib=libc++ --Iavr/cores/megacommand/ --Iavr/cores/megacommand/CommonTools +# Treats all headers as c++ headers +%h %hpp -x c++-header +# Include dirs in Makefile: +# DIRS = Sequencer SDCard Elektron A4 Wire Wire/utility Midi MidiTools Adafruit-GFX-Library Adafruit-GFX-Library/Fonts SdFat SdFat/SdCard SdFat/FatLib SdFat/SpiDriver SdFat/SpiDriver/boards MCL SPI Adafruit_SSD1305_Library CommonTools GUI MD MNM +# LDIRS = $(foreach dir,$(DIRS),$(CORE_DIR)/$(dir)) ${CORE_DIR} ${VARIENTS_DIR} +# INCS = $(foreach dir,$(LDIRS),-I$(dir)) +# +# DIRS +-Iavr/cores/megacommand/Sequencer +-Iavr/cores/megacommand/SDCard -Iavr/cores/megacommand/Elektron +-Iavr/cores/megacommand/A4 +-Iavr/cores/megacommand/Wire +-Iavr/cores/megacommand/Wire/utility -Iavr/cores/megacommand/Midi +-Iavr/cores/megacommand/MidiTools +-Iavr/cores/megacommand/Adafruit-GFX-Library +-Iavr/cores/megacommand/Adafruit-GFX-Library/Fonts +-Iavr/cores/megacommand/SdFat +-Iavr/cores/megacommand/SdFat/SdCard +-Iavr/cores/megacommand/SdFat/FatLib +-Iavr/cores/megacommand/SdFat/SpiDriver +-Iavr/cores/megacommand/SdFat/SpiDriver/boards -Iavr/cores/megacommand/MCL +-Iavr/cores/megacommand/SPI +-Iavr/cores/megacommand/Adafruit_SSD1305_Library +-Iavr/cores/megacommand/CommonTools +-Iavr/cores/megacommand/GUI +-Iavr/cores/megacommand/MD +-Iavr/cores/megacommand/MNM +# CORE_DIR +-Iavr/cores/megacommand +# VARIENTS_DIR +-Ivariants/mega +# Additionally include the AVR environment -IC:/Program Files (x86)/Arduino/hardware/tools/avr/avr/include +# +# CXXFLAGS = -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -Wl,--gc-sections,--defsym=__stack=0x8021ff,--section-start,.data=0x82200,--defsym=__heap_end=0x80ffff -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR +# Define the flags: -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR +-DF_CPU=16000000L +-DARDUINO=10803 +-DARDUINO_AVR_MEGA2560 +-DARDUINO_ARCH_AVR +# Additional define flags -D__AVR_ATmega2560__ --DARDUINO=110 -DAVR +# Compiler options +-Wall +-Wextra +%c %cpp -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 36f38148a..a7cee5ca8 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -7,6 +7,7 @@ #include "MCLSysConfig.h" #include "ProjectPages.h" #include "PolyPage.h" +#include "SDDrivePage.h" #include "GridPages.h" #include "TextInputPage.h" #include "SeqPages.h" @@ -67,12 +68,13 @@ const menu_t midiconfig_menu_layout PROGMEM = { const menu_t mdconfig_menu_layout PROGMEM = { "MD", - 4, + 5, { {"KIT SAVE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_save, (Page*) NULL, (void*)NULL, {{0, "OFF"},{1, "AUTO"}}}, {"NORMALIZE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_normalize, (Page*) NULL, (void*)NULL, {{0, "OFF"},{1, "AUTO"}}}, {"CTRL CHAN:",0, 18, 2, (uint8_t *) &mcl_cfg.uart2_ctrl_mode, (Page*) NULL, (void*)NULL, {{0, "INT"},{17, "OMNI"}}}, {"POLY CONFIG", 0, 0, 0, (uint8_t *) NULL, (Page*) &poly_page, (void*)NULL, {}}, + {"SD DRIVE", 0, 0, 0, (uint8_t *) NULL, (Page*) &sddrive_page, (void*)NULL, {}}, }, (void*)(&mclsys_apply_config), }; diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp new file mode 100644 index 000000000..bdcae9ec9 --- /dev/null +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -0,0 +1,149 @@ +#include "MCL.h" +#include "SDDrivePage.h" + +const char* c_snapshot_suffix = ".snap"; + +void SDDrivePage::setup() { + SD.mkdir("/Snapshots/MD", true); + FileBrowserPage::setup(); +} + +void SDDrivePage::init() { + + DEBUG_PRINT_FN(); + md_exploit.off(); + strcpy(match, c_snapshot_suffix); + dir_browser = true; + strcpy(title, "Snapshots"); + FileBrowserPage::init(); + +} + +void SDDrivePage::save_snapshot() { + DEBUG_PRINT_FN(); + + MDSound sound; + char sound_name[] = "________"; + char my_title[] = "Snapshot Name"; + + if (mcl_gui.wait_for_input(sound_name, my_title, 8)) { + char temp_entry[16]; + strcpy(temp_entry, sound_name); + strcat(temp_entry, c_snapshot_suffix); + DEBUG_PRINTLN("creating new snapshot:"); + DEBUG_PRINTLN(temp_entry); + //sound.file.open(temp_entry, O_RDWR | O_CREAT); + //sound.fetch_sound(MD.currentTrack); + //sound.write_sound(); + //sound.file.close(); + gfx.alert("File Saved", temp_entry); + } +} + +void SDDrivePage::load_snapshot() { + + DEBUG_PRINT_FN(); + if (file.isOpen()) { + char temp_entry[16]; + file.getName(temp_entry, 16); + file.close(); + DEBUG_PRINTLN("loading snapshot"); + DEBUG_PRINTLN(temp_entry); + //if (!sound.file.open(temp_entry, O_READ)) { + //DEBUG_PRINTLN("error openning"); + //gfx.alert("Error", "Opening"); + //return; + //} + //sound.file.close(); + gfx.alert("Loaded","Snapshot"); + } + +} + + +bool SDDrivePage::handleEvent(gui_event_t *event) +{ + if (note_interface.is_event(event)) { + + return true; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || + EVENT_PRESSED(event, Buttons.ENCODER2) || + EVENT_PRESSED(event, Buttons.ENCODER3) || + EVENT_PRESSED(event, Buttons.ENCODER4)) { + + if (encoders[1]->getValue() == 0) { + save_snapshot(); + init(); + return false; + } + + if (encoders[1]->getValue() == 1) { + create_folder(); + return false; + } + + char temp_entry[16]; + char dir_entry[16]; + uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; + volatile uint8_t *ptr = (uint8_t*)pos; + memcpy_bank1(&temp_entry[0], ptr, 16); + + if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { + file.close(); + + SD.vwd()->getName(dir_entry, 16); + lwd[strlen(lwd) - strlen(dir_entry) - 1] = '\0'; + DEBUG_PRINTLN(lwd); + SD.chdir(lwd); + + init(); + return false; + } + + file.open(temp_entry, O_READ); + + if (file.isDirectory()) { + file.close(); + SD.vwd()->getName(dir_entry, 16); + strcat(lwd, dir_entry); + if (dir_entry[strlen(dir_entry) - 1] != '/') { + strcat(lwd, "/"); + } + DEBUG_PRINTLN(lwd); + DEBUG_PRINTLN(temp_entry); + SD.chdir(temp_entry); + init(); + return false; + } + + load_snapshot(); + + return true; + } + + // if ((EVENT_RELEASED(event, Buttons.BUTTON1) && BUTTON_DOWN(Buttons.BUTTON3)) || + if (EVENT_RELEASED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON1)) { + char temp_entry[16]; + char dir_entry[16]; + uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; + volatile uint8_t *ptr = pos; + memcpy_bank1(&temp_entry[0], ptr, 16); + SD.remove(temp_entry); + init(); + return false; + } + + if (EVENT_RELEASED(event, Buttons.BUTTON2) || + EVENT_RELEASED(event, Buttons.BUTTON1) || + EVENT_RELEASED(event, Buttons.BUTTON4)) { + // EVENT_RELEASED(event, Buttons.BUTTON4)) { + GUI.setPage(&grid_page); + return true; + } + return false; +} + +MCLEncoder sddrive_param1(1, 10, ENCODER_RES_SYS); +MCLEncoder sddrive_param2(0, 36, ENCODER_RES_SYS); +SDDrivePage sddrive_page(&sddrive_param1, &sddrive_param2); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.h b/avr/cores/megacommand/MCL/SDDrivePage.h new file mode 100644 index 000000000..154f604bf --- /dev/null +++ b/avr/cores/megacommand/MCL/SDDrivePage.h @@ -0,0 +1,24 @@ +/* Yatao Li yatao.li@live.com 2019 */ + +#ifndef SDDRIVEPAGE_H__ +#define SDDRIVEPAGE_H__ + +#include "FileBrowserPage.h" + +class SDDrivePage : public FileBrowserPage { +public: + + SDDrivePage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, + Encoder *e4 = NULL) + : FileBrowserPage(e1, e2, e3, e4) { } + + bool handleEvent(gui_event_t *event); + void init(); + void setup(); + void save_snapshot(); + void load_snapshot(); +}; + +extern SDDrivePage sddrive_page; + +#endif /* SDDRIVEPAGE_H__ */ From eb1eec1105bcf6c5e6a6b861cf7ca54a342f97e4 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 13 Oct 2019 02:15:48 +0800 Subject: [PATCH 027/469] initial attempt --- avr/cores/megacommand/MCL/SDDrivePage.cpp | 69 +++++++++++++++++++---- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index bdcae9ec9..d989fdbd1 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -32,10 +32,29 @@ void SDDrivePage::save_snapshot() { strcat(temp_entry, c_snapshot_suffix); DEBUG_PRINTLN("creating new snapshot:"); DEBUG_PRINTLN(temp_entry); - //sound.file.open(temp_entry, O_RDWR | O_CREAT); - //sound.fetch_sound(MD.currentTrack); - //sound.write_sound(); - //sound.file.close(); + file.open(temp_entry, O_RDWR | O_CREAT); + // Globals + for(int i=0;i<8;++i){ + MD.getBlockingGlobal(i); + mcl_sd.write_data(&MD.global, sizeof(MD.global), &file); + } + // Patterns + for(int i=0;i<128;++i){ + MD.getBlockingPattern(i); + mcl_sd.write_data(&MD.pattern, sizeof(MD.pattern), &file); + } + // Kits + for(int i=0;i<64;++i){ + MD.getBlockingKit(i); + mcl_sd.write_data(&MD.kit, sizeof(MD.kit), &file); + } + // Songs + //for(int i=0;i<64;++i){ + //MD.getBlockingSong(i); + //mcl_sd.write_data(&MD.song, sizeof(MD.song), &file); // <--- ?? + //} + // Save complete + file.close(); gfx.alert("File Saved", temp_entry); } } @@ -49,12 +68,42 @@ void SDDrivePage::load_snapshot() { file.close(); DEBUG_PRINTLN("loading snapshot"); DEBUG_PRINTLN(temp_entry); - //if (!sound.file.open(temp_entry, O_READ)) { - //DEBUG_PRINTLN("error openning"); - //gfx.alert("Error", "Opening"); - //return; - //} - //sound.file.close(); + if (!file.open(temp_entry, O_READ)) + { + DEBUG_PRINTLN("error openning"); + gfx.alert("Error", "Opening"); + return; + } + + MidiUart.sendRaw(MIDI_STOP); + MidiClock.handleImmediateMidiStop(); + // Stop everything + grid_page.prepare(); + + + // Globals + for(int i=0;i<8;++i){ + mcl_sd.read_data(&MD.global, sizeof(MD.global), &file); + mcl_actions.md_setsysex_recpos(2, i); + { + ElektronDataToSysexEncoder encoder(&MidiUart); + MD.global.toSysex(encoder); + } + } + // Patterns + for(int i=0;i<128;++i){ + mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file); + mcl_actions.md_setsysex_recpos(8, i); + MD.pattern.toSysex(); + } + // Kits + for(int i=0;i<64;++i){ + mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file); + mcl_actions.md_setsysex_recpos(4, i); + MD.kit.toSysex(); + } + // Load complete + file.close(); gfx.alert("Loaded","Snapshot"); } From 97e3739e9201a5d44c0256a01cab85d1d79e0af0 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 13 Oct 2019 16:07:36 +0800 Subject: [PATCH 028/469] move .ccls to megacommand --- .ccls => avr/cores/megacommand/.ccls | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) rename .ccls => avr/cores/megacommand/.ccls (62%) diff --git a/.ccls b/avr/cores/megacommand/.ccls similarity index 62% rename from .ccls rename to avr/cores/megacommand/.ccls index 99f2d8f70..e7980c8ea 100644 --- a/.ccls +++ b/avr/cores/megacommand/.ccls @@ -9,32 +9,32 @@ clang++ # INCS = $(foreach dir,$(LDIRS),-I$(dir)) # # DIRS --Iavr/cores/megacommand/Sequencer --Iavr/cores/megacommand/SDCard --Iavr/cores/megacommand/Elektron --Iavr/cores/megacommand/A4 --Iavr/cores/megacommand/Wire --Iavr/cores/megacommand/Wire/utility --Iavr/cores/megacommand/Midi --Iavr/cores/megacommand/MidiTools --Iavr/cores/megacommand/Adafruit-GFX-Library --Iavr/cores/megacommand/Adafruit-GFX-Library/Fonts --Iavr/cores/megacommand/SdFat --Iavr/cores/megacommand/SdFat/SdCard --Iavr/cores/megacommand/SdFat/FatLib --Iavr/cores/megacommand/SdFat/SpiDriver --Iavr/cores/megacommand/SdFat/SpiDriver/boards --Iavr/cores/megacommand/MCL --Iavr/cores/megacommand/SPI --Iavr/cores/megacommand/Adafruit_SSD1305_Library --Iavr/cores/megacommand/CommonTools --Iavr/cores/megacommand/GUI --Iavr/cores/megacommand/MD --Iavr/cores/megacommand/MNM +-ISequencer +-ISDCard +-IElektron +-IA4 +-IWire +-IWire/utility +-IMidi +-IMidiTools +-IAdafruit-GFX-Library +-IAdafruit-GFX-Library/Fonts +-ISdFat +-ISdFat/SdCard +-ISdFat/FatLib +-ISdFat/SpiDriver +-ISdFat/SpiDriver/boards +-IMCL +-ISPI +-IAdafruit_SSD1305_Library +-ICommonTools +-IGUI +-IMD +-IMNM # CORE_DIR --Iavr/cores/megacommand +-I./ # VARIENTS_DIR --Ivariants/mega +-I../../variants/mega # Additionally include the AVR environment -IC:/Program Files (x86)/Arduino/hardware/tools/avr/avr/include # From 6ead6716b282daf00a2ac7e1d4da6602874df237 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 13 Oct 2019 21:36:25 +0800 Subject: [PATCH 029/469] refactoring. dir_browser -> show_dirs --- avr/cores/megacommand/.vim/coc-settings.json | 15 ++++ avr/cores/megacommand/MCL/FileBrowserPage.cpp | 81 +++++++++--------- avr/cores/megacommand/MCL/FileBrowserPage.h | 2 +- avr/cores/megacommand/MCL/LoadProjectPage.cpp | 2 +- avr/cores/megacommand/MCL/MCL.cpp | 10 +-- avr/cores/megacommand/MCL/SDDrivePage.cpp | 83 ++++++++++++------- .../megacommand/MCL/SoundBrowserPage.cpp | 2 +- avr/cores/megacommand/WProgram.h | 2 +- 8 files changed, 111 insertions(+), 86 deletions(-) create mode 100644 avr/cores/megacommand/.vim/coc-settings.json diff --git a/avr/cores/megacommand/.vim/coc-settings.json b/avr/cores/megacommand/.vim/coc-settings.json new file mode 100644 index 000000000..86b3286a4 --- /dev/null +++ b/avr/cores/megacommand/.vim/coc-settings.json @@ -0,0 +1,15 @@ +{ + "languageserver": { + "ccls": { + "command": "ccls", + "filetypes": ["c", "cpp", "objc", "objcpp", "cuda"], + "rootPatterns": [".ccls", "compile_commands.json", ".vim/", ".git/", ".hg/"], + "initializationOptions": { + "cache": { + "directory": "../../../.ccls-cache" + }, + "highlight": { "lsRanges" : true } + } + } + } +} diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 4e5973f5f..8eaa566ec 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -2,24 +2,21 @@ #include "MCL.h" void FileBrowserPage::setup() { - bool ret; - int b; #ifdef OLED_DISPLAY oled_display.clearDisplay(); classic_display = false; - //char *mcl = ".mcl"; - //strcpy(match, mcl); + // char *mcl = ".mcl"; + // strcpy(match, mcl); #endif - char *files = "Files"; - strcpy(title, files); + strcpy(title, "Files"); DEBUG_PRINT_FN(); } void FileBrowserPage::add_entry(char *entry) { - uint32_t pos = BANK1_FILE_ENTRIES_START + numEntries * 16; - volatile uint8_t *ptr = pos; - memcpy_bank1(ptr, entry, 16); - numEntries++; + uint32_t pos = BANK1_FILE_ENTRIES_START + numEntries * 16; + volatile uint8_t *ptr = (uint8_t*)pos; + memcpy_bank1(ptr, entry, 16); + numEntries++; } void FileBrowserPage::init() { @@ -27,55 +24,53 @@ void FileBrowserPage::init() { char temp_entry[16]; int index = 0; - // file.open("/",O_READ); + // reset directory pointer SD.vwd()->rewind(); numEntries = 0; cur_file = 255; if (show_save) { - char create_new[9] = "[ SAVE ]"; - add_entry(&create_new[0]); + char create_new[9] = "[ SAVE ]"; + add_entry(&create_new[0]); } if (show_new_folder) { - char folder[16] = "[ NEW FOLDER ]"; - add_entry(&folder[0]); + char folder[16] = "[ NEW FOLDER ]"; + add_entry(&folder[0]); } char up_one_dir[3] = ".."; SD.vwd()->getName(temp_entry, 16); DEBUG_PRINTLN(temp_entry); - if ((show_parent) && !(strcmp(temp_entry,"/") == 0)) { + if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { add_entry(&up_one_dir[0]); } encoders[1]->cur = 1; + // iterate through the files while (file.openNext(SD.vwd(), O_READ) && (numEntries < MAX_ENTRIES)) { for (uint8_t c = 0; c < 16; c++) { temp_entry[c] = 0; } file.getName(temp_entry, 16); bool is_match_file = false; - // if (dir_browser == true) { DEBUG_PRINTLN(temp_entry); if (temp_entry[0] == '.') { is_match_file = false; - } else if (file.isDirectory() && dir_browser) { + } else if (file.isDirectory() && show_dirs) { is_match_file = true; } else { - // } else { - char *arg1 = &temp_entry[strlen(temp_entry) - 4]; - DEBUG_PRINTLN(arg1); - if (strcmp(arg1,match) == 0) { + char *arg1 = &temp_entry[strlen(temp_entry) - 4]; + DEBUG_PRINTLN(arg1); + if (strcmp(arg1, match) == 0) { is_match_file = true; - } + } } - // } if (is_match_file) { - DEBUG_PRINTLN("project file identified"); - add_entry(&temp_entry[0]); - if (strcmp(&temp_entry[0], &mcl_cfg.project[0]) == 0) { + DEBUG_PRINTLN("file matched"); + add_entry(temp_entry); + if (strcmp(temp_entry, mcl_cfg.project) == 0) { DEBUG_PRINTLN("match"); DEBUG_PRINTLN(temp_entry); DEBUG_PRINTLN(mcl_cfg.project); @@ -83,7 +78,6 @@ void FileBrowserPage::init() { cur_file = numEntries - 1; encoders[1]->cur = numEntries - 1; } - } index++; file.close(); @@ -95,7 +89,7 @@ void FileBrowserPage::init() { ((MCLEncoder *)encoders[1])->max = 0; } ((MCLEncoder *)encoders[1])->max = numEntries - 1; - DEBUG_PRINTLN("finished load proj setup"); + DEBUG_PRINTLN("finished list files"); } void FileBrowserPage::display() { @@ -152,7 +146,7 @@ void FileBrowserPage::display() { GUI.put_string_at_fill(0, ">"); } else { GUI.put_string_at_fill(0, " "); - } + } char temp_entry[17]; uint16_t entry_num = encoders[1]->cur; uint32_t pos = BANK1_FILE_ENTRIES_START + entry_num * 16; @@ -208,18 +202,18 @@ void FileBrowserPage::loop() { } bool FileBrowserPage::create_folder() { - char *my_title = "Create Folder"; - char new_dir[17] = "new_folder "; - if (mcl_gui.wait_for_input(new_dir, my_title, 8)) { - for (uint8_t n = 0; n < strlen(new_dir); n++) { - if (new_dir[n] == ' ') { - new_dir[n] = '\0'; - } + char *my_title = "Create Folder"; + char new_dir[17] = "new_folder "; + if (mcl_gui.wait_for_input(new_dir, my_title, 8)) { + for (uint8_t n = 0; n < strlen(new_dir); n++) { + if (new_dir[n] == ' ') { + new_dir[n] = '\0'; } - SD.mkdir(new_dir); - init(); } - return true; + SD.mkdir(new_dir); + init(); + } + return true; } bool FileBrowserPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { @@ -232,7 +226,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER4)) { if (encoders[0]->getValue() == 0) { - return; + return; } if (encoders[1]->getValue() == 1) { @@ -306,9 +300,8 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON1) || EVENT_RELEASED(event, Buttons.BUTTON3) || EVENT_PRESSED(event, Buttons.BUTTON4)) { - GUI.setPage(&grid_page); - return true; + GUI.setPage(&grid_page); + return true; } return false; } - diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 9b457a36e..50bfac645 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -30,7 +30,7 @@ class FileBrowserPage : public LightPage { uint8_t cur_col = 0; uint8_t cur_row = 0; uint8_t cur_file = 0; - bool dir_browser = false; + bool show_dirs = false; bool show_save = true; bool show_parent = true; bool show_new_folder = true; diff --git a/avr/cores/megacommand/MCL/LoadProjectPage.cpp b/avr/cores/megacommand/MCL/LoadProjectPage.cpp index d90b2d004..f1cfd58d1 100644 --- a/avr/cores/megacommand/MCL/LoadProjectPage.cpp +++ b/avr/cores/megacommand/MCL/LoadProjectPage.cpp @@ -5,7 +5,7 @@ void LoadProjectPage::init() { DEBUG_PRINT_FN(); show_save = false; - dir_browser = false; + show_dirs = false; show_new_folder = false; char *mcl = ".mcl"; strcpy(match, mcl); diff --git a/avr/cores/megacommand/MCL/MCL.cpp b/avr/cores/megacommand/MCL/MCL.cpp index 748108752..79a3ce6e9 100644 --- a/avr/cores/megacommand/MCL/MCL.cpp +++ b/avr/cores/megacommand/MCL/MCL.cpp @@ -3,7 +3,6 @@ int8_t curpage; uint8_t patternswitch = PATTERN_UDEF; - void MCL::setup() { DEBUG_PRINTLN("Welcome to MegaCommand Live"); DEBUG_PRINTLN(VERSION); @@ -60,11 +59,10 @@ void MCL::setup() { GUI.display_mirror = true; #endif } - if (mcl_cfg.screen_saver == 1) { - GUI.use_screen_saver = true; - } - else { - GUI.use_screen_saver = false; + if (mcl_cfg.screen_saver == 1) { + GUI.use_screen_saver = true; + } else { + GUI.use_screen_saver = false; } DEBUG_PRINTLN("Track sizes:"); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index d989fdbd1..ae125505f 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -1,10 +1,13 @@ -#include "MCL.h" #include "SDDrivePage.h" +#include "MCL.h" -const char* c_snapshot_suffix = ".snap"; +const char *c_snapshot_suffix = ".snp"; +const char *c_snapshot_root = "/Snapshots/MD"; void SDDrivePage::setup() { - SD.mkdir("/Snapshots/MD", true); + SD.mkdir(c_snapshot_root, true); + SD.chdir(c_snapshot_root); + strcpy(lwd, c_snapshot_root); FileBrowserPage::setup(); } @@ -12,11 +15,11 @@ void SDDrivePage::init() { DEBUG_PRINT_FN(); md_exploit.off(); + // !note match only supports 3-char suffix strcpy(match, c_snapshot_suffix); - dir_browser = true; + show_dirs = true; strcpy(title, "Snapshots"); FileBrowserPage::init(); - } void SDDrivePage::save_snapshot() { @@ -24,34 +27,48 @@ void SDDrivePage::save_snapshot() { MDSound sound; char sound_name[] = "________"; - char my_title[] = "Snapshot Name"; - if (mcl_gui.wait_for_input(sound_name, my_title, 8)) { + if (mcl_gui.wait_for_input(sound_name, "Snapshot Name", 8)) { + if (file.isOpen()) { + file.close(); + } + char temp_entry[16]; strcpy(temp_entry, sound_name); strcat(temp_entry, c_snapshot_suffix); DEBUG_PRINTLN("creating new snapshot:"); DEBUG_PRINTLN(temp_entry); - file.open(temp_entry, O_RDWR | O_CREAT); + if (!file.open(temp_entry, O_WRITE | O_CREAT)) { + DEBUG_PRINTLN("error openning"); + gfx.alert("Error", "Cannot open file for write"); + return; + } // Globals - for(int i=0;i<8;++i){ + for (int i = 0; i < 8; ++i) { + DEBUG_PRINT("saving global #"); + DEBUG_PRINTLN(i); MD.getBlockingGlobal(i); mcl_sd.write_data(&MD.global, sizeof(MD.global), &file); } // Patterns - for(int i=0;i<128;++i){ + for (int i = 0; i < 128; ++i) { + DEBUG_PRINT("getting pattern #"); + DEBUG_PRINTLN(i); MD.getBlockingPattern(i); + DEBUG_PRINT("writting pattern"); mcl_sd.write_data(&MD.pattern, sizeof(MD.pattern), &file); } // Kits - for(int i=0;i<64;++i){ + for (int i = 0; i < 64; ++i) { + DEBUG_PRINT("saving kit #"); + DEBUG_PRINTLN(i); MD.getBlockingKit(i); mcl_sd.write_data(&MD.kit, sizeof(MD.kit), &file); } // Songs - //for(int i=0;i<64;++i){ - //MD.getBlockingSong(i); - //mcl_sd.write_data(&MD.song, sizeof(MD.song), &file); // <--- ?? + // for(int i=0;i<64;++i){ + // MD.getBlockingSong(i); + // mcl_sd.write_data(&MD.song, sizeof(MD.song), &file); // <--- ?? //} // Save complete file.close(); @@ -68,10 +85,9 @@ void SDDrivePage::load_snapshot() { file.close(); DEBUG_PRINTLN("loading snapshot"); DEBUG_PRINTLN(temp_entry); - if (!file.open(temp_entry, O_READ)) - { + if (!file.open(temp_entry, O_READ)) { DEBUG_PRINTLN("error openning"); - gfx.alert("Error", "Opening"); + gfx.alert("Error", "Cannot open file for read"); return; } @@ -80,9 +96,10 @@ void SDDrivePage::load_snapshot() { // Stop everything grid_page.prepare(); - // Globals - for(int i=0;i<8;++i){ + for (int i = 0; i < 8; ++i) { + DEBUG_PRINT("loading global #"); + DEBUG_PRINTLN(i); mcl_sd.read_data(&MD.global, sizeof(MD.global), &file); mcl_actions.md_setsysex_recpos(2, i); { @@ -91,27 +108,28 @@ void SDDrivePage::load_snapshot() { } } // Patterns - for(int i=0;i<128;++i){ + for (int i = 0; i < 128; ++i) { + DEBUG_PRINT("loading pattern #"); + DEBUG_PRINTLN(i); mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file); mcl_actions.md_setsysex_recpos(8, i); MD.pattern.toSysex(); } // Kits - for(int i=0;i<64;++i){ + for (int i = 0; i < 64; ++i) { + DEBUG_PRINT("loading kit #"); + DEBUG_PRINTLN(i); mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file); mcl_actions.md_setsysex_recpos(4, i); MD.kit.toSysex(); } // Load complete file.close(); - gfx.alert("Loaded","Snapshot"); + gfx.alert("Loaded", "Snapshot"); } - } - -bool SDDrivePage::handleEvent(gui_event_t *event) -{ +bool SDDrivePage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { return true; @@ -135,7 +153,7 @@ bool SDDrivePage::handleEvent(gui_event_t *event) char temp_entry[16]; char dir_entry[16]; uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = (uint8_t*)pos; + volatile uint8_t *ptr = (uint8_t *)pos; memcpy_bank1(&temp_entry[0], ptr, 16); if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { @@ -170,10 +188,11 @@ bool SDDrivePage::handleEvent(gui_event_t *event) return true; } - - // if ((EVENT_RELEASED(event, Buttons.BUTTON1) && BUTTON_DOWN(Buttons.BUTTON3)) || + + // if ((EVENT_RELEASED(event, Buttons.BUTTON1) && + // BUTTON_DOWN(Buttons.BUTTON3)) || if (EVENT_RELEASED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON1)) { - char temp_entry[16]; + char temp_entry[16]; char dir_entry[16]; uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; volatile uint8_t *ptr = pos; @@ -187,8 +206,8 @@ bool SDDrivePage::handleEvent(gui_event_t *event) EVENT_RELEASED(event, Buttons.BUTTON1) || EVENT_RELEASED(event, Buttons.BUTTON4)) { // EVENT_RELEASED(event, Buttons.BUTTON4)) { - GUI.setPage(&grid_page); - return true; + GUI.setPage(&grid_page); + return true; } return false; } diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp index 0b4143d2e..9bb4c7585 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp @@ -12,7 +12,7 @@ void SoundBrowserPage::init() { md_exploit.off(); char *snd = ".snd"; strcpy(match, snd); - dir_browser = true; + show_dirs = true; char *files = "Sounds"; strcpy(title, files); FileBrowserPage::init(); diff --git a/avr/cores/megacommand/WProgram.h b/avr/cores/megacommand/WProgram.h index 5a77d8578..66370408a 100644 --- a/avr/cores/megacommand/WProgram.h +++ b/avr/cores/megacommand/WProgram.h @@ -11,7 +11,7 @@ #include "wiring_private.h" -//#define DEBUGMODE +#define DEBUGMODE #ifdef MEGACOMMAND #define SD_CS 53 //PB0 From 975207111cf1b82f12ca2fe2fc007c7a6ab3858b Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 13 Oct 2019 22:01:14 +0800 Subject: [PATCH 030/469] refactoring. consolidate drawing routines --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 27 +---- avr/cores/megacommand/MCL/MCLGUI.cpp | 29 ++++- avr/cores/megacommand/MCL/MCLGUI.h | 3 + avr/cores/megacommand/MCL/MenuPage.cpp | 111 +++++++----------- 4 files changed, 77 insertions(+), 93 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 8eaa566ec..2342c2ad0 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -100,11 +100,7 @@ void FileBrowserPage::display() { oled_display.setCursor(0, 8); oled_display.setTextColor(WHITE, BLACK); oled_display.println(title); - for (uint8_t n = 0; n < 32; n++) { - if (n % 2 != 0) { - oled_display.drawPixel(x_offset - 6, n, WHITE); - } - } + mcl_gui.draw_vertical_dashline(x_offset - 6); oled_display.setCursor(x_offset, 8); uint8_t max_items; @@ -130,7 +126,7 @@ void FileBrowserPage::display() { char temp_entry[16]; uint16_t entry_num = encoders[1]->cur - cur_row + n; uint32_t pos = BANK1_FILE_ENTRIES_START + entry_num * 16; - volatile uint8_t *ptr = pos; + volatile uint8_t *ptr = (uint8_t*)pos; memcpy_bank1(temp_entry, ptr, 16); oled_display.println(temp_entry); } @@ -161,24 +157,7 @@ void FileBrowserPage::display() { void FileBrowserPage::draw_scrollbar(uint8_t x_offset) { #ifdef OLED_DISPLAY - uint8_t number_of_items = numEntries; - uint8_t length = round( - ((float)(MAX_VISIBLE_ROWS - 1) / (float)(number_of_items - 1)) * 32); - uint8_t y = round( - ((float)(encoders[1]->cur - cur_row) / (float)(number_of_items - 1)) * - 32); - for (uint8_t n = 0; n < 32; n++) { - if (n % 2 == 0) { - oled_display.drawPixel(x_offset + 1, n, WHITE); - oled_display.drawPixel(x_offset + 3, n, WHITE); - - } else { - oled_display.drawPixel(x_offset + 2, n, WHITE); - } - } - - oled_display.fillRect(x_offset + 1, y + 1, 3, length - 2, BLACK); - oled_display.drawRect(x_offset, y, 5, length, WHITE); + mcl_gui.draw_vertical_scrollbar(x_offset, numEntries, MAX_VISIBLE_ROWS, encoders[1]->cur - cur_row); #endif } diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index b0528c523..38bb177bc 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -5,8 +5,35 @@ bool MCLGUI::wait_for_input(char *dst, char *title, uint8_t len) { text_input_page.init_text(dst, title, len); GUI.pushPage(&text_input_page); while (GUI.currentPage() == &text_input_page) { - GUI.loop(); + GUI.loop(); } return text_input_page.return_state; } +void MCLGUI::draw_vertical_dashline(uint8_t x) { + for (uint8_t y = 1; y < 32; y += 2) { + oled_display.drawPixel(x, y, WHITE); + } +} + +void MCLGUI::draw_vertical_separator(uint8_t x) { + auto x_ = x + 2; + for (uint8_t y = 0; y < 32; y += 2) { + oled_display.drawPixel(x, y, WHITE); + oled_display.drawPixel(x_, y, WHITE); + } + x_ = x + 1; + for (uint8_t y = 1; y < 32; y += 2) { + oled_display.drawPixel(x_, y, WHITE); + } +} + +void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, + uint8_t n_window, uint8_t n_current) { + uint8_t length = round(((float)(n_window - 1) / (float)(n_items - 1)) * 32); + uint8_t y = round(((float)(n_current) / (float)(n_items - 1)) * 32); + mcl_gui.draw_vertical_separator(x); + oled_display.fillRect(x + 1, y + 1, 3, length - 2, BLACK); + oled_display.drawRect(x, y, 5, length, WHITE); +} + diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 384c36a62..03c9f3d21 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -8,6 +8,9 @@ class MCLGUI { public: bool wait_for_input(char *dst, char *title, uint8_t len); + void draw_vertical_dashline(uint8_t x); + void draw_vertical_separator(uint8_t x); + void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); }; extern MCLGUI mcl_gui; diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 0c07dfd50..75f89fca9 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -1,6 +1,6 @@ -#include "MCL.h" #include "MenuPage.h" +#include "MCL.h" void MenuPage::init() { uint8_t *dest_var = menu.get_dest_variable(encoders[1]->cur); @@ -24,14 +24,14 @@ void MenuPage::loop() { uint8_t diff = encoders[1]->cur - encoders[1]->old; int8_t new_val = cur_row + diff; - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY if (new_val > MAX_VISIBLE_ROWS - 1) { new_val = MAX_VISIBLE_ROWS - 1; } if (new_val < 0) { new_val = 0; } - #endif +#endif // MD.assignMachine(0, encoders[1]->cur); cur_row = new_val; uint8_t *dest_var = menu.get_dest_variable(encoders[1]->cur); @@ -51,23 +51,8 @@ void MenuPage::loop() { void MenuPage::draw_scrollbar(uint8_t x_offset) { #ifdef OLED_DISPLAY - uint8_t number_of_items = menu.get_number_of_items(); - uint8_t length = - round(((float)(MAX_VISIBLE_ROWS - 1) / (float)(number_of_items - 1)) * 32); - uint8_t y = - round(((float)(encoders[1]->cur - cur_row) / (float)(number_of_items - 1)) * 32); - for (uint8_t n = 0; n < 32; n++) { - if (n % 2 == 0) { - oled_display.drawPixel(x_offset + 1, n, WHITE); - oled_display.drawPixel(x_offset + 3, n, WHITE); - - } else { - oled_display.drawPixel(x_offset + 2, n, WHITE); - } - } - - oled_display.fillRect(x_offset + 1, y + 1, 3, length - 2, BLACK); - oled_display.drawRect(x_offset, y, 5, length, WHITE); + mcl_gui.draw_vertical_scrollbar(x_offset, menu.get_number_of_items(), + MAX_VISIBLE_ROWS, encoders[1]->cur - cur_row); #endif } @@ -82,7 +67,7 @@ void MenuPage::draw_item(uint8_t item_n, uint8_t row) { uint8_t number_of_items = menu.get_number_of_items(); if (item_n > number_of_items - 1) { - return true; + return; } uint8_t number_of_options = menu.get_number_of_options(item_n); @@ -123,12 +108,10 @@ void MenuPage::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width) { draw_item(encoders[1]->cur - cur_row + n, n); } - // draw_item.read(getRow()); - + // draw_item.read(getRow()); oled_display.setTextColor(WHITE, BLACK); #endif - } void MenuPage::display() { @@ -139,43 +122,37 @@ void MenuPage::display() { m_strncpy_p(str, pgp, 16); - uint8_t number_of_items = menu.get_number_of_items(); - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY uint8_t x_offset = 43; oled_display.clearDisplay(); oled_display.setTextColor(WHITE, BLACK); oled_display.setFont(&TomThumb); oled_display.setCursor(0, 8); oled_display.println(str); - for (uint8_t n = 0; n < 32; n++) { - if (n % 2 != 0) { - oled_display.drawPixel(x_offset - 6, n, WHITE); - } - } + mcl_gui.draw_vertical_dashline(x_offset - 6); draw_menu(x_offset, 8); if (number_of_items > MAX_VISIBLE_ROWS) { - draw_scrollbar(120); + draw_scrollbar(120); } oled_display.display(); - #else - GUI.setLine(GUI.LINE1); - GUI.put_string_at(0,"["); - GUI.put_string_at(1, str); +#else + GUI.setLine(GUI.LINE1); + GUI.put_string_at(0, "["); + GUI.put_string_at(1, str); - GUI.put_string_at(m_strlen(str),"]"); - pgp = menu.get_item_name(cur_row); + GUI.put_string_at(m_strlen(str), "]"); + pgp = menu.get_item_name(cur_row); - GUI.setLine(GUI.LINE2); - if (pgp != NULL) { + GUI.setLine(GUI.LINE2); + if (pgp != NULL) { m_strncpy_p(str, pgp, 16); - GUI.put_string_at_fill(0,str); + GUI.put_string_at_fill(0, str); } - if (cur_row > number_of_items - 1) { return true; } @@ -185,10 +162,10 @@ void MenuPage::display() { pgp = menu.get_option_name(cur_row, *(menu.get_dest_variable(cur_row))); if (pgp == NULL) { - GUI.put_value_at(10,*(menu.get_dest_variable(cur_row))); + GUI.put_value_at(10, *(menu.get_dest_variable(cur_row))); } else { m_strncpy_p(str, pgp, 11); - GUI.put_string_at(10,str); + GUI.put_string_at(10, str); } } @@ -196,33 +173,31 @@ void MenuPage::display() { } bool MenuPage::enter() { - DEBUG_PRINT_FN(); - void (*row_func)() = menu.get_row_function(encoders[1]->cur); - Page *page_callback = menu.get_page_callback(encoders[1]->cur); - if (page_callback != NULL) { - DEBUG_PRINTLN("setting page"); - DEBUG_PRINTLN((uint16_t)page_callback); - GUI.pushPage(page_callback); - return; - } - if (row_func != NULL) { - DEBUG_PRINTLN("calling callback func"); - (*row_func)(); - } - + DEBUG_PRINT_FN(); + void (*row_func)() = menu.get_row_function(encoders[1]->cur); + Page *page_callback = menu.get_page_callback(encoders[1]->cur); + if (page_callback != NULL) { + DEBUG_PRINTLN("setting page"); + DEBUG_PRINTLN((uint16_t)page_callback); + GUI.pushPage(page_callback); + return false; + } + if (row_func != NULL) { + DEBUG_PRINTLN("calling callback func"); + (*row_func)(); + } } bool MenuPage::exit() { - // Page *exit_page_callback = menu.get_exit_page_callback(); - void (*exit_func)() = menu.get_exit_function(); - if (exit_func != NULL) { - (*exit_func)(); - // - } - // if (exit_page_callback != NULL) { - GUI.popPage(); - //} - + // Page *exit_page_callback = menu.get_exit_page_callback(); + void (*exit_func)() = menu.get_exit_function(); + if (exit_func != NULL) { + (*exit_func)(); + // + } + // if (exit_page_callback != NULL) { + GUI.popPage(); + //} } bool MenuPage::handleEvent(gui_event_t *event) { From 3624b4c59d78ae0be8ad77f119ea51acd04310c2 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 13 Oct 2019 23:26:46 +0800 Subject: [PATCH 031/469] progress bar infobox ok. --- avr/cores/megacommand/MCL/MCLGUI.cpp | 54 ++++++++++++++++++++++- avr/cores/megacommand/MCL/MCLGUI.h | 1 + avr/cores/megacommand/MCL/SDDrivePage.cpp | 27 ++++++------ 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 38bb177bc..d8a726d5a 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -32,8 +32,60 @@ void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current) { uint8_t length = round(((float)(n_window - 1) / (float)(n_items - 1)) * 32); uint8_t y = round(((float)(n_current) / (float)(n_items - 1)) * 32); - mcl_gui.draw_vertical_separator(x); + mcl_gui.draw_vertical_separator(x+1); oled_display.fillRect(x + 1, y + 1, 3, length - 2, BLACK); oled_display.drawRect(x, y, 5, length, WHITE); } +static uint8_t s_prog_cookie = 0; + +static constexpr uint8_t s_menu_w = 96; +static constexpr uint8_t s_menu_h = 24; +static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; +static constexpr uint8_t s_menu_y = (32 - s_menu_h) / 2; +static constexpr uint8_t s_title_x = 31; +static constexpr uint8_t s_title_w = 64; +static constexpr uint8_t s_progress_x = 31; +static constexpr uint8_t s_progress_y = 16; +static constexpr uint8_t s_progress_w = 64; +static constexpr uint8_t s_progress_h = 5; + +static uint8_t s_progress_cookie = 0; + +void MCLGUI::draw_progress(char* msg, uint8_t cur, uint8_t _max) +{ + oled_display.setFont(&TomThumb); + + // draw menu body + oled_display.fillRect(s_menu_x-1, s_menu_y-1, s_menu_w+2, s_menu_h+2, BLACK); + oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, s_menu_h, WHITE); + oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 3, WHITE); + + // draw the title '____/**********\____' part + oled_display.drawRect(s_title_x, s_menu_y - 3, s_title_w, 3, BLACK); + oled_display.drawRect(s_title_x, s_menu_y - 2, s_title_w, 2, WHITE); + oled_display.drawPixel(s_title_x, s_menu_y - 2, BLACK); + oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 2, BLACK); + + oled_display.setTextColor(BLACK); + //auto len = strlen(msg) * 3; + //oled_display.setCursor(64 - (len / 2), s_menu_y); + oled_display.setCursor(s_title_x + 2, s_menu_y + 3); + oled_display.println(msg); + oled_display.setTextColor(WHITE); + + oled_display.fillRect(s_progress_x+1, s_progress_y+1, s_progress_w-2, s_progress_h-2, BLACK); + + float prog = (float)cur / (float)_max; + auto progx = (uint8_t)(s_progress_x + 1 + prog * (s_progress_w - 2)); + oled_display.fillRect(s_progress_x+1, s_progress_y+1, progx - s_progress_x + 1, s_progress_h - 2, WHITE); + s_progress_cookie = (s_progress_cookie + 1) % 3; + + for(uint8_t i = s_progress_cookie + s_progress_x + 1; i < progx; i += 3) + { + oled_display.drawLine(i, s_progress_y + s_progress_h - 2, i+2, s_progress_y + 1, BLACK); + } + + oled_display.drawRect(s_progress_x, s_progress_y, s_progress_w, s_progress_h, WHITE); + oled_display.display(); +} diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 03c9f3d21..703c9705e 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -11,6 +11,7 @@ class MCLGUI { void draw_vertical_dashline(uint8_t x); void draw_vertical_separator(uint8_t x); void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); + void draw_progress(char* msg, uint8_t cur, uint8_t _max); }; extern MCLGUI mcl_gui; diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index ae125505f..06e93b53a 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -33,6 +33,11 @@ void SDDrivePage::save_snapshot() { file.close(); } + MidiUart.sendRaw(MIDI_STOP); + MidiClock.handleImmediateMidiStop(); + // Stop everything + grid_page.prepare(); + char temp_entry[16]; strcpy(temp_entry, sound_name); strcat(temp_entry, c_snapshot_suffix); @@ -45,23 +50,20 @@ void SDDrivePage::save_snapshot() { } // Globals for (int i = 0; i < 8; ++i) { - DEBUG_PRINT("saving global #"); - DEBUG_PRINTLN(i); + mcl_gui.draw_progress("Saving global", i, 8); MD.getBlockingGlobal(i); mcl_sd.write_data(&MD.global, sizeof(MD.global), &file); } // Patterns for (int i = 0; i < 128; ++i) { - DEBUG_PRINT("getting pattern #"); - DEBUG_PRINTLN(i); + mcl_gui.draw_progress("Saving pattern", i, 128); + DEBUG_PRINT(i); MD.getBlockingPattern(i); - DEBUG_PRINT("writting pattern"); mcl_sd.write_data(&MD.pattern, sizeof(MD.pattern), &file); } // Kits for (int i = 0; i < 64; ++i) { - DEBUG_PRINT("saving kit #"); - DEBUG_PRINTLN(i); + mcl_gui.draw_progress("Saving kit", i, 64); MD.getBlockingKit(i); mcl_sd.write_data(&MD.kit, sizeof(MD.kit), &file); } @@ -98,8 +100,7 @@ void SDDrivePage::load_snapshot() { // Globals for (int i = 0; i < 8; ++i) { - DEBUG_PRINT("loading global #"); - DEBUG_PRINTLN(i); + mcl_gui.draw_progress("Loading global", i, 8); mcl_sd.read_data(&MD.global, sizeof(MD.global), &file); mcl_actions.md_setsysex_recpos(2, i); { @@ -109,16 +110,14 @@ void SDDrivePage::load_snapshot() { } // Patterns for (int i = 0; i < 128; ++i) { - DEBUG_PRINT("loading pattern #"); - DEBUG_PRINTLN(i); + mcl_gui.draw_progress("Loading pattern", i, 128); mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file); mcl_actions.md_setsysex_recpos(8, i); MD.pattern.toSysex(); } // Kits for (int i = 0; i < 64; ++i) { - DEBUG_PRINT("loading kit #"); - DEBUG_PRINTLN(i); + mcl_gui.draw_progress("Loading kit", i, 64); mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file); mcl_actions.md_setsysex_recpos(4, i); MD.kit.toSysex(); @@ -195,7 +194,7 @@ bool SDDrivePage::handleEvent(gui_event_t *event) { char temp_entry[16]; char dir_entry[16]; uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = pos; + volatile uint8_t *ptr = (uint8_t*)pos; memcpy_bank1(&temp_entry[0], ptr, 16); SD.remove(temp_entry); init(); From 0ce9acffcd279fc6e7b148919e9dac846f7b0a9c Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 14 Oct 2019 00:32:04 +0800 Subject: [PATCH 032/469] prevent bad load from bricking the device; fix browser .. nav --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 16 +++---- avr/cores/megacommand/MCL/SDDrivePage.cpp | 45 +++++++++++++++---- avr/cores/megacommand/WProgram.h | 2 + 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 2342c2ad0..d13a08ff9 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -205,18 +205,18 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER4)) { if (encoders[0]->getValue() == 0) { - return; + return false; } if (encoders[1]->getValue() == 1) { create_folder(); - return; + return false; } char temp_entry[16]; char dir_entry[16]; uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = pos; + volatile uint8_t *ptr = (uint8_t*)pos; memcpy_bank1(temp_entry, ptr, 16); char *up_one_dir = ".."; if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { @@ -234,13 +234,13 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { SD.vwd()->getName(dir_entry, 16); lwd[strlen(lwd) - strlen(dir_entry) - 1] = '\0'; - DEBUG_PRINTLN(lwd); + DEBUG_DUMP(lwd); init(); - return; + return false; SD.vwd()->getName(temp_entry, 16); - DEBUG_PRINTLN(temp_entry); - return; + DEBUG_DUMP(temp_entry); + return false; } file.open(temp_entry, O_READ); if (file.isDirectory()) { @@ -255,7 +255,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { DEBUG_PRINTLN(temp_entry); SD.chdir(temp_entry); init(); - return; + return false; } GUI.popPage(); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 06e93b53a..b492324c9 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -2,7 +2,7 @@ #include "MCL.h" const char *c_snapshot_suffix = ".snp"; -const char *c_snapshot_root = "/Snapshots/MD"; +const char *c_snapshot_root = "/SDDrive/MD"; void SDDrivePage::setup() { SD.mkdir(c_snapshot_root, true); @@ -18,7 +18,7 @@ void SDDrivePage::init() { // !note match only supports 3-char suffix strcpy(match, c_snapshot_suffix); show_dirs = true; - strcpy(title, "Snapshots"); + strcpy(title, "SD-Drive"); FileBrowserPage::init(); } @@ -101,7 +101,10 @@ void SDDrivePage::load_snapshot() { // Globals for (int i = 0; i < 8; ++i) { mcl_gui.draw_progress("Loading global", i, 8); - mcl_sd.read_data(&MD.global, sizeof(MD.global), &file); + if(!mcl_sd.read_data(&MD.global, sizeof(MD.global), &file)) + { + goto load_error; + } mcl_actions.md_setsysex_recpos(2, i); { ElektronDataToSysexEncoder encoder(&MidiUart); @@ -111,20 +114,32 @@ void SDDrivePage::load_snapshot() { // Patterns for (int i = 0; i < 128; ++i) { mcl_gui.draw_progress("Loading pattern", i, 128); - mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file); + if(!mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file)) + { + goto load_error; + } mcl_actions.md_setsysex_recpos(8, i); MD.pattern.toSysex(); } // Kits for (int i = 0; i < 64; ++i) { mcl_gui.draw_progress("Loading kit", i, 64); - mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file); + if(!mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file)) + { + goto load_error; + } mcl_actions.md_setsysex_recpos(4, i); MD.kit.toSysex(); } // Load complete +load_complete: file.close(); gfx.alert("Loaded", "Snapshot"); + return; +load_error: + file.close(); + gfx.alert("Snapshot loading failed!", "SD card read failure"); + return; } } @@ -157,11 +172,23 @@ bool SDDrivePage::handleEvent(gui_event_t *event) { if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { file.close(); + SD.chdir(lwd); SD.vwd()->getName(dir_entry, 16); - lwd[strlen(lwd) - strlen(dir_entry) - 1] = '\0'; - DEBUG_PRINTLN(lwd); - SD.chdir(lwd); + auto len_lwd = strlen(lwd); + auto len_dir_entry = strlen(dir_entry); + + // trim ending '/' + if (lwd[len_lwd - 1] == '/') { + lwd[--len_lwd] = '\0'; + } + if (dir_entry[len_dir_entry - 1] == '/') { + dir_entry[--len_dir_entry] = '\0'; + } + + lwd[len_lwd - len_dir_entry] = '\0'; + DEBUG_DUMP(dir_entry); + DEBUG_DUMP(lwd); init(); return false; @@ -194,7 +221,7 @@ bool SDDrivePage::handleEvent(gui_event_t *event) { char temp_entry[16]; char dir_entry[16]; uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = (uint8_t*)pos; + volatile uint8_t *ptr = (uint8_t *)pos; memcpy_bank1(&temp_entry[0], ptr, 16); SD.remove(temp_entry); init(); diff --git a/avr/cores/megacommand/WProgram.h b/avr/cores/megacommand/WProgram.h index 66370408a..1b159fbe3 100644 --- a/avr/cores/megacommand/WProgram.h +++ b/avr/cores/megacommand/WProgram.h @@ -26,12 +26,14 @@ #define DEBUG_INIT() Serial.begin(SERIAL_SPEED); #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) +#define DEBUG_DUMP(x) ({Serial.print(#x); Serial.print(" = "); Serial.println(x);}) #define DEBUG_PRINT_FN(x) ({DEBUG_PRINT("func_call: "); DEBUG_PRINTLN(__FUNCTION__);}) #else #define DEBUG_INIT() #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) +#define DEBUG_DUMP(x) #define DEBUG_PRINT_FN(x) #endif From df400c4e823dd05a46d2994637739a8c37fb1e24 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Oct 2019 03:32:44 +0800 Subject: [PATCH 033/469] shift char pane integrated to text input page --- avr/cores/megacommand/.ccls | 2 +- .../Adafruit_SSD1305.cpp | 8 +- .../Adafruit_SSD1305.h | 1 + avr/cores/megacommand/MCL/FileBrowserPage.cpp | 10 +- avr/cores/megacommand/MCL/MCLGUI.cpp | 71 +++--- avr/cores/megacommand/MCL/MCLGUI.h | 13 +- avr/cores/megacommand/MCL/NewProjectPage.h | 2 - avr/cores/megacommand/MCL/SDDrivePage.cpp | 13 +- avr/cores/megacommand/MCL/TextInputPage.cpp | 241 ++++++++++++++---- avr/cores/megacommand/MCL/TextInputPage.h | 6 + 10 files changed, 274 insertions(+), 93 deletions(-) diff --git a/avr/cores/megacommand/.ccls b/avr/cores/megacommand/.ccls index e7980c8ea..d17a34ee1 100644 --- a/avr/cores/megacommand/.ccls +++ b/avr/cores/megacommand/.ccls @@ -1,6 +1,6 @@ clang++ %c -std=gnu11 -%cpp -std=c++11 +%cpp -std=c++14 # Treats all headers as c++ headers %h %hpp -x c++-header # Include dirs in Makefile: diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index 9e10e061f..c0567f0cf 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -140,10 +140,16 @@ void Adafruit_SSD1305::drawPixel(int16_t x, int16_t y, uint16_t color) { break; } +draw_pixel: // x is which column if (color == WHITE) buffer[x+ (y/8)*SSD1305_LCDWIDTH] |= _BV((y%8)); - else + else if (color == INVERT) + { + color = (buffer[x+ (y/8)*SSD1305_LCDWIDTH] & _BV((y%8))) ? BLACK : WHITE; + goto draw_pixel; + } + else // BLACK buffer[x+ (y/8)*SSD1305_LCDWIDTH] &= ~_BV((y%8)); } diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 09ec25896..5c8cee196 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -27,6 +27,7 @@ All text above, and the splash screen must be included in any redistribution #define BLACK 0 #define WHITE 1 +#define INVERT 2 /*========================================================================= SSD1305 Displays diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index d13a08ff9..b8ccba928 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -205,12 +205,12 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER4)) { if (encoders[0]->getValue() == 0) { - return false; + return true; } if (encoders[1]->getValue() == 1) { create_folder(); - return false; + return true; } char temp_entry[16]; @@ -237,10 +237,10 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { DEBUG_DUMP(lwd); init(); - return false; + return true; SD.vwd()->getName(temp_entry, 16); DEBUG_DUMP(temp_entry); - return false; + return true; } file.open(temp_entry, O_READ); if (file.isDirectory()) { @@ -255,7 +255,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { DEBUG_PRINTLN(temp_entry); SD.chdir(temp_entry); init(); - return false; + return true; } GUI.popPage(); diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index d8a726d5a..88ff2e6fd 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -32,32 +32,17 @@ void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current) { uint8_t length = round(((float)(n_window - 1) / (float)(n_items - 1)) * 32); uint8_t y = round(((float)(n_current) / (float)(n_items - 1)) * 32); - mcl_gui.draw_vertical_separator(x+1); + mcl_gui.draw_vertical_separator(x + 1); oled_display.fillRect(x + 1, y + 1, 3, length - 2, BLACK); oled_display.drawRect(x, y, 5, length, WHITE); } -static uint8_t s_prog_cookie = 0; - -static constexpr uint8_t s_menu_w = 96; -static constexpr uint8_t s_menu_h = 24; -static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; -static constexpr uint8_t s_menu_y = (32 - s_menu_h) / 2; -static constexpr uint8_t s_title_x = 31; -static constexpr uint8_t s_title_w = 64; -static constexpr uint8_t s_progress_x = 31; -static constexpr uint8_t s_progress_y = 16; -static constexpr uint8_t s_progress_w = 64; -static constexpr uint8_t s_progress_h = 5; - -static uint8_t s_progress_cookie = 0; - -void MCLGUI::draw_progress(char* msg, uint8_t cur, uint8_t _max) -{ +void MCLGUI::draw_popup(char *title, bool deferred_display) { oled_display.setFont(&TomThumb); // draw menu body - oled_display.fillRect(s_menu_x-1, s_menu_y-1, s_menu_w+2, s_menu_h+2, BLACK); + oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, s_menu_h + 2, + BLACK); oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, s_menu_h, WHITE); oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 3, WHITE); @@ -68,24 +53,52 @@ void MCLGUI::draw_progress(char* msg, uint8_t cur, uint8_t _max) oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 2, BLACK); oled_display.setTextColor(BLACK); - //auto len = strlen(msg) * 3; - //oled_display.setCursor(64 - (len / 2), s_menu_y); + // auto len = strlen(msg) * 3; + // oled_display.setCursor(64 - (len / 2), s_menu_y); oled_display.setCursor(s_title_x + 2, s_menu_y + 3); - oled_display.println(msg); + oled_display.println(title); oled_display.setTextColor(WHITE); + if (!deferred_display) { + oled_display.display(); + } +} + +void MCLGUI::clear_popup() { + oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, s_menu_h - 5, + BLACK); +} - oled_display.fillRect(s_progress_x+1, s_progress_y+1, s_progress_w-2, s_progress_h-2, BLACK); +static constexpr uint8_t s_progress_x = 31; +static constexpr uint8_t s_progress_y = 16; +static constexpr uint8_t s_progress_w = 64; +static constexpr uint8_t s_progress_h = 5; + +static uint8_t s_progress_cookie = 0; + +void MCLGUI::draw_progress(char *msg, uint8_t cur, uint8_t _max, + bool deferred_display) { + draw_popup(msg, true); + + oled_display.fillRect(s_progress_x + 1, s_progress_y + 1, s_progress_w - 2, + s_progress_h - 2, BLACK); float prog = (float)cur / (float)_max; auto progx = (uint8_t)(s_progress_x + 1 + prog * (s_progress_w - 2)); - oled_display.fillRect(s_progress_x+1, s_progress_y+1, progx - s_progress_x + 1, s_progress_h - 2, WHITE); + // draw the progress + oled_display.fillRect(s_progress_x + 1, s_progress_y + 1, + progx - s_progress_x + 1, s_progress_h - 2, WHITE); s_progress_cookie = (s_progress_cookie + 1) % 3; - for(uint8_t i = s_progress_cookie + s_progress_x + 1; i < progx; i += 3) - { - oled_display.drawLine(i, s_progress_y + s_progress_h - 2, i+2, s_progress_y + 1, BLACK); + // draw the '///////' pattern, note the cookie + for (uint8_t i = s_progress_cookie + s_progress_x + 1; i < progx; i += 3) { + oled_display.drawLine(i, s_progress_y + s_progress_h - 2, i + 2, + s_progress_y + 1, BLACK); } - oled_display.drawRect(s_progress_x, s_progress_y, s_progress_w, s_progress_h, WHITE); - oled_display.display(); + oled_display.drawRect(s_progress_x, s_progress_y, s_progress_w, s_progress_h, + WHITE); + if (!deferred_display) { + oled_display.display(); + } } + diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 703c9705e..b3e9205d2 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -11,7 +11,18 @@ class MCLGUI { void draw_vertical_dashline(uint8_t x); void draw_vertical_separator(uint8_t x); void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); - void draw_progress(char* msg, uint8_t cur, uint8_t _max); + /// Clear the content area of a popup + void clear_popup(); + void draw_popup(char* title, bool deferred_display = false); + void draw_progress(char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); + + static constexpr uint8_t s_menu_w = 96; + static constexpr uint8_t s_menu_h = 24; + static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; + static constexpr uint8_t s_menu_y = (32 - s_menu_h) / 2; + static constexpr uint8_t s_title_x = 31; + static constexpr uint8_t s_title_w = 64; + }; extern MCLGUI mcl_gui; diff --git a/avr/cores/megacommand/MCL/NewProjectPage.h b/avr/cores/megacommand/MCL/NewProjectPage.h index e1197ab9c..fd16acbba 100644 --- a/avr/cores/megacommand/MCL/NewProjectPage.h +++ b/avr/cores/megacommand/MCL/NewProjectPage.h @@ -7,8 +7,6 @@ #define FLASH_SPEED 400 -extern char allowedchar[38]; - class NewProjectPage : public LightPage { public: char newprj[18]; diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index b492324c9..9557002b8 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -101,8 +101,7 @@ void SDDrivePage::load_snapshot() { // Globals for (int i = 0; i < 8; ++i) { mcl_gui.draw_progress("Loading global", i, 8); - if(!mcl_sd.read_data(&MD.global, sizeof(MD.global), &file)) - { + if (!mcl_sd.read_data(&MD.global, sizeof(MD.global), &file)) { goto load_error; } mcl_actions.md_setsysex_recpos(2, i); @@ -114,8 +113,7 @@ void SDDrivePage::load_snapshot() { // Patterns for (int i = 0; i < 128; ++i) { mcl_gui.draw_progress("Loading pattern", i, 128); - if(!mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file)) - { + if (!mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file)) { goto load_error; } mcl_actions.md_setsysex_recpos(8, i); @@ -124,19 +122,18 @@ void SDDrivePage::load_snapshot() { // Kits for (int i = 0; i < 64; ++i) { mcl_gui.draw_progress("Loading kit", i, 64); - if(!mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file)) - { + if (!mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file)) { goto load_error; } mcl_actions.md_setsysex_recpos(4, i); MD.kit.toSysex(); } // Load complete -load_complete: + load_complete: file.close(); gfx.alert("Loaded", "Snapshot"); return; -load_error: + load_error: file.close(); gfx.alert("Snapshot loading failed!", "SD card read failure"); return; diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 71d51b33f..4f908e592 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -1,16 +1,41 @@ -#include "MCL.h" #include "TextInputPage.h" +#include "MCL.h" + +constexpr auto sz_allowedchar = 71; + +// idx -> chr +inline char _getchar(uint8_t i) { + if (i >= sz_allowedchar) + i = sz_allowedchar - 1; + return i + ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_&@-=! "]; +} + +// chr -> idx +uint8_t _findchar(char chr) +{ + // Check to see that the character chosen is in the list of allowed + // characters + for (auto x = 0; x < sz_allowedchar; ++x) { + if (chr == _getchar(x)) { + return x; + } + } + // Ensure the encoder does not go out of bounds, by resetting it to a + // character within the allowed characters list + return sz_allowedchar - 1; +} void TextInputPage::setup() {} void TextInputPage::init() { #ifdef OLED_DISPLAY + classic_display = false; + GUI.lines[0].flashActive = false; + GUI.lines[1].flashActive = false; oled_display.setTextColor(WHITE, BLACK); oled_display.setFont(); - oled_display.clearDisplay(); #endif - last_clock = slowclock; - encoders[0]->cur = 0; } void TextInputPage::init_text(char *text_, char *title_, uint8_t len) { @@ -19,92 +44,211 @@ void TextInputPage::init_text(char *text_, char *title_, uint8_t len) { length = len; max_length = len; m_strncpy(text, text_, len); - ((MCLEncoder *)encoders[0])->max = length - 1; - ((MCLEncoder *)encoders[1])->max = 68; - update_char(); + cursor_position = 0; + config_normal(); } void TextInputPage::update_char() { - uint8_t x = 0; - char allowedchar[71] = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_&@-=! "; - // Check to see that the character chosen is in the list of allowed - // characters - // - uint8_t match = 255; - while ((match == 255) && (x < 70)) { - if (text[encoders[0]->cur] == allowedchar[x]) { - match = x; - } - x++; - } - // Ensure the encoder does not go out of bounds, by resetting it to a - // character within the allowed characters list - encoders[1]->setValue(match); - // Update the projectname. + auto chr = text[cursor_position]; + auto match = _findchar(chr); + encoders[1]->old = encoders[1]->cur = match; encoders[0]->old = encoders[0]->cur; } -void TextInputPage::display() { +// normal: +// E0 -> cursor +// E1 -> choose char +void TextInputPage::config_normal() { + ((MCLEncoder *)encoders[0])->max = length - 1; + ((MCLEncoder *)encoders[1])->max = sz_allowedchar - 1; + normal_mode = true; + // restore E0 + encoders[0]->cur = cursor_position; + // restore E1 + update_char(); #ifdef OLED_DISPLAY - oled_display.clearDisplay(); + // redraw popup body + mcl_gui.draw_popup(title); +#endif + // update clock + last_clock = slowclock; +} + +// charpane layout: +// TomThumb: 5x6 +// Boundary: (1, 1, 126, 30) +// Dimension: (126, 30) +// draw each char in a 7x7 cell (padded) to fill the boundary. + +constexpr auto charpane_w = 18; +constexpr auto charpane_h = 4; +constexpr auto charpane_padx = 1; +constexpr auto charpane_pady = 6; + +// input: col and row +// output: coordinates on screen +static void calc_charpane_coord(uint8_t &x, uint8_t &y) { + x = 2 + (x * 7); + y = 2 + (y * 7); +} + +// charpane: +// E0 -> x axis [0..17] +// E1 -> y axis [0..3] +void TextInputPage::config_charpane() { +#ifndef OLED_DISPLAY + // char pane not supported on 1602 displays + return; #endif - char allowedchar[71] = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_&@-=! "; + ((MCLEncoder *)encoders[0])->max = charpane_w - 1; + ((MCLEncoder *)encoders[1])->max = charpane_h - 1; + normal_mode = false; + auto chridx = _findchar(text[cursor_position]); + // restore E0 + encoders[0]->cur = chridx % charpane_w; + encoders[0]->old = encoders[0]->cur; + // restore E1 + encoders[1]->cur = chridx / charpane_w; + encoders[1]->old = encoders[1]->cur; + + oled_display.setFont(&TomThumb); + oled_display.clearDisplay(); + oled_display.drawRect(0, 0, 128, 32, WHITE); + uint8_t chidx = 0; + for (uint8_t y = 0; y < charpane_h; ++y) { + for (uint8_t x = 0; x < charpane_w; ++x) { + auto sx = x, sy = y; + calc_charpane_coord(sx, sy); + oled_display.setCursor(sx + charpane_padx, sy + charpane_pady); + oled_display.print(_getchar(chidx)); + ++chidx; + } + } + + // initial highlight of selected char + uint8_t sx = encoders[0]->cur, sy = encoders[1]->cur; + calc_charpane_coord(sx, sy); + oled_display.fillRect(sx, sy, 7, 7, INVERT); + oled_display.display(); +} + +void TextInputPage::display_normal() { + constexpr auto s_text_x = MCLGUI::s_menu_x + 16; + constexpr auto s_text_y = MCLGUI::s_menu_y + 12; + + // update cursor position + cursor_position = encoders[0]->getValue(); // Check to see that the character chosen is in the list of allowed // characters - if (encoders[0]->hasChanged()) { + if (encoders[0]->hasChanged()) { update_char(); + last_clock = slowclock; } if (encoders[1]->hasChanged()) { last_clock = slowclock; + encoders[1]->old = encoders[1]->cur; + text[cursor_position] = _getchar(encoders[1]->getValue()); + } + + auto time = clock_diff(last_clock, slowclock); + +#ifdef OLED_DISPLAY + mcl_gui.clear_popup(); + oled_display.setFont(); + oled_display.setCursor(s_text_x, s_text_y); + oled_display.println(text); + if (time < FLASH_SPEED) { + // the default font is 6x8 + oled_display.fillRect(s_text_x + 6 * cursor_position, s_text_y, 6, 8, + INVERT); + } + if (time > FLASH_SPEED * 2) { + last_clock = slowclock; } - // if ((encoders[2]->hasChanged())){ - text[encoders[0]->getValue()] = allowedchar[encoders[1]->getValue()]; - // } + oled_display.display(); +#else GUI.setLine(GUI.LINE1); GUI.put_string_at(0, title); GUI.setLine(GUI.LINE2); char tmp_str[18]; memcpy(tmp_str, &text[0], 18); - if (clock_diff(last_clock, slowclock) > FLASH_SPEED) { - #ifdef OLED_DISPLAY - tmp_str[encoders[0]->getValue()] = (char) 3; - #else - tmp_str[encoders[0]->getValue()] = (char) 255; - #endif - + if (time > FLASH_SPEED) { + tmp_str[cursor_position] = (char)255; } - if (clock_diff(last_clock, slowclock) > FLASH_SPEED * 2) { + if (time > FLASH_SPEED * 2) { last_clock = slowclock; } GUI.clearLine(); GUI.put_string_at(0, tmp_str); +#endif } + +void TextInputPage::display_charpane() { + + if (encoders[0]->hasChanged() || encoders[1]->hasChanged()) { + // clear old highlight + uint8_t sx = encoders[0]->old, sy = encoders[1]->old; + calc_charpane_coord(sx, sy); + oled_display.fillRect(sx, sy, 7, 7, INVERT); + // draw new highlight + sx = encoders[0]->cur; sy = encoders[1]->cur; + calc_charpane_coord(sx, sy); + oled_display.fillRect(sx, sy, 7, 7, INVERT); + // update text. in charpane mode, cursor_position remains constant + uint8_t chridx = encoders[0]->cur + encoders[1]->cur * charpane_w; + text[cursor_position] = _getchar(chridx); + // mark encoders as unchanged + encoders[0]->old = encoders[0]->cur; + encoders[1]->old = encoders[1]->cur; + } + + last_clock = slowclock; + oled_display.display(); +} + +void TextInputPage::display() { + if (normal_mode) + display_normal(); + else + display_charpane(); +} + bool TextInputPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { return true; } + // in char-pane mode, do not handle any events + // except shift-release event. + if (!normal_mode) { + if (EVENT_RELEASED(event, Buttons.BUTTON2)) { + oled_display.clearDisplay(); + config_normal(); + return true; + } + return false; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { text_input_page.return_state = true; uint8_t cpy_len = text_input_page.length; - for (uint8_t n = text_input_page.length - 1; n > 0 && text_input_page.text[n] == ' '; n--) { - cpy_len -= 1; + for (uint8_t n = text_input_page.length - 1; + n > 0 && text_input_page.text[n] == ' '; n--) { + cpy_len -= 1; } - m_strncpy(text_input_page.textp, &(text_input_page.text[0]), - cpy_len); + m_strncpy(text_input_page.textp, &(text_input_page.text[0]), cpy_len); text_input_page.textp[cpy_len] = '\0'; GUI.popPage(); return true; } + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { // Toggle upper + lower case if (encoders[1]->cur <= 25) { @@ -125,8 +269,13 @@ bool TextInputPage::handleEvent(gui_event_t *event) { update_char(); return true; } - if (EVENT_RELEASED(event, Buttons.BUTTON1) || - EVENT_RELEASED(event, Buttons.BUTTON2)) { + + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + config_charpane(); + return true; + } + + if (EVENT_RELEASED(event, Buttons.BUTTON1)) { text_input_page.return_state = false; GUI.popPage(); } diff --git a/avr/cores/megacommand/MCL/TextInputPage.h b/avr/cores/megacommand/MCL/TextInputPage.h index 502d37e09..81092d6ba 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.h +++ b/avr/cores/megacommand/MCL/TextInputPage.h @@ -16,12 +16,18 @@ class TextInputPage : public LightPage { uint8_t max_length; bool return_state; uint16_t last_clock; + bool normal_mode; + uint8_t cursor_position; TextInputPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) {} virtual bool handleEvent(gui_event_t *event); + void config_normal(); + void config_charpane(); void display(); + void display_normal(); + void display_charpane(); void init(); void init_text(char *text_, char *title_, uint8_t len); void setup(); From d849970ad26791b618f3dfbddc8a47452cc91ded Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Oct 2019 12:43:07 +0800 Subject: [PATCH 034/469] advance cursor pos on charpane; refactoring DEBUG_DUMP; new project page uses text input page --- avr/cores/megacommand/Design/popup_menu.png | Bin 0 -> 242 bytes avr/cores/megacommand/MCL/DSP.cpp | 8 +- avr/cores/megacommand/MCL/ExtSeqTrack.cpp | 4 +- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 19 ++- avr/cores/megacommand/MCL/Grid.cpp | 6 +- avr/cores/megacommand/MCL/GridPage.cpp | 4 +- avr/cores/megacommand/MCL/NewProjectPage.cpp | 117 ++++-------------- avr/cores/megacommand/MCL/NewProjectPage.h | 5 +- avr/cores/megacommand/MCL/Project.cpp | 16 ++- avr/cores/megacommand/MCL/ProjectPages.cpp | 11 +- avr/cores/megacommand/MCL/TextInputPage.cpp | 22 ++-- avr/cores/megacommand/MD/MD.cpp | 3 +- avr/cores/megacommand/MD/MDMessages.cpp | 4 +- avr/cores/megacommand/MD/MDPattern.cpp | 2 +- build.ps1 | 80 +++++++----- 15 files changed, 131 insertions(+), 170 deletions(-) create mode 100644 avr/cores/megacommand/Design/popup_menu.png diff --git a/avr/cores/megacommand/Design/popup_menu.png b/avr/cores/megacommand/Design/popup_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..8af1f38ce909dd965bd5ad8dcde1cce6db89817f GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA5fDaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(eheoCO|{#S9FR*Fcz2;^L{dK*4rT7srqc=eL(E`3@-Xum--X&s_d9cFKv) z2Ej{vy1I@Y)>z2Ha9c28j!u3=+F!=!r@gQJ*i)^ylwC4-Hly0<8Mk;0B9C49rtT27 z%aGv=g8}ma9tLBA3^UdX-90PMzUP4`U9d`Tb6mm$Nwr1%KqoPHy85}Sb4q9e04Zus AmjD0& literal 0 HcmV?d00001 diff --git a/avr/cores/megacommand/MCL/DSP.cpp b/avr/cores/megacommand/MCL/DSP.cpp index fa8b325ef..3c1b279e7 100644 --- a/avr/cores/megacommand/MCL/DSP.cpp +++ b/avr/cores/megacommand/MCL/DSP.cpp @@ -13,9 +13,9 @@ float DSP::saturate(float sample, float max) { float skew = .07; float sat_percent = 20; - // DEBUG_PRINTLN(abs(sample)); - // DEBUG_PRINTLN(max); - // DEBUG_PRINTLN(percent_max); + // DEBUG_DUMP(abs(sample)); + // DEBUG_DUMP(max); + // DEBUG_DUMP(percent_max); float c = log((float)skew * (float)max) / sat_percent; if (abs(sample) < ((1.00 - (sat_percent / 100)) * max)) { @@ -24,7 +24,7 @@ float DSP::saturate(float sample, float max) { float y = -1 * exp(percent_max * .80) + max; - // DEBUG_PRINTLN(y); + // DEBUG_DUMP(y); if (sample < 0) { y = y * -1.00; } diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp index 3f681009c..7b3a99e27 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp @@ -6,7 +6,7 @@ void ExtSeqTrack::set_length(uint8_t len) { if (step_count >= length) { step_count = length % step_count; } - DEBUG_PRINTLN(step_count); + DEBUG_DUMP(step_count); /*uint8_t step_count = ((MidiClock.div32th_counter / resolution) - (mcl_actions.start_clock32th / resolution)) - @@ -90,7 +90,7 @@ void ExtSeqTrack::seq() { void ExtSeqTrack::note_on(uint8_t note) { uart->sendNoteOn(channel, note, 100); DEBUG_PRINTLN("note on"); - DEBUG_PRINTLN(note); + DEBUG_DUMP(note); // Greater than 64 if (IS_BIT_SET(note, 6)) { SET_BIT64(note_buffer[1], note - 64); diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index b8ccba928..8dcd8cd9d 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -40,7 +40,7 @@ void FileBrowserPage::init() { char up_one_dir[3] = ".."; SD.vwd()->getName(temp_entry, 16); - DEBUG_PRINTLN(temp_entry); + DEBUG_DUMP(temp_entry); if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { add_entry(&up_one_dir[0]); @@ -55,14 +55,14 @@ void FileBrowserPage::init() { } file.getName(temp_entry, 16); bool is_match_file = false; - DEBUG_PRINTLN(temp_entry); + DEBUG_DUMP(temp_entry); if (temp_entry[0] == '.') { is_match_file = false; } else if (file.isDirectory() && show_dirs) { is_match_file = true; } else { char *arg1 = &temp_entry[strlen(temp_entry) - 4]; - DEBUG_PRINTLN(arg1); + DEBUG_DUMP(arg1); if (strcmp(arg1, match) == 0) { is_match_file = true; } @@ -71,9 +71,8 @@ void FileBrowserPage::init() { DEBUG_PRINTLN("file matched"); add_entry(temp_entry); if (strcmp(temp_entry, mcl_cfg.project) == 0) { - DEBUG_PRINTLN("match"); - DEBUG_PRINTLN(temp_entry); - DEBUG_PRINTLN(mcl_cfg.project); + DEBUG_DUMP(temp_entry); + DEBUG_DUMP(mcl_cfg.project); cur_file = numEntries - 1; encoders[1]->cur = numEntries - 1; @@ -81,7 +80,7 @@ void FileBrowserPage::init() { } index++; file.close(); - DEBUG_PRINTLN(numEntries); + DEBUG_DUMP(numEntries); } if (numEntries <= 0) { @@ -222,7 +221,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { /* SD.vwd()->getName(temp_entry,16); - DEBUG_PRINTLN(temp_entry); + DEBUG_DUMP(temp_entry); file.openParent(SD.vwd()); file.getName(temp_entry,16); @@ -251,8 +250,8 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { char *slash = "/"; strcat(lwd, slash); } - DEBUG_PRINTLN(lwd); - DEBUG_PRINTLN(temp_entry); + DEBUG_DUMP(lwd); + DEBUG_DUMP(temp_entry); SD.chdir(temp_entry); init(); return true; diff --git a/avr/cores/megacommand/MCL/Grid.cpp b/avr/cores/megacommand/MCL/Grid.cpp index 34a0e2ead..9323a3168 100644 --- a/avr/cores/megacommand/MCL/Grid.cpp +++ b/avr/cores/megacommand/MCL/Grid.cpp @@ -175,12 +175,12 @@ bool Grid::clear_slot(int16_t column, int16_t row, bool update_header) { if (!ret) { DEBUG_PRINT_FN(); DEBUG_PRINTLN("Clear grid failed: "); - DEBUG_PRINTLN(row); - DEBUG_PRINTLN(column); + DEBUG_DUMP(row); + DEBUG_DUMP(column); return false; } // DEBUG_PRINTLN("Writing"); - // DEBUG_PRINTLN(sizeof(temptrack.active)); + // DEBUG_DUMP(sizeof(temptrack.active)); ret = mcl_sd.write_data((uint8_t *)&(temp_track), sizeof(temp_track), &proj.file); diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index c4f088a43..c8677f160 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -109,8 +109,8 @@ void GridPage::loop() { } if (clock_diff(grid_lastclock, slowclock) > GUI_NAME_TIMEOUT) { - /// DEBUG_PRINTLN(grid_lastclock); - // DEBUG_PRINTLN(slowclock); + /// DEBUG_DUMP(grid_lastclock); + // DEBUG_DUMP(slowclock); // display_name = 1; if ((write_cfg) && (MidiClock.state != 2)) { mcl_cfg.cur_col = cur_col; diff --git a/avr/cores/megacommand/MCL/NewProjectPage.cpp b/avr/cores/megacommand/MCL/NewProjectPage.cpp index ca1e9b731..93ab5dd4c 100644 --- a/avr/cores/megacommand/MCL/NewProjectPage.cpp +++ b/avr/cores/megacommand/MCL/NewProjectPage.cpp @@ -1,118 +1,55 @@ -#include "MCL.h" #include "NewProjectPage.h" +#include "MCL.h" void NewProjectPage::setup() {} void NewProjectPage::init() { -#ifdef OLED_DISPLAY - oled_display.setFont(); - oled_display.clearDisplay(); -#endif - last_clock = slowclock; - DEBUG_PRINTLN("New project page"); - char my_string[16] = "/project___.mcl"; - my_string[8] = (mcl_cfg.number_projects % 1000) / 100 + '0'; - my_string[8 + 1] = (mcl_cfg.number_projects % 100) / 10 + '0'; - my_string[8 + 2] = (mcl_cfg.number_projects % 10) + '0'; - m_strncpy(newprj, my_string, 16); - curpage = NEW_PROJECT_PAGE; - encoders[0]->cur = 10; - update_prjpage_char(); -} - -void NewProjectPage::update_prjpage_char() { - uint8_t x = 0; - char allowedchar[38] = "0123456789abcdefghijklmnopqrstuvwxyz_"; - // Check to see that the character chosen is in the list of allowed characters - while ((newprj[encoders[0]->cur] != allowedchar[x]) && (x < 38)) { - - x++; - } + DEBUG_PRINTLN("New project page"); + char my_string[sizeof(newprj)] = "project___"; - // Ensure the encoder does not go out of bounds, by resetting it to a - // character within the allowed characters list - encoders[1]->setValue(x); - // Update the projectname. - encoders[0]->old = encoders[0]->cur; + my_string[7] = (mcl_cfg.number_projects % 1000) / 100 + '0'; + my_string[7 + 1] = (mcl_cfg.number_projects % 100) / 10 + '0'; + my_string[7 + 2] = (mcl_cfg.number_projects % 10) + '0'; + m_strncpy(newprj, my_string, sizeof(newprj)); } void NewProjectPage::display() { -#ifdef OLED_DISPLAY - oled_display.clearDisplay(); -#endif - - char allowedchar[38] = "0123456789abcdefghijklmnopqrstuvwxyz_"; - // Check to see that the character chosen is in the list of allowed characters - if (encoders[0]->hasChanged()) { + if (mcl_gui.wait_for_input(newprj, "New Project:", sizeof(newprj))) { - update_prjpage_char(); - } - if (encoders[1]->hasChanged()) { - last_clock = slowclock; - } - // if ((encoders[2]->hasChanged())){ - newprj[encoders[0]->getValue()] = allowedchar[encoders[1]->getValue()]; - // } - - GUI.setLine(GUI.LINE1); - GUI.put_string_at(0, "New Project:"); - GUI.setLine(GUI.LINE2); - char tmp_str[18]; - m_strncpy(tmp_str, newprj, 18); - if (clock_diff(last_clock, slowclock) > FLASH_SPEED) { - tmp_str[encoders[0]->getValue()] = ' '; - } - if (clock_diff(last_clock, slowclock) > FLASH_SPEED * 2) { - last_clock = slowclock; - } - GUI.put_string_at(0, &tmp_str[1]); -} -bool NewProjectPage::handleEvent(gui_event_t *event) { - if (note_interface.is_event(event)) { - - return true; - } - - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { + char full_path[sizeof(newprj) + 5] = {'\0'}; + strcat(full_path, "/"); + strcat(full_path, newprj); + strcat(full_path, ".mcl"); gfx.alert("PLEASE WAIT", "CREATING PROJECT"); - DEBUG_PRINTLN(newprj); - if (SD.exists(newprj)) { + DEBUG_PRINTLN(full_path); + if (SD.exists(full_path)) { gfx.alert("PROJECT EXISTS", ""); DEBUG_PRINTLN("Project exists"); - return true; + return; } - bool ret = proj.new_project(newprj); + bool ret = proj.new_project(full_path); if (ret) { - if (proj.load_project(newprj)) { - + if (proj.load_project(full_path)) { grid_page.reload_slot_models = false; GUI.setPage(&grid_page); - return true; + return; } else { gfx.alert("SD FAILURE", "--"); - return false; - // LCD.goLine(0); - // LCD.puts("SD Failure"); + return; } } - - return true; - } - if (EVENT_PRESSED(event, Buttons.BUTTON1) || - EVENT_RELEASED(event, Buttons.BUTTON2) || - EVENT_RELEASED(event, Buttons.BUTTON3) || - EVENT_PRESSED(event, Buttons.BUTTON4)) { - if (proj.project_loaded) { - GUI.setPage(&grid_page); - return true; - } + } else if (proj.project_loaded) { + GUI.setPage(&grid_page); } +} + + +bool NewProjectPage::handleEvent(gui_event_t *event) +{ + // don't handle any events return false; } diff --git a/avr/cores/megacommand/MCL/NewProjectPage.h b/avr/cores/megacommand/MCL/NewProjectPage.h index fd16acbba..8aa133aec 100644 --- a/avr/cores/megacommand/MCL/NewProjectPage.h +++ b/avr/cores/megacommand/MCL/NewProjectPage.h @@ -9,17 +9,16 @@ class NewProjectPage : public LightPage { public: - char newprj[18]; + char newprj[14]; uint16_t last_clock; NewProjectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) {} - virtual bool handleEvent(gui_event_t *event); virtual void display(); + virtual bool handleEvent(gui_event_t *event); void init(); void setup(); - void update_prjpage_char(); }; #endif /* NEWPROJECTPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/Project.cpp b/avr/cores/megacommand/MCL/Project.cpp index cb022129e..98352770e 100644 --- a/avr/cores/megacommand/MCL/Project.cpp +++ b/avr/cores/megacommand/MCL/Project.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "Project.h" +#include "MCL.h" void Project::setup() {} @@ -140,17 +140,15 @@ bool Project::new_project(char *projectname) { for (int32_t i = 0; i < GRID_LENGTH; i++) { #ifdef OLED_DISPLAY - if (i % 16 == 0) { - oled_display.fillRect(15, 23, ((float)i / (float)GRID_LENGTH) * 98, 6, - WHITE); - oled_display.display(); + if (i % 16 == 0) { + mcl_gui.draw_progress("Initializing project", i, GRID_LENGTH); } #endif - if (i % 2 == 0) { + if (i % 2 == 0) { if (ledstatus == 0) { setLed2(); ledstatus = 1; - } else { + } else { clearLed2(); ledstatus = 0; } @@ -158,8 +156,8 @@ bool Project::new_project(char *projectname) { ret = grid.clear_row(i); if (!ret) { - DEBUG_PRINTLN("coud not clear row"); - return false; + DEBUG_PRINTLN("coud not clear row"); + return false; } } clearLed2(); diff --git a/avr/cores/megacommand/MCL/ProjectPages.cpp b/avr/cores/megacommand/MCL/ProjectPages.cpp index 21c6b7f77..16c12cf76 100644 --- a/avr/cores/megacommand/MCL/ProjectPages.cpp +++ b/avr/cores/megacommand/MCL/ProjectPages.cpp @@ -1,13 +1,12 @@ #include "ProjectPages.h" #include "MCL.h" -extern MCLEncoder loadproj_param1(0, 64, ENCODER_RES_SYS); +MCLEncoder loadproj_param1(0, 64, ENCODER_RES_SYS); -extern MCLEncoder newproj_param1(1, 10, ENCODER_RES_SYS); -extern MCLEncoder newproj_param2(0, 36, ENCODER_RES_SYS); +MCLEncoder newproj_param1(1, 10, ENCODER_RES_SYS); +MCLEncoder newproj_param2(0, 36, ENCODER_RES_SYS); -extern NewProjectPage new_proj_page(&newproj_param1, &newproj_param2); - -extern LoadProjectPage load_proj_page(&loadproj_param1,&loadproj_param1); +NewProjectPage new_proj_page(&newproj_param1, &newproj_param2); +LoadProjectPage load_proj_page(&loadproj_param1,&loadproj_param1); diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 4f908e592..c9b1ef8b6 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -133,7 +133,7 @@ void TextInputPage::config_charpane() { } void TextInputPage::display_normal() { - constexpr auto s_text_x = MCLGUI::s_menu_x + 16; + constexpr auto s_text_x = MCLGUI::s_menu_x + 8; constexpr auto s_text_y = MCLGUI::s_menu_y + 12; // update cursor position @@ -227,6 +227,12 @@ bool TextInputPage::handleEvent(gui_event_t *event) { if (!normal_mode) { if (EVENT_RELEASED(event, Buttons.BUTTON2)) { oled_display.clearDisplay(); + // before exiting charpane, advance current cursor to the next. + ++cursor_position; + if(cursor_position >= length) { + cursor_position = length - 1; + } + // then, config normal input line config_normal(); return true; } @@ -237,14 +243,14 @@ bool TextInputPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { - text_input_page.return_state = true; - uint8_t cpy_len = text_input_page.length; - for (uint8_t n = text_input_page.length - 1; - n > 0 && text_input_page.text[n] == ' '; n--) { + return_state = true; + uint8_t cpy_len = length; + for (uint8_t n = length - 1; + n > 0 && text[n] == ' '; n--) { cpy_len -= 1; } - m_strncpy(text_input_page.textp, &(text_input_page.text[0]), cpy_len); - text_input_page.textp[cpy_len] = '\0'; + m_strncpy(textp, text, cpy_len); + textp[cpy_len] = '\0'; GUI.popPage(); return true; } @@ -276,7 +282,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON1)) { - text_input_page.return_state = false; + return_state = false; GUI.popPage(); } return false; diff --git a/avr/cores/megacommand/MD/MD.cpp b/avr/cores/megacommand/MD/MD.cpp index 2a24112e1..939443a9e 100644 --- a/avr/cores/megacommand/MD/MD.cpp +++ b/avr/cores/megacommand/MD/MD.cpp @@ -844,8 +844,7 @@ void MDClass::enter_global_edit() { if (global == 255) { return; } - DEBUG_PRINTLN("global"); - DEBUG_PRINTLN(global); + DEBUG_DUMP(global); clear_all_windows_quick(); delay(10); toggle_global_window(); diff --git a/avr/cores/megacommand/MD/MDMessages.cpp b/avr/cores/megacommand/MD/MDMessages.cpp index a6c27d2ed..674587267 100644 --- a/avr/cores/megacommand/MD/MDMessages.cpp +++ b/avr/cores/megacommand/MD/MDMessages.cpp @@ -296,7 +296,7 @@ bool MDKit::fromSysex(MidiClass *midi) { uint16_t offset = 5; if (len != (0x4d1 - 7)) { DEBUG_PRINTLN("kit wrong length"); - DEBUG_PRINTLN(len); + DEBUG_DUMP(len); return false; } @@ -371,7 +371,7 @@ uint16_t MDKit::toSysex(ElektronDataToSysexEncoder &encoder) { //float swing = (float) MD.swing_last / 16385.0; //encoder.throttle_mod12 = floor((swing) * 12); //DEBUG_PRINTLN("swing"); - //DEBUG_PRINTLN(encoder.throttle_mod12); + //DEBUG_DUMP(encoder.throttle_mod12); } encoder.stop7Bit(); encoder.begin(); diff --git a/avr/cores/megacommand/MD/MDPattern.cpp b/avr/cores/megacommand/MD/MDPattern.cpp index 1c45426c6..7bda9ef76 100644 --- a/avr/cores/megacommand/MD/MDPattern.cpp +++ b/avr/cores/megacommand/MD/MDPattern.cpp @@ -190,7 +190,7 @@ bool MDPattern::fromSysex(MidiClass *midi) { #endif DEBUG_PRINTLN("WRONG LENGTH"); - DEBUG_PRINTLN(len); + DEBUG_DUMP(len); return false; } diff --git a/build.ps1 b/build.ps1 index 702bab38a..9411a9ee4 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,38 +1,62 @@ +param( + [Switch] + $ShowStats, + [Switch] + $Quiet, + [Switch] + $Upload +) + $pattern = "#pragma message:" Write-Host "============> Build started." -$buildOutput = &{ - arduino compile --warnings default -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ +$buildOutput = & { + arduino compile --warnings default -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ + $Script:compileStatus = $LASTEXITCODE } 2>&1 | ForEach-Object { - $content = $_.ToString() - if ($content.Contains("pragma message")) { - Write-Host $_ -ForegroundColor Green - } elseif ($content.Contains("overflow")){ - Write-Host $_ -ForegroundColor Red - } elseif ($content.Contains("invalid conversion")) { - Write-Host $_ -ForegroundColor DarkGray - }else{ - Write-Host $_ + $content = $_.ToString() + if ($content.Contains("pragma message")) { + if (-not $Quiet) { + Write-Host $_ -ForegroundColor Green + } + } elseif ($content.Contains("overflow") -or $content.Contains("error:")){ + Write-Host $_ -ForegroundColor Red + } elseif ($content.Contains("invalid conversion") -or $content.Contains("warning:") -or $content.Contains("note:")) { + if (-not $Quiet) { + Write-Host $_ -ForegroundColor DarkGray } - $_ + }else{ + Write-Host $_ + } + $_ } | Select-String $pattern -Write-Host "============> Build complete." - -$bank1 = @{} +if ($Script:compileStatus -eq 0) { + Write-Host "============> Build complete." -ForegroundColor Green +} else { + Write-Host "============> Build failed, exit code = $compileStatus." -ForegroundColor Red + return +} -$buildOutput | ForEach-Object { - $lines = $_.ToString() - $equation = $lines.Substring($lines.IndexOf($pattern) + $pattern.Length).Split([System.Environment]::NewLine)[0].Trim() - $equation = $equation.Replace("sizeof(MDTrackLight)", "501").Replace("sizeof(A4Track)", "1742") - $evaluate = $equation.Replace("sizeof(", '$($').Replace("UL", "").Insert(0, '$') - - $variable = $evaluate.Split("=")[0] - . Invoke-Expression $evaluate - $value = Invoke-Expression $variable - $bank1[$variable] = $value -} -ErrorAction SilentlyContinue +if ($ShowStats) { + $bank1 = @{} + $buildOutput | ForEach-Object { + $lines = $_.ToString() + $equation = $lines.Substring($lines.IndexOf($pattern) + $pattern.Length).Split([System.Environment]::NewLine)[0].Trim() + $equation = $equation.Replace("sizeof(MDTrackLight)", "501").Replace("sizeof(A4Track)", "1742") + $evaluate = $equation.Replace("sizeof(", '$($').Replace("UL", "").Insert(0, '$') + + $variable = $evaluate.Split("=")[0] + . Invoke-Expression $evaluate + $value = Invoke-Expression $variable + $bank1[$variable] = $value + } -ErrorAction SilentlyContinue + $bank1.GetEnumerator() | Sort-Object -Property Value | Format-Table +} -$bank1.GetEnumerator() | Sort-Object -Property Value | Format-Table -#arduino upload -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ -pCOM4 +if ($Upload) { + Write-Host "==============> Uploading..." -ForegroundColor Yellow + arduino upload -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ -pCOM4 + Write-Host "==============> Finished." -ForegroundColor Green +} From d710e126930a99b255eeb373cea4b473fbd67f76 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Oct 2019 14:30:45 +0800 Subject: [PATCH 035/469] move newprojectpage logic to handleEvent --- avr/cores/megacommand/MCL/GridPage.cpp | 10 +++--- avr/cores/megacommand/MCL/GridRowHeader.cpp | 2 +- avr/cores/megacommand/MCL/GridSavePage.cpp | 2 +- avr/cores/megacommand/MCL/GridTask.cpp | 38 ++++++++++---------- avr/cores/megacommand/MCL/GridWritePage.cpp | 2 +- avr/cores/megacommand/MCL/NewProjectPage.cpp | 17 ++++----- avr/cores/megacommand/Midi/MidiIDSysex.cpp | 4 +-- 7 files changed, 36 insertions(+), 39 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index c8677f160..932d4f29f 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -197,7 +197,7 @@ void GridPage::tick_frames() { } if (clock_diff(frames_startclock, current_clock) >= 250) { frames_fps = frames; - // DEBUG_PRINTLN((float)frames * (float)4); + // DEBUG_DUMP((float)frames * (float)4); // frames_fps = ((frames + frames_fps)/ 2); frames = 0; frames_startclock = slowclock; @@ -683,12 +683,12 @@ bool GridPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON3)) { show_slot_menu = true; - DEBUG_PRINTLN(getCol()); - DEBUG_PRINTLN(getRow()); + DEBUG_DUMP(getCol()); + DEBUG_DUMP(getRow()); slot.load_track_from_grid(getCol(), getRow()); DEBUG_PRINTLN("what's in the slot"); - DEBUG_PRINTLN(slot.chain.loops); - DEBUG_PRINTLN(slot.chain.row); + DEBUG_DUMP(slot.chain.loops); + DEBUG_DUMP(slot.chain.row); /* if (slot.active == EMPTY_TRACK_TYPE) { DEBUG_PRINTLN("empty track"); diff --git a/avr/cores/megacommand/MCL/GridRowHeader.cpp b/avr/cores/megacommand/MCL/GridRowHeader.cpp index dfd4ca3c3..f314cb923 100644 --- a/avr/cores/megacommand/MCL/GridRowHeader.cpp +++ b/avr/cores/megacommand/MCL/GridRowHeader.cpp @@ -45,7 +45,7 @@ bool GridRowHeader::is_empty() { if (track_type[x] == 0xFF) { count++; } - DEBUG_PRINTLN(track_type[x]); + DEBUG_DUMP(track_type[x]); } return (count == GRID_WIDTH - 1); } diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 23940aeb0..573c7089b 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -79,7 +79,7 @@ bool GridSavePage::handleEvent(gui_event_t *event) { md_exploit.off(); DEBUG_PRINTLN("notes"); - DEBUG_PRINTLN(note_interface.notes_all_off()); + DEBUG_DUMP(note_interface.notes_all_off()); for (int i = 0; i < 20; i++) { diff --git a/avr/cores/megacommand/MCL/GridTask.cpp b/avr/cores/megacommand/MCL/GridTask.cpp index 2b6a7858f..bfbd4c45f 100644 --- a/avr/cores/megacommand/MCL/GridTask.cpp +++ b/avr/cores/megacommand/MCL/GridTask.cpp @@ -46,8 +46,8 @@ void GridTask::run() { (uint32_t)mcl_actions.next_transition * 2)) { DEBUG_PRINTLN("Preparing for next transition:"); - DEBUG_PRINTLN(MidiClock.div16th_counter); - DEBUG_PRINTLN(mcl_actions.next_transition); + DEBUG_DUMP(MidiClock.div16th_counter); + DEBUG_DUMP(mcl_actions.next_transition); div32th_counter = MidiClock.div32th_counter + div32th_margin; } else { @@ -99,11 +99,11 @@ void GridTask::run() { #ifdef EXT_TRACKS if (send_ext_slots) { DEBUG_PRINTLN("waiting to send a4"); - DEBUG_PRINTLN(MidiClock.div192th_counter); - DEBUG_PRINTLN(mcl_actions.a4_latency); - DEBUG_PRINTLN(mcl_actions.a4_div192th_latency); - DEBUG_PRINTLN(mcl_actions.next_transition * 12 - - mcl_actions.a4_div192th_latency); + DEBUG_DUMP(MidiClock.div192th_counter); + DEBUG_DUMP(mcl_actions.a4_latency); + DEBUG_DUMP(mcl_actions.a4_div192th_latency); + DEBUG_DUMP(mcl_actions.next_transition * 12 - + mcl_actions.a4_div192th_latency); uint32_t go_step = mcl_actions.next_transition * 12 - mcl_actions.md_div192th_latency - @@ -124,7 +124,7 @@ void GridTask::run() { for (uint8_t n = NUM_MD_TRACKS; n < NUM_TRACKS; n++) { if (slots_changed[n] >= 0) { a4_track->load_from_mem(n); - DEBUG_PRINTLN(mcl_actions.a4_latency); + DEBUG_DUMP(mcl_actions.a4_latency); if (a4_track->active == A4_TRACK_TYPE) { if ((mcl_actions.a4_latency > 0) && @@ -154,8 +154,8 @@ void GridTask::run() { } #endif if (send_md_slots) { - DEBUG_PRINTLN(MidiClock.div192th_counter); - DEBUG_PRINTLN(mcl_actions.next_transition * 12 - + DEBUG_DUMP(MidiClock.div192th_counter); + DEBUG_DUMP(mcl_actions.next_transition * 12 - mcl_actions.md_div192th_latency); uint32_t go_step = mcl_actions.next_transition * 12 - mcl_actions.md_div192th_latency; @@ -171,7 +171,7 @@ void GridTask::run() { } } DEBUG_PRINTLN("div16"); - DEBUG_PRINTLN(MidiClock.div16th_counter); + DEBUG_DUMP(MidiClock.div16th_counter); // in_sysex = 1; uint32_t div192th_counter_old = MidiClock.div192th_counter; for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { @@ -195,12 +195,12 @@ void GridTask::run() { break; case TRANSITION_UNMUTE: DEBUG_PRINTLN("unmuting"); - DEBUG_PRINT(trigGroup); + DEBUG_DUMP(trigGroup); MD.muteTrack(trigGroup, false); break; case TRANSITION_MUTE: DEBUG_PRINTLN("muting"); - DEBUG_PRINT(trigGroup); + DEBUG_DUMP(trigGroup); MD.muteTrack(trigGroup, true); break; } @@ -222,12 +222,12 @@ void GridTask::run() { break; case TRANSITION_UNMUTE: DEBUG_PRINTLN("unmuting"); - DEBUG_PRINT(n); + DEBUG_DUMP(n); MD.muteTrack(n, false); break; case TRANSITION_MUTE: DEBUG_PRINTLN("muting"); - DEBUG_PRINT(n); + DEBUG_DUMP(n); MD.muteTrack(n, true); break; } @@ -247,7 +247,7 @@ void GridTask::run() { else { //&& (mcl_cfg.chain_mode != 2)) { DEBUG_PRINTLN("clearing track"); - DEBUG_PRINTLN(n); + DEBUG_DUMP(n); bool clear_locks = true; bool reset_params = false; mcl_seq.md_tracks[n].clear_track(clear_locks, reset_params); @@ -280,7 +280,7 @@ void GridTask::run() { count++; if (n < NUM_MD_TRACKS) { // DEBUG_PRINTLN("trying to cache MD track"); - // DEBUG_PRINTLN(n); + // DEBUG_DUMP(n); // DEBUG_PRINTLN(mcl_actions.chains[n].row); int32_t len = sizeof(GridTrack) + sizeof(MDSeqTrackData) + sizeof(MDMachine); @@ -321,8 +321,8 @@ void GridTask::run() { #ifdef EXT_TRACKS else { DEBUG_PRINTLN("trying to load a4 track"); - DEBUG_PRINTLN(n); - DEBUG_PRINTLN(mcl_actions.chains[n].row); + DEBUG_DUMP(n); + DEBUG_DUMP(mcl_actions.chains[n].row); if (a4_track->load_track_from_grid(n, mcl_actions.chains[n].row, 0)) { a4_temp_track->load_from_mem(n); diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index e14e64100..6607ecf67 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -87,7 +87,7 @@ bool GridWritePage::handleEvent(gui_event_t *event) { if (GridIOPage::handleEvent(event)) { return true; } - DEBUG_PRINTLN(event->source); + DEBUG_DUMP(event->source); if (note_interface.is_event(event)) { DEBUG_PRINTLN("note event"); if (note_interface.notes_all_off()) { diff --git a/avr/cores/megacommand/MCL/NewProjectPage.cpp b/avr/cores/megacommand/MCL/NewProjectPage.cpp index 93ab5dd4c..3d183b038 100644 --- a/avr/cores/megacommand/MCL/NewProjectPage.cpp +++ b/avr/cores/megacommand/MCL/NewProjectPage.cpp @@ -15,6 +15,10 @@ void NewProjectPage::init() { } void NewProjectPage::display() { +} + +bool NewProjectPage::handleEvent(gui_event_t *event) { + // don't handle any events if (mcl_gui.wait_for_input(newprj, "New Project:", sizeof(newprj))) { char full_path[sizeof(newprj) + 5] = {'\0'}; @@ -28,7 +32,7 @@ void NewProjectPage::display() { if (SD.exists(full_path)) { gfx.alert("PROJECT EXISTS", ""); DEBUG_PRINTLN("Project exists"); - return; + return false; } bool ret = proj.new_project(full_path); @@ -36,20 +40,13 @@ void NewProjectPage::display() { if (proj.load_project(full_path)) { grid_page.reload_slot_models = false; GUI.setPage(&grid_page); - return; } else { gfx.alert("SD FAILURE", "--"); - return; } + return false; } } else if (proj.project_loaded) { GUI.setPage(&grid_page); + return true; } } - - -bool NewProjectPage::handleEvent(gui_event_t *event) -{ - // don't handle any events - return false; -} diff --git a/avr/cores/megacommand/Midi/MidiIDSysex.cpp b/avr/cores/megacommand/Midi/MidiIDSysex.cpp index f34dd3c27..0e391cc61 100644 --- a/avr/cores/megacommand/Midi/MidiIDSysex.cpp +++ b/avr/cores/megacommand/Midi/MidiIDSysex.cpp @@ -26,7 +26,7 @@ void MidiIDSysexListenerClass::end_immediate() { DEBUG_PRINT_FN(); MidiID *dev = &(sysex->uart->device); uint16_t p = (uint16_t) dev; - DEBUG_PRINTLN(p); + DEBUG_DUMP(p); uint8_t i = 2; if ((sysex->getByte(i++) == 0x06) && @@ -51,7 +51,7 @@ void MidiIDSysexListenerClass::end_immediate() { msgType = sysex->getByte(2); isIDMessage = true; DEBUG_PRINTLN("fam code"); - DEBUG_PRINTLN(dev->family_code[0]); + DEBUG_DUMP(dev->family_code[0]); return; } From 51d9d11e66be86500f2735633dff691e083bbcbc Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Oct 2019 19:33:47 +0800 Subject: [PATCH 036/469] refactor FileBrowserPage derivatives to use base implementation --- avr/cores/megacommand/Design/infobox.png | Bin 0 -> 403 bytes avr/cores/megacommand/MCL/FileBrowserPage.cpp | 141 ++++++++++-------- avr/cores/megacommand/MCL/FileBrowserPage.h | 20 ++- avr/cores/megacommand/MCL/LoadProjectPage.cpp | 71 +-------- avr/cores/megacommand/MCL/LoadProjectPage.h | 2 +- avr/cores/megacommand/MCL/MCLGUI.cpp | 43 +++++- avr/cores/megacommand/MCL/MCLGUI.h | 7 +- avr/cores/megacommand/MCL/MCLGfx.cpp | 10 +- avr/cores/megacommand/MCL/Project.cpp | 6 +- avr/cores/megacommand/MCL/Project.h | 4 +- avr/cores/megacommand/MCL/SDDrivePage.cpp | 99 +----------- avr/cores/megacommand/MCL/SDDrivePage.h | 3 +- .../megacommand/MCL/SoundBrowserPage.cpp | 110 +++----------- avr/cores/megacommand/MCL/SoundBrowserPage.h | 4 +- avr/cores/megacommand/MCL/TextInputPage.cpp | 2 +- avr/cores/megacommand/MCL/TextInputPage.h | 4 +- 16 files changed, 187 insertions(+), 339 deletions(-) create mode 100644 avr/cores/megacommand/Design/infobox.png diff --git a/avr/cores/megacommand/Design/infobox.png b/avr/cores/megacommand/Design/infobox.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d4e54d1c7a8ccfee2877862c1c1d7d25605ef9 GIT binary patch literal 403 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW&!DaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(eheoCO|{#S9FR*Fcz2;^L{dKn)t6E{-7;ac^%0@--Ot@Qp5@?Sx-@mWuCc!2iooQTCA;1J{yr_ZCfq_;YbAr~6WwK*x6WC*pUj@vUnsM^ zc;zGAgn8-Pb-Z`DPispoc)iPu>yYwwQT7AX@_B*2J1?J*V~}BH=woCk_vR1yAvWdZ zjrxu33v_rGEG9E}thAETTxa*cQ)gXc7DHX_k1LKR_qE-LkLZclWq8in$E&8wvSjs5 zIflxo^E2bPr!vGu-dw{}Y8uUWf{mHsoDM_KtH^h^7Yj8+To(E*rf}oU)vIsB4rFb- zyhkjH(PI6Z8)65p#?Q}U{Sp2xSj}EcN=5AP^YaH~t=p8#`DVQZ8v=CL@%^p#E?eaq tcoXjD-DHwzsuRs;ykc(Fcwx?O2A#vf@%L^AhynwW!PC{xWt~$(69CGrngRd- literal 0 HcmV?d00001 diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 8dcd8cd9d..2967333a2 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -14,7 +14,7 @@ void FileBrowserPage::setup() { void FileBrowserPage::add_entry(char *entry) { uint32_t pos = BANK1_FILE_ENTRIES_START + numEntries * 16; - volatile uint8_t *ptr = (uint8_t*)pos; + volatile uint8_t *ptr = (uint8_t *)pos; memcpy_bank1(ptr, entry, 16); numEntries++; } @@ -43,7 +43,7 @@ void FileBrowserPage::init() { DEBUG_DUMP(temp_entry); if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { - add_entry(&up_one_dir[0]); + add_entry(up_one_dir); } encoders[1]->cur = 1; @@ -125,7 +125,7 @@ void FileBrowserPage::display() { char temp_entry[16]; uint16_t entry_num = encoders[1]->cur - cur_row + n; uint32_t pos = BANK1_FILE_ENTRIES_START + entry_num * 16; - volatile uint8_t *ptr = (uint8_t*)pos; + volatile uint8_t *ptr = (uint8_t *)pos; memcpy_bank1(temp_entry, ptr, 16); oled_display.println(temp_entry); } @@ -156,7 +156,8 @@ void FileBrowserPage::display() { void FileBrowserPage::draw_scrollbar(uint8_t x_offset) { #ifdef OLED_DISPLAY - mcl_gui.draw_vertical_scrollbar(x_offset, numEntries, MAX_VISIBLE_ROWS, encoders[1]->cur - cur_row); + mcl_gui.draw_vertical_scrollbar(x_offset, numEntries, MAX_VISIBLE_ROWS, + encoders[1]->cur - cur_row); #endif } @@ -193,93 +194,111 @@ bool FileBrowserPage::create_folder() { } return true; } + +void FileBrowserPage::_calcindices(int &saveidx, int &newfolderidx) { + saveidx = show_save ? 0 : -1; + newfolderidx = show_new_folder ? (saveidx + 1) : -1; +} + +void FileBrowserPage::_cd_up() { + char dir_entry[16]; + file.close(); + SD.chdir(lwd); + + SD.vwd()->getName(dir_entry, 16); + auto len_lwd = strlen(lwd); + auto len_dir_entry = strlen(dir_entry); + + // trim ending '/' + if (lwd[len_lwd - 1] == '/') { + lwd[--len_lwd] = '\0'; + } + if (dir_entry[len_dir_entry - 1] == '/') { + dir_entry[--len_dir_entry] = '\0'; + } + + lwd[len_lwd - len_dir_entry] = '\0'; + DEBUG_DUMP(dir_entry); + DEBUG_DUMP(lwd); + + init(); +} + +void FileBrowserPage::_cd(const char *child) { + char dir_entry[16]; + file.close(); + SD.vwd()->getName(dir_entry, 16); + strcat(lwd, dir_entry); + if (dir_entry[strlen(dir_entry) - 1] != '/') { + strcat(lwd, "/"); + } + DEBUG_DUMP(lwd); + DEBUG_DUMP(child); + SD.chdir(child); + init(); +} + bool FileBrowserPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { - - return true; + return false; } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { - if (encoders[0]->getValue() == 0) { + int i_save, i_newfolder; + _calcindices(i_save, i_newfolder); + + if (encoders[1]->getValue() == i_save) { + on_new(); return true; } - if (encoders[1]->getValue() == 1) { + if (encoders[1]->getValue() == i_newfolder) { create_folder(); - return true; + return false; } char temp_entry[16]; - char dir_entry[16]; uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = (uint8_t*)pos; - memcpy_bank1(temp_entry, ptr, 16); - char *up_one_dir = ".."; - if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { - /* - SD.vwd()->getName(temp_entry,16); - DEBUG_DUMP(temp_entry); - - file.openParent(SD.vwd()); - file.getName(temp_entry,16); + volatile uint8_t *ptr = (uint8_t *)pos; + memcpy_bank1(&temp_entry[0], ptr, 16); - // SD.chdir(temp_entry); - */ - file.close(); - SD.chdir(lwd); - - SD.vwd()->getName(dir_entry, 16); - lwd[strlen(lwd) - strlen(dir_entry) - 1] = '\0'; - DEBUG_DUMP(lwd); - - init(); - return true; - SD.vwd()->getName(temp_entry, 16); - DEBUG_DUMP(temp_entry); - return true; + // chdir to parent + if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { + _cd_up(); + return false; } + file.open(temp_entry, O_READ); + + // chdir to child if (file.isDirectory()) { - file.close(); - SD.vwd()->getName(dir_entry, 16); - strcat(lwd, dir_entry); - if (dir_entry[strlen(dir_entry) - 1] != '/') { - char *slash = "/"; - strcat(lwd, slash); - } - DEBUG_DUMP(lwd); - DEBUG_DUMP(temp_entry); - SD.chdir(temp_entry); - init(); - return true; + _cd(temp_entry); + return false; } - GUI.popPage(); - /* - #ifdef OLED_DISPLAY - oled_display.clearDisplay(); - oled_display.setFont(&TomThumb); - oled_display.setCursor(0, 8); - oled_display.setTextColor(WHITE, BLACK); - oled_display.println("PROJECT NOT COMPATIBLE"); - oled_display.display(); - delay(700); - #else - GUI.flash_strings_fill("PROJECT ERROR", "NOT COMPATIBLE"); - #endif - */ + // select an entry + on_select(temp_entry); return true; } + if (EVENT_RELEASED(event, Buttons.BUTTON2)) { + // TODO shift menu + // TODO delete + // TODO rename + // TODO copy/paste } + + // cancel if (EVENT_PRESSED(event, Buttons.BUTTON1) || EVENT_RELEASED(event, Buttons.BUTTON3) || EVENT_PRESSED(event, Buttons.BUTTON4)) { - GUI.setPage(&grid_page); + on_cancel(); return true; } + return false; } diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 50bfac645..af23a48e8 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -4,8 +4,8 @@ #define FILEBROWSERPAGE_H__ #include "GUI.h" -#include "SdFat.h" #include "MCLEncoder.h" +#include "SdFat.h" #include "SeqPage.h" #define MAX_ENTRIES 1024 @@ -21,8 +21,8 @@ #define MAX_FB_ITEMS 4 class FileBrowserPage : public LightPage { - public: -// char file_entries[NUM_FILE_ENTRIES][16]; +public: + // char file_entries[NUM_FILE_ENTRIES][16]; int numEntries; char match[5]; @@ -48,7 +48,19 @@ class FileBrowserPage : public LightPage { virtual void loop(); virtual void setup(); virtual void init(); -}; + virtual void on_new() {} + virtual void on_select(char *) {} + virtual void on_delete(char *) {} + virtual void on_rename(char *from, char *to) {} + // on cancel, the page will be popped, + // and there's a last chance to clean up. + virtual void on_cancel() { GUI.popPage(); } + +private: + void _calcindices(int &, int &); + void _cd_up(); + void _cd(const char *); +}; #endif /* FILEBROWSERPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/LoadProjectPage.cpp b/avr/cores/megacommand/MCL/LoadProjectPage.cpp index f1cfd58d1..ccdf50cd9 100644 --- a/avr/cores/megacommand/MCL/LoadProjectPage.cpp +++ b/avr/cores/megacommand/MCL/LoadProjectPage.cpp @@ -13,69 +13,14 @@ void LoadProjectPage::init() { strcpy(title, files); FileBrowserPage::init(); } -bool LoadProjectPage::handleEvent(gui_event_t *event) { - if (note_interface.is_event(event)) { - return true; - } - if (EVENT_RELEASED(event, Buttons.BUTTON1) || - EVENT_RELEASED(event, Buttons.BUTTON2) || - EVENT_RELEASED(event, Buttons.BUTTON3) || - EVENT_RELEASED(event, Buttons.BUTTON4)) { - GUI.popPage(); - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { - - if (encoders[1]->getValue() == 1) { - create_folder(); - return; - } - - char temp_entry[16]; - char dir_entry[16]; - uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = pos; - memcpy_bank1(&temp_entry[0], ptr, 16); - char *up_one_dir = ".."; - - if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { - file.close(); - SD.chdir(lwd); - - SD.vwd()->getName(dir_entry, 16); - lwd[strlen(lwd) - strlen(dir_entry) - 1] = '\0'; - DEBUG_PRINTLN(lwd); - - init(); - return; - } - - file.open(temp_entry, O_READ); - - if (file.isDirectory()) { - file.close(); - SD.vwd()->getName(dir_entry, 16); - strcat(lwd, dir_entry); - if (dir_entry[strlen(dir_entry) - 1] != '/') { - char *slash = "/"; - strcat(lwd, slash); - } - DEBUG_PRINTLN(lwd); - DEBUG_PRINTLN(temp_entry); - SD.chdir(temp_entry); - init(); - return; - } - file.close(); - if (proj.load_project(temp_entry)) { - GUI.setPage(&grid_page); - } else { - gfx.alert("PROJECT ERROR", "NOT COMPATIBLE"); - } - return true; +void LoadProjectPage::on_select(const char* entry) +{ + file.close(); + if (proj.load_project(entry)) { + GUI.setPage(&grid_page); + } else { + gfx.alert("PROJECT ERROR", "NOT COMPATIBLE"); } } + diff --git a/avr/cores/megacommand/MCL/LoadProjectPage.h b/avr/cores/megacommand/MCL/LoadProjectPage.h index 30dc2866f..2c724face 100644 --- a/avr/cores/megacommand/MCL/LoadProjectPage.h +++ b/avr/cores/megacommand/MCL/LoadProjectPage.h @@ -21,7 +21,7 @@ class LoadProjectPage : public FileBrowserPage { LoadProjectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : FileBrowserPage(e1, e2, e3, e4) {} - virtual bool handleEvent(gui_event_t *event); + virtual void on_select(const char* entry); void init(); }; diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 88ff2e6fd..8145fa4bc 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -1,6 +1,6 @@ #include "MCL.h" -bool MCLGUI::wait_for_input(char *dst, char *title, uint8_t len) { +bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { text_input_page.init(); text_input_page.init_text(dst, title, len); GUI.pushPage(&text_input_page); @@ -37,7 +37,8 @@ void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, oled_display.drawRect(x, y, 5, length, WHITE); } -void MCLGUI::draw_popup(char *title, bool deferred_display) { +// ref: Design/popup_menu.png +void MCLGUI::draw_popup(const char *title, bool deferred_display) { oled_display.setFont(&TomThumb); // draw menu body @@ -75,7 +76,7 @@ static constexpr uint8_t s_progress_h = 5; static uint8_t s_progress_cookie = 0; -void MCLGUI::draw_progress(char *msg, uint8_t cur, uint8_t _max, +void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, bool deferred_display) { draw_popup(msg, true); @@ -102,3 +103,39 @@ void MCLGUI::draw_progress(char *msg, uint8_t cur, uint8_t _max, } } +// ref: Design/infobox.png +void MCLGUI::draw_infobox(const char* line1, const char* line2) +{ + constexpr auto info_y1 = 2; + constexpr auto info_y2 = 27; + constexpr auto info_x1 = 12; + constexpr auto info_x2 = 124; + constexpr auto circle_x = info_x1 + 10; + constexpr auto circle_y = info_y1 + 15; + + constexpr auto info_w = info_x2 - info_x1 + 1; + constexpr auto info_h = info_y2 - info_y1 + 1; + + auto oldfont = oled_display.getFont(); + + oled_display.fillRect(info_x1 - 1, info_y1 - 1, info_w + 3, info_h + 3, BLACK); + oled_display.drawRect(info_x1, info_y1, info_w, info_h, WHITE); + oled_display.drawFastHLine(info_x1 + 1, info_y2 + 1, info_w, WHITE); + oled_display.drawFastVLine(info_x2 + 1, info_y1 + 1, info_h - 1, WHITE); + oled_display.fillRect(info_x1 + 1, info_y1 + 1, info_w - 2, 6, WHITE); + + oled_display.fillCircle(circle_x, circle_y, 6, WHITE); + oled_display.fillRect(circle_x - 1, circle_y - 3, 2, 4, BLACK); + oled_display.fillRect(circle_x - 1, circle_y + 2, 2, 2, BLACK); + + oled_display.setFont(&TomThumb); + oled_display.setTextColor(BLACK); + oled_display.setCursor(info_x1 + 4, info_y1 + 6); + oled_display.println(line1); + + oled_display.setTextColor(WHITE); + oled_display.setCursor(info_x1 + 23, info_y1 + 17); + oled_display.println(line2); + + oled_display.setFont(oldfont); +} diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index b3e9205d2..4b0be9532 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -7,14 +7,15 @@ class MCLGUI { public: - bool wait_for_input(char *dst, char *title, uint8_t len); + bool wait_for_input(char *dst, const char *title, uint8_t len); + void draw_infobox(const char* line1, const char* line2); void draw_vertical_dashline(uint8_t x); void draw_vertical_separator(uint8_t x); void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); /// Clear the content area of a popup void clear_popup(); - void draw_popup(char* title, bool deferred_display = false); - void draw_progress(char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); + void draw_popup(const char* title, bool deferred_display = false); + void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); static constexpr uint8_t s_menu_w = 96; static constexpr uint8_t s_menu_h = 24; diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index 9296b095c..5f3a25302 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -89,17 +89,9 @@ void MCLGfx::splashscreen() { void MCLGfx::alert(char *str1, char *str2) { #ifdef OLED_DISPLAY - GFXfont *old_font = oled_display.getFont(); - oled_display.clearDisplay(); - oled_display.setFont(&TomThumb); - oled_display.setCursor(0, 8); - oled_display.setTextColor(WHITE, BLACK); - oled_display.println(str1); - oled_display.setCursor(0, 18); - oled_display.println(str2); + mcl_gui.draw_infobox(str1, str2); oled_display.display(); delay(700); - oled_display.setFont(old_font); #else GUI.flash_strings_fill(str1, str2); GUI.display(); diff --git a/avr/cores/megacommand/MCL/Project.cpp b/avr/cores/megacommand/MCL/Project.cpp index 98352770e..27d9062fd 100644 --- a/avr/cores/megacommand/MCL/Project.cpp +++ b/avr/cores/megacommand/MCL/Project.cpp @@ -3,7 +3,7 @@ void Project::setup() {} -bool Project::load_project(char *projectname) { +bool Project::load_project(const char *projectname) { bool ret; @@ -40,7 +40,6 @@ bool Project::load_project(char *projectname) { bool Project::check_project_version() { bool ret; - int b = 0; DEBUG_PRINT_FN(); DEBUG_PRINTLN("Check project version"); @@ -68,7 +67,6 @@ bool Project::check_project_version() { bool Project::write_header() { bool ret; - int b; DEBUG_PRINT_FN(); DEBUG_PRINTLN("Writing project header"); @@ -97,7 +95,7 @@ bool Project::write_header() { return true; } -bool Project::new_project(char *projectname) { +bool Project::new_project(const char *projectname) { bool ret; diff --git a/avr/cores/megacommand/MCL/Project.h b/avr/cores/megacommand/MCL/Project.h index 41bc07f21..d51e0a0e3 100644 --- a/avr/cores/megacommand/MCL/Project.h +++ b/avr/cores/megacommand/MCL/Project.h @@ -19,9 +19,9 @@ class Project : public ProjectHeader { File file; bool project_loaded = false; void setup(); - bool load_project(char *projectname); + bool load_project(const char *projectname); bool check_project_version(); - bool new_project(char *projectname); + bool new_project(const char *projectname); bool write_header(); }; diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 9557002b8..e24c480cc 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -129,7 +129,6 @@ void SDDrivePage::load_snapshot() { MD.kit.toSysex(); } // Load complete - load_complete: file.close(); gfx.alert("Loaded", "Snapshot"); return; @@ -140,101 +139,13 @@ void SDDrivePage::load_snapshot() { } } -bool SDDrivePage::handleEvent(gui_event_t *event) { - if (note_interface.is_event(event)) { - - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { - - if (encoders[1]->getValue() == 0) { - save_snapshot(); - init(); - return false; - } - - if (encoders[1]->getValue() == 1) { - create_folder(); - return false; - } - - char temp_entry[16]; - char dir_entry[16]; - uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = (uint8_t *)pos; - memcpy_bank1(&temp_entry[0], ptr, 16); - - if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { - file.close(); - SD.chdir(lwd); - - SD.vwd()->getName(dir_entry, 16); - auto len_lwd = strlen(lwd); - auto len_dir_entry = strlen(dir_entry); - - // trim ending '/' - if (lwd[len_lwd - 1] == '/') { - lwd[--len_lwd] = '\0'; - } - if (dir_entry[len_dir_entry - 1] == '/') { - dir_entry[--len_dir_entry] = '\0'; - } - - lwd[len_lwd - len_dir_entry] = '\0'; - DEBUG_DUMP(dir_entry); - DEBUG_DUMP(lwd); - - init(); - return false; - } - - file.open(temp_entry, O_READ); - - if (file.isDirectory()) { - file.close(); - SD.vwd()->getName(dir_entry, 16); - strcat(lwd, dir_entry); - if (dir_entry[strlen(dir_entry) - 1] != '/') { - strcat(lwd, "/"); - } - DEBUG_PRINTLN(lwd); - DEBUG_PRINTLN(temp_entry); - SD.chdir(temp_entry); - init(); - return false; - } - - load_snapshot(); - - return true; - } - - // if ((EVENT_RELEASED(event, Buttons.BUTTON1) && - // BUTTON_DOWN(Buttons.BUTTON3)) || - if (EVENT_RELEASED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON1)) { - char temp_entry[16]; - char dir_entry[16]; - uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = (uint8_t *)pos; - memcpy_bank1(&temp_entry[0], ptr, 16); - SD.remove(temp_entry); - init(); - return false; - } - - if (EVENT_RELEASED(event, Buttons.BUTTON2) || - EVENT_RELEASED(event, Buttons.BUTTON1) || - EVENT_RELEASED(event, Buttons.BUTTON4)) { - // EVENT_RELEASED(event, Buttons.BUTTON4)) { - GUI.setPage(&grid_page); - return true; - } - return false; +void SDDrivePage::on_new() { + save_snapshot(); + init(); } +void SDDrivePage::on_select(const char *__) { load_snapshot(); } + MCLEncoder sddrive_param1(1, 10, ENCODER_RES_SYS); MCLEncoder sddrive_param2(0, 36, ENCODER_RES_SYS); SDDrivePage sddrive_page(&sddrive_param1, &sddrive_param2); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.h b/avr/cores/megacommand/MCL/SDDrivePage.h index 154f604bf..0c17bc008 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.h +++ b/avr/cores/megacommand/MCL/SDDrivePage.h @@ -12,11 +12,12 @@ class SDDrivePage : public FileBrowserPage { Encoder *e4 = NULL) : FileBrowserPage(e1, e2, e3, e4) { } - bool handleEvent(gui_event_t *event); void init(); void setup(); void save_snapshot(); void load_snapshot(); + virtual void on_select(const char*); + virtual void on_new(); }; extern SDDrivePage sddrive_page; diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp index 9bb4c7585..f608594cf 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp @@ -1,9 +1,13 @@ -#include "MCL.h" #include "SoundBrowserPage.h" +#include "MCL.h" #include "MDSound.h" +const char* c_sound_root = "/Sounds/MD"; + void SoundBrowserPage::setup() { - SD.mkdir("/Sounds/MD", true); + SD.mkdir(c_sound_root, true); + SD.chdir(c_sound_root); + strcpy(lwd, c_sound_root); FileBrowserPage::setup(); } void SoundBrowserPage::init() { @@ -29,7 +33,7 @@ void SoundBrowserPage::save_sound() { grid_page.prepare(); PGM_P tmp; tmp = getMachineNameShort(MD.kit.models[MD.currentTrack], 2); - memcpy(sound_name,MD.kit.name,4); + memcpy(sound_name, MD.kit.name, 4); m_strncpy_p(&sound_name[5], tmp, 3); if (mcl_gui.wait_for_input(sound_name, my_title, 8)) { @@ -59,107 +63,33 @@ void SoundBrowserPage::load_sound() { DEBUG_PRINTLN("loading sound"); DEBUG_PRINTLN(temp_entry); if (!sound.file.open(temp_entry, O_READ)) { - DEBUG_PRINTLN("error openning"); - gfx.alert("Error", "Opening"); - return; + DEBUG_PRINTLN("error openning"); + gfx.alert("Error", "Opening"); + return; } sound.read_sound(); if (sound.id != SOUND_ID) { sound.file.close(); gfx.alert("Error", "Not compatible"); - return; + return; } sound.load_sound(MD.currentTrack); - gfx.alert("Loaded","Sound"); + gfx.alert("Loaded", "Sound"); sound.file.close(); } - } -bool SoundBrowserPage::handleEvent(gui_event_t *event) { - if (note_interface.is_event(event)) { - - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { - - if (encoders[1]->getValue() == 0) { - save_sound(); - init(); - return; - } - - if (encoders[1]->getValue() == 1) { - create_folder(); - return; - } - - char temp_entry[16]; - char dir_entry[16]; - uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = pos; - memcpy_bank1(&temp_entry[0], ptr, 16); - char *up_one_dir = ".."; - - if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { - file.close(); - SD.chdir(lwd); - - SD.vwd()->getName(dir_entry, 16); - lwd[strlen(lwd) - strlen(dir_entry) - 1] = '\0'; - DEBUG_PRINTLN(lwd); - - init(); - return; - } - - file.open(temp_entry, O_READ); - - if (file.isDirectory()) { - file.close(); - SD.vwd()->getName(dir_entry, 16); - strcat(lwd, dir_entry); - if (dir_entry[strlen(dir_entry) - 1] != '/') { - char *slash = "/"; - strcat(lwd, slash); - } - DEBUG_PRINTLN(lwd); - DEBUG_PRINTLN(temp_entry); - SD.chdir(temp_entry); - init(); - return; - } - - load_sound(); - - return true; - } - - // if ((EVENT_RELEASED(event, Buttons.BUTTON1) && BUTTON_DOWN(Buttons.BUTTON3)) || - if (EVENT_RELEASED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON1)) { - char temp_entry[16]; - char dir_entry[16]; - uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = pos; - memcpy_bank1(&temp_entry[0], ptr, 16); - SD.remove(temp_entry); - init(); - return; - } +void SoundBrowserPage::on_new() { + save_sound(); + init(); +} - if (EVENT_RELEASED(event, Buttons.BUTTON2) || - EVENT_RELEASED(event, Buttons.BUTTON1) || - EVENT_RELEASED(event, Buttons.BUTTON4)) { - // EVENT_RELEASED(event, Buttons.BUTTON4)) { - GUI.setPage(&grid_page); - return true; - } - return false; +void SoundBrowserPage::on_cancel() { + GUI.setPage(&grid_page); } +void SoundBrowserPage::on_select(const char *__) { load_sound(); } + MCLEncoder soundbrowser_param1(1, 10, ENCODER_RES_SYS); MCLEncoder soundbrowser_param2(0, 36, ENCODER_RES_SYS); SoundBrowserPage sound_browser(&soundbrowser_param1, &soundbrowser_param2); diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.h b/avr/cores/megacommand/MCL/SoundBrowserPage.h index 8b0b473c5..a11cdaa35 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.h +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.h @@ -11,7 +11,9 @@ class SoundBrowserPage : public FileBrowserPage { SoundBrowserPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : FileBrowserPage(e1, e2, e3, e4) {} - virtual bool handleEvent(gui_event_t *event); + virtual void on_new(); + virtual void on_select(const char*); + virtual void on_cancel(); void add_entry(char *entry); void draw_scrollbar(uint8_t x_offset); void init(); diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index c9b1ef8b6..fcf6453c5 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -38,7 +38,7 @@ void TextInputPage::init() { #endif } -void TextInputPage::init_text(char *text_, char *title_, uint8_t len) { +void TextInputPage::init_text(char *text_, const char *title_, uint8_t len) { textp = text_; title = title_; length = len; diff --git a/avr/cores/megacommand/MCL/TextInputPage.h b/avr/cores/megacommand/MCL/TextInputPage.h index 81092d6ba..642726bc8 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.h +++ b/avr/cores/megacommand/MCL/TextInputPage.h @@ -10,7 +10,7 @@ class TextInputPage : public LightPage { public: char *textp; - char *title; + const char *title; char text[17]; uint8_t length; uint8_t max_length; @@ -29,7 +29,7 @@ class TextInputPage : public LightPage { void display_normal(); void display_charpane(); void init(); - void init_text(char *text_, char *title_, uint8_t len); + void init_text(char *text_, const char *title_, uint8_t len); void setup(); void update_char(); }; From beabbc10bb936d8e7461dd86419e542add420a62 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Oct 2019 20:42:15 +0800 Subject: [PATCH 037/469] LoadProjectPage should cd to / --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 3 ++- avr/cores/megacommand/MCL/FileBrowserPage.h | 1 + avr/cores/megacommand/MCL/LoadProjectPage.cpp | 9 +++++---- avr/cores/megacommand/MCL/MCLGfx.cpp | 2 +- avr/cores/megacommand/MCL/MCLGfx.h | 2 +- avr/cores/megacommand/MCL/SDDrivePage.cpp | 8 ++++---- avr/cores/megacommand/MCL/SoundBrowserPage.cpp | 9 +++------ 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 2967333a2..f4b7ec567 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -61,6 +61,7 @@ void FileBrowserPage::init() { } else if (file.isDirectory() && show_dirs) { is_match_file = true; } else { + // XXX only 3char suffix char *arg1 = &temp_entry[strlen(temp_entry) - 4]; DEBUG_DUMP(arg1); if (strcmp(arg1, match) == 0) { @@ -93,7 +94,7 @@ void FileBrowserPage::init() { void FileBrowserPage::display() { #ifdef OLED_DISPLAY - uint8_t x_offset = 43, y_offset = 8, width = MENU_WIDTH; + constexpr uint8_t x_offset = 43, y_offset = 8, width = MENU_WIDTH; oled_display.clearDisplay(); oled_display.setFont(&TomThumb); oled_display.setCursor(0, 8); diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index af23a48e8..e2b9a2f94 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -36,6 +36,7 @@ class FileBrowserPage : public LightPage { bool show_new_folder = true; char title[12]; File file; + const char* blank_entry = " "; FileBrowserPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) diff --git a/avr/cores/megacommand/MCL/LoadProjectPage.cpp b/avr/cores/megacommand/MCL/LoadProjectPage.cpp index ccdf50cd9..9e65f0d87 100644 --- a/avr/cores/megacommand/MCL/LoadProjectPage.cpp +++ b/avr/cores/megacommand/MCL/LoadProjectPage.cpp @@ -7,10 +7,11 @@ void LoadProjectPage::init() { show_save = false; show_dirs = false; show_new_folder = false; - char *mcl = ".mcl"; - strcpy(match, mcl); - char *files = "Project"; - strcpy(title, files); + strcpy(match, ".mcl"); + strcpy(title, "Project"); + strcpy(lwd, "/"); + SD.chdir("/"); + FileBrowserPage::init(); } diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index 5f3a25302..3605a7097 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -87,7 +87,7 @@ void MCLGfx::splashscreen() { // GUI.setPage(&grid_page); } -void MCLGfx::alert(char *str1, char *str2) { +void MCLGfx::alert(const char *str1, const char *str2) { #ifdef OLED_DISPLAY mcl_gui.draw_infobox(str1, str2); oled_display.display(); diff --git a/avr/cores/megacommand/MCL/MCLGfx.h b/avr/cores/megacommand/MCL/MCLGfx.h index 63c723548..aad3ca23e 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.h +++ b/avr/cores/megacommand/MCL/MCLGfx.h @@ -8,7 +8,7 @@ class MCLGfx { public: void splashscreen(); void init_oled(); - void alert(char *str1, char *str2); + void alert(const char *str1, const char *str2); }; extern MCLGfx gfx; diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index e24c480cc..e772f0a80 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -25,10 +25,10 @@ void SDDrivePage::init() { void SDDrivePage::save_snapshot() { DEBUG_PRINT_FN(); - MDSound sound; - char sound_name[] = "________"; + char entry_name[] = "________"; + strcpy(entry_name, blank_entry); - if (mcl_gui.wait_for_input(sound_name, "Snapshot Name", 8)) { + if (mcl_gui.wait_for_input(entry_name, "Snapshot Name", 8)) { if (file.isOpen()) { file.close(); } @@ -39,7 +39,7 @@ void SDDrivePage::save_snapshot() { grid_page.prepare(); char temp_entry[16]; - strcpy(temp_entry, sound_name); + strcpy(temp_entry, entry_name); strcat(temp_entry, c_snapshot_suffix); DEBUG_PRINTLN("creating new snapshot:"); DEBUG_PRINTLN(temp_entry); diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp index f608594cf..ffa995827 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp @@ -25,10 +25,8 @@ void SoundBrowserPage::init() { void SoundBrowserPage::save_sound() { DEBUG_PRINT_FN(); - char *snd = ".snd"; MDSound sound; - char *sound_name = "________"; - char *my_title = "Sound Name"; + char sound_name[] = "________"; grid_page.prepare(); PGM_P tmp; @@ -36,10 +34,10 @@ void SoundBrowserPage::save_sound() { memcpy(sound_name, MD.kit.name, 4); m_strncpy_p(&sound_name[5], tmp, 3); - if (mcl_gui.wait_for_input(sound_name, my_title, 8)) { + if (mcl_gui.wait_for_input(sound_name, "Sound Name", 8)) { char temp_entry[16]; strcpy(temp_entry, sound_name); - strcat(temp_entry, snd); + strcat(temp_entry, ".snd"); DEBUG_PRINTLN("creating new sound:"); DEBUG_PRINTLN(temp_entry); sound.file.open(temp_entry, O_RDWR | O_CREAT); @@ -53,7 +51,6 @@ void SoundBrowserPage::save_sound() { void SoundBrowserPage::load_sound() { DEBUG_PRINT_FN(); - char *snd = ".snd"; grid_page.prepare(); if (file.isOpen()) { char temp_entry[16]; From 902cd0dc9f5de657af277dfa5df578512ec1159f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Oct 2019 21:35:35 +0800 Subject: [PATCH 038/469] wrapup. optimize text input page perf --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 7 +++++-- avr/cores/megacommand/MCL/FileBrowserPage.h | 6 +++--- avr/cores/megacommand/MCL/LoadProjectPage.cpp | 6 +++--- avr/cores/megacommand/MCL/TextInputPage.cpp | 5 +++-- avr/cores/megacommand/WProgram.h | 3 ++- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index f4b7ec567..a1b72128c 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -182,9 +182,8 @@ void FileBrowserPage::loop() { } bool FileBrowserPage::create_folder() { - char *my_title = "Create Folder"; char new_dir[17] = "new_folder "; - if (mcl_gui.wait_for_input(new_dir, my_title, 8)) { + if (mcl_gui.wait_for_input(new_dir, "Create Folder", 8)) { for (uint8_t n = 0; n < strlen(new_dir); n++) { if (new_dir[n] == ' ') { new_dir[n] = '\0'; @@ -240,6 +239,9 @@ void FileBrowserPage::_cd(const char *child) { } bool FileBrowserPage::handleEvent(gui_event_t *event) { + + DEBUG_PRINT_FN(); + if (note_interface.is_event(event)) { return false; } @@ -273,6 +275,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { return false; } + DEBUG_DUMP(temp_entry); file.open(temp_entry, O_READ); // chdir to child diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index e2b9a2f94..b7c40cb7b 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -51,9 +51,9 @@ class FileBrowserPage : public LightPage { virtual void init(); virtual void on_new() {} - virtual void on_select(char *) {} - virtual void on_delete(char *) {} - virtual void on_rename(char *from, char *to) {} + virtual void on_select(const char *) {} + virtual void on_delete(const char *) {} + virtual void on_rename(const char *from, const char *to) {} // on cancel, the page will be popped, // and there's a last chance to clean up. virtual void on_cancel() { GUI.popPage(); } diff --git a/avr/cores/megacommand/MCL/LoadProjectPage.cpp b/avr/cores/megacommand/MCL/LoadProjectPage.cpp index 9e65f0d87..f8e8251e6 100644 --- a/avr/cores/megacommand/MCL/LoadProjectPage.cpp +++ b/avr/cores/megacommand/MCL/LoadProjectPage.cpp @@ -15,8 +15,9 @@ void LoadProjectPage::init() { FileBrowserPage::init(); } -void LoadProjectPage::on_select(const char* entry) -{ +void LoadProjectPage::on_select(const char *entry) { + DEBUG_PRINT_FN(); + DEBUG_DUMP(entry); file.close(); if (proj.load_project(entry)) { GUI.setPage(&grid_page); @@ -24,4 +25,3 @@ void LoadProjectPage::on_select(const char* entry) gfx.alert("PROJECT ERROR", "NOT COMPATIBLE"); } } - diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index fcf6453c5..41ab514d9 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -155,7 +155,8 @@ void TextInputPage::display_normal() { auto time = clock_diff(last_clock, slowclock); #ifdef OLED_DISPLAY - mcl_gui.clear_popup(); + //mcl_gui.clear_popup(); <-- E_TOOSLOW + oled_display.fillRect(s_text_x, s_text_y, 6 * length, 8, BLACK); oled_display.setFont(); oled_display.setCursor(s_text_x, s_text_y); oled_display.println(text); @@ -218,7 +219,6 @@ void TextInputPage::display() { bool TextInputPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { - return true; } @@ -264,6 +264,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { } return true; } + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { // Clear text for (uint8_t n = 1; n < length; n++) { diff --git a/avr/cores/megacommand/WProgram.h b/avr/cores/megacommand/WProgram.h index 1b159fbe3..885c19a11 100644 --- a/avr/cores/megacommand/WProgram.h +++ b/avr/cores/megacommand/WProgram.h @@ -27,7 +27,8 @@ #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #define DEBUG_DUMP(x) ({Serial.print(#x); Serial.print(" = "); Serial.println(x);}) -#define DEBUG_PRINT_FN(x) ({DEBUG_PRINT("func_call: "); DEBUG_PRINTLN(__FUNCTION__);}) +// __PRETTY_FUNCTION__ is a gcc extension +#define DEBUG_PRINT_FN(x) ({DEBUG_PRINT("func_call: "); DEBUG_PRINTLN(__PRETTY_FUNCTION__);}) #else #define DEBUG_INIT() From ab7dc0b688e3a00dd462770c595bded1b8e87147 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Oct 2019 23:02:50 +0800 Subject: [PATCH 039/469] uppercase titles. fix up glitches --- avr/cores/megacommand/CommonTools/helpers.c | 12 ++++++++++++ avr/cores/megacommand/CommonTools/helpers.h | 1 + avr/cores/megacommand/MCL/FileBrowserPage.h | 1 - avr/cores/megacommand/MCL/MCLGUI.cpp | 16 ++++++++++++---- avr/cores/megacommand/MCL/SDDrivePage.cpp | 3 +-- avr/cores/megacommand/MCL/SoundBrowserPage.cpp | 2 +- avr/cores/megacommand/MCL/TextInputPage.cpp | 9 +++++++-- 7 files changed, 34 insertions(+), 10 deletions(-) diff --git a/avr/cores/megacommand/CommonTools/helpers.c b/avr/cores/megacommand/CommonTools/helpers.c index 79681116c..9fccb0b7f 100644 --- a/avr/cores/megacommand/CommonTools/helpers.c +++ b/avr/cores/megacommand/CommonTools/helpers.c @@ -288,6 +288,18 @@ void m_strnappend(void *dst, const char *src, int len) { m_strncpy(ptr, src, len - i); } +/** Convert the string to UPPERCASE. **/ +void m_toupper(char* str) +{ + char chr; + while((chr = *str)) { + if(chr >= 'a' && chr <= 'z') { + *str = chr - 'a' + 'A'; + } + ++str; + } +} + /** @} **/ /** diff --git a/avr/cores/megacommand/CommonTools/helpers.h b/avr/cores/megacommand/CommonTools/helpers.h index d8ce273e7..16b93eb26 100644 --- a/avr/cores/megacommand/CommonTools/helpers.h +++ b/avr/cores/megacommand/CommonTools/helpers.h @@ -211,6 +211,7 @@ void m_str16cpy_p_fill(void *dst, PGM_P src); void m_str16cpy_p(void *dst, PGM_P src); void m_strnappend(void *dst, const char *src, int len); uint16_t m_strlen(const char *str); +void m_toupper(char* str); /** @} */ diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index b7c40cb7b..09d05b250 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -36,7 +36,6 @@ class FileBrowserPage : public LightPage { bool show_new_folder = true; char title[12]; File file; - const char* blank_entry = " "; FileBrowserPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 8145fa4bc..fad287453 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -37,8 +37,14 @@ void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, oled_display.drawRect(x, y, 5, length, WHITE); } +static char title_buf[16]; + // ref: Design/popup_menu.png void MCLGUI::draw_popup(const char *title, bool deferred_display) { + + strcpy(title_buf, title); + m_toupper(title_buf); + oled_display.setFont(&TomThumb); // draw menu body @@ -54,10 +60,10 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display) { oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 2, BLACK); oled_display.setTextColor(BLACK); - // auto len = strlen(msg) * 3; - // oled_display.setCursor(64 - (len / 2), s_menu_y); + //auto len = strlen(title_buf) * 5; + //oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); oled_display.setCursor(s_title_x + 2, s_menu_y + 3); - oled_display.println(title); + oled_display.println(title_buf); oled_display.setTextColor(WHITE); if (!deferred_display) { oled_display.display(); @@ -131,7 +137,9 @@ void MCLGUI::draw_infobox(const char* line1, const char* line2) oled_display.setFont(&TomThumb); oled_display.setTextColor(BLACK); oled_display.setCursor(info_x1 + 4, info_y1 + 6); - oled_display.println(line1); + strcpy(title_buf, line1); + m_toupper(title_buf); + oled_display.println(title_buf); oled_display.setTextColor(WHITE); oled_display.setCursor(info_x1 + 23, info_y1 + 17); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index e772f0a80..5984f84f0 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -25,8 +25,7 @@ void SDDrivePage::init() { void SDDrivePage::save_snapshot() { DEBUG_PRINT_FN(); - char entry_name[] = "________"; - strcpy(entry_name, blank_entry); + char entry_name[] = " "; if (mcl_gui.wait_for_input(entry_name, "Snapshot Name", 8)) { if (file.isOpen()) { diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp index ffa995827..a78cc4ba3 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp @@ -26,7 +26,7 @@ void SoundBrowserPage::save_sound() { DEBUG_PRINT_FN(); MDSound sound; - char sound_name[] = "________"; + char sound_name[] = " "; grid_page.prepare(); PGM_P tmp; diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 41ab514d9..1d8160d62 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -162,8 +162,13 @@ void TextInputPage::display_normal() { oled_display.println(text); if (time < FLASH_SPEED) { // the default font is 6x8 - oled_display.fillRect(s_text_x + 6 * cursor_position, s_text_y, 6, 8, - INVERT); + auto tx = s_text_x + 6 * cursor_position; + oled_display.fillRect(tx, s_text_y, 6, 8, + WHITE); + oled_display.setCursor(s_text_x + 6 * cursor_position, s_text_y); + oled_display.setTextColor(BLACK); + oled_display.print(text[cursor_position]); + oled_display.setTextColor(WHITE); } if (time > FLASH_SPEED * 2) { last_clock = slowclock; From 433be8ca0fb05113e398d6d6aab5345ff1dbed19 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 16 Oct 2019 13:29:34 +1100 Subject: [PATCH 040/469] RAMPage progress. 2 RAM pages are now accesible from PageSelect (triggers 14/15). RAM Page 1 will record + play UW sample R1 on track 15. RAM Page 2 will record + play UW sample R2 on track 16. The two tracks can be used to record in stereo by setting encoder[1] to "LINK". Save button initiates RAM RECORD. Write button initiates RAM Playback. Shift 2 button initiates RAM Playback with slice magic. Encoder 4 controls length in steps. Encoder 3 controls number of slices. Values greater than 1 will cause the sample to be split in to n slices. Encoder 2 selects 'slice magic" mode. - Reverse Playback - Random Slice order --- avr/cores/megacommand/MCL/AuxPages.h | 3 +- avr/cores/megacommand/MCL/MCLMenus.cpp | 1 + avr/cores/megacommand/MCL/MCLMenus.h | 14 +- avr/cores/megacommand/MCL/PageSelectPage.cpp | 14 +- avr/cores/megacommand/MCL/RAMPage.cpp | 132 +++++++++++-------- avr/cores/megacommand/MCL/RAMPage.h | 44 +++++-- 6 files changed, 137 insertions(+), 71 deletions(-) diff --git a/avr/cores/megacommand/MCL/AuxPages.h b/avr/cores/megacommand/MCL/AuxPages.h index a7b3df9f7..d395b2f29 100644 --- a/avr/cores/megacommand/MCL/AuxPages.h +++ b/avr/cores/megacommand/MCL/AuxPages.h @@ -18,5 +18,6 @@ extern MCLEncoder route_param2; extern MixerPage mixer_page; extern RoutePage route_page; -extern RAMPage ram_page; +extern RAMPage ram_page_a; +extern RAMPage ram_page_b; #endif /* AUXPAGES_H__ */ diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index f2a1c698f..4fc4f7b92 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -11,6 +11,7 @@ MCLEncoder config_param4(0, 17, ENCODER_RES_SYS); MCLEncoder config_param5(0, 17, ENCODER_RES_SYS); MCLEncoder config_param6(0, 17, ENCODER_RES_SYS); +MenuPage aux_config_page(&auxconfig_menu_layout, &config_param1, &config_param6); MenuPage system_page(&system_menu_layout, &options_param1, &options_param2); MenuPage midi_config_page(&midiconfig_menu_layout, &config_param1, &config_param3); diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 36f38148a..21e5b9dfd 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -28,6 +28,7 @@ extern MenuPage midi_config_page; extern MenuPage md_config_page; extern MenuPage mcl_config_page; extern MenuPage chain_config_page; +extern MenuPage aux_config_page; extern MCLEncoder input_encoder1; extern MCLEncoder input_encoder2; @@ -36,18 +37,29 @@ extern TextInputPage text_input_page; const menu_t system_menu_layout PROGMEM = { "GLOBAL", - 6, + 7, { {"LOAD PROJECT" ,0, 0, 0, (uint8_t *) NULL, (Page*) &load_proj_page, (void*)NULL, {}}, {"NEW PROJECT",0, 0, 0, (uint8_t *) NULL, (Page*) &new_proj_page, (void*)NULL, {}}, {"MIDI",0, 0, 0, (uint8_t *) NULL, (Page*) &midi_config_page, (void*)NULL, {}}, {"MACHINEDRUM", 0, 0, 0, (uint8_t *) NULL, (Page*) &md_config_page, (void*)NULL, {}}, {"CHAIN MODE", 0, 0, 0, (uint8_t *) NULL, (Page*) &chain_config_page, (void*)NULL, {}}, + {"AUX PAGES", 0, 0, 0, (uint8_t *) NULL, (Page*) &aux_config_page, (void*)NULL, {}}, {"SYSTEM", 0, 0, 0, (uint8_t *) NULL, (Page*) &mcl_config_page, (void*)NULL, {}}, }, (void*) NULL, }; +const menu_t auxconfig_menu_layout PROGMEM = { + "AUX PAGES", + 1, + { + {"RAM Page" ,0, 0, 0, (uint8_t *) NULL, (Page*) &load_proj_page, (void*)NULL, {}}, + }, + (void*) NULL, +}; + + const menu_t midiconfig_menu_layout PROGMEM = { "MIDI", 5, diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 3732e889f..e96d7e70c 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -3,7 +3,8 @@ #define MIX_PAGE 0 #define ROUTE_PAGE 1 -#define RAM_PAGE 4 +#define RAM_PAGE_A 14 +#define RAM_PAGE_B 15 #define WAVD_PAGE 8 #define SOUND 7 @@ -34,10 +35,15 @@ LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { strncpy(str, "ROUTE", 6); r_page = &route_page; break; - case RAM_PAGE: + case RAM_PAGE_A: if (str) - strncpy(str, "RAM", 4); - r_page = &ram_page; + strncpy(str, "RAM 1", 6); + r_page = &ram_page_a; + break; + case RAM_PAGE_B: + if (str) + strncpy(str, "RAM 2", 6); + r_page = &ram_page_b; break; #ifdef WAV_DESIGNER case WAVD_PAGE: diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index ac16e6fff..f44381314 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -5,6 +5,8 @@ #define STATE_QUEUE 1 #define STATE_RECORD 2 #define STATE_PLAY 3 +#define MONO 0 +#define LINK 1 void RAMPage::setup() { DEBUG_PRINT_FN(); } @@ -15,9 +17,11 @@ void RAMPage::init() { oled_display.clearDisplay(); oled_display.setFont(); #endif - encoders[0]->cur = 100; + encoders[0]->cur = MONO; md_exploit.off(); + if (page_id == 0) { setup_callbacks(); + } } void RAMPage::cleanup() { @@ -51,7 +55,9 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, md_track.machine.params[RAM_R_MBAL] = pan; md_track.machine.params[RAM_R_ILEV] = 0; md_track.machine.params[RAM_R_LEN] = encoders[3]->cur * 16; - if (md_track.machine.params[RAM_R_LEN] > 127) { md_track.machine.params[RAM_R_LEN] = 127; } + if (md_track.machine.params[RAM_R_LEN] > 127) { + md_track.machine.params[RAM_R_LEN] = 127; + } md_track.machine.params[RAM_R_RATE] = 127; md_track.machine.params[MODEL_AMD] = 0; md_track.machine.params[MODEL_AMF] = 0; @@ -74,12 +80,12 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, if (linked_track == 255) { md_track.machine.trigGroup = 255; md_track.seq_data.pattern_mask = 1; - md_track.seq_data.conditional[0] = 14; + // md_track.seq_data.conditional[0] = 14; } else if (track > linked_track) { md_track.machine.trigGroup = linked_track; md_track.seq_data.pattern_mask = 1; // oneshot - md_track.seq_data.conditional[0] = 14; + //md_track.seq_data.conditional[0] = 14; } else { md_track.machine.trigGroup = 255; md_track.seq_data.pattern_mask = 0; @@ -95,7 +101,14 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, grid_page.active_slots[track] = 0x7FFF; mcl_actions.chains[track].row = SLOT_RAM_RECORD; mcl_actions.chains[track].loops = 1; - uint16_t next_step = (MidiClock.div16th_counter / steps) * steps + steps; + + uint8_t m = mcl_seq.md_tracks[track].length; + + uint16_t next_step = + MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); + + transition_step = next_step; + record_len = (uint8_t) steps; /* if (MD.kit.models[track] == md_track.machine.model) { mcl_actions.send_machine[track] = 1; } else { @@ -316,9 +329,10 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, next_step = MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); grid_page.active_slots[track] = 0x7FFF; - //mcl_actions.transition_level[track] = TRANSITION_MUTE; + // mcl_actions.transition_level[track] = TRANSITION_MUTE; mcl_actions.next_transitions[track] = next_step; - + transition_step = next_step; + record_len = (uint8_t) steps; mcl_actions.calc_next_transition(); EmptyTrack empty_track; @@ -327,8 +341,13 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, void RAMPage::setup_ram_play_mono(uint8_t track) { magic = 0; - setup_ram_play(track, RAM_P1_MODEL, 64); + uint8_t model = RAM_P1_MODEL; + if (page_id == 0) { + model = RAM_P2_MODEL; + } + setup_ram_play(track, model, 64); } + void RAMPage::setup_ram_play_stereo(uint8_t track) { if (track == 15) { return; @@ -341,7 +360,11 @@ void RAMPage::setup_ram_play_stereo(uint8_t track) { void RAMPage::setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, uint8_t rate) { - setup_ram_rec(track, RAM_R1_MODEL, mlev, len, rate, 63); + uint8_t model = RAM_R1_MODEL; + if (page_id == 0) { + model = RAM_R2_MODEL; + } + setup_ram_rec(track, model, mlev, len, rate, 63); } void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, @@ -365,24 +388,16 @@ void RAMPage::display() { uint8_t x; // GUI.put_string_at(12,"RAM"); GUI.put_string_at(0, "RAM"); - uint8_t break_loop = 0; - for (uint8_t n = NUM_MD_TRACKS - 1; n > 0 && break_loop == 0; n--) { - if ((grid_page.active_slots[n] == SLOT_RAM_RECORD) && - (mcl_seq.md_tracks[n].step_count == 0)) { - if (rec_state == STATE_QUEUE) { - rec_state = STATE_RECORD; - } else if ((rec_state == STATE_RECORD) && - (mcl_seq.md_tracks[n].oneshot_mask != 0)) { - rec_state = STATE_NOSTATE; - } - break_loop = 1; - } else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && - (mcl_seq.md_tracks[n].step_count == 0)) { - rec_state = STATE_PLAY; - break_loop = 1; - } - // in_sysex = 0; + uint8_t n = 15 - page_id; + if (grid_page.active_slots[n] == SLOT_RAM_RECORD) { + if ((rec_state == STATE_QUEUE) && (MidiClock.div16th_counter == transition_step + record_len)) { + rec_state = STATE_RECORD; + } + //else if ((rec_state == STATE_RECORD) && (MidiClock.div16th_counter >= transition_step + record_len + record_len)) { + } else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (MidiClock.div16th_counter >= transition_step + record_len)) { + rec_state = STATE_PLAY; } + // in_sysex = 0; switch (rec_state) { case STATE_QUEUE: GUI.put_string_at(5, "[Queue]"); @@ -398,14 +413,16 @@ void RAMPage::display() { GUI.setLine(GUI.LINE2); if (encoders[0]->cur == 0) { - GUI.put_string_at(0, "MONO"); + GUI.put_string_at(0, "MON"); } else { - GUI.put_string_at(0, "STER"); + GUI.put_string_at(0, "LNK"); } - GUI.put_value_at(5, encoders[1]->cur); - GUI.put_value_at(9, 1 << encoders[2]->cur); - GUI.put_value_at(13, encoders[3]->cur); + GUI.put_value_at1(4, encoders[1]->cur); + GUI.put_string_at(6, "S:"); + GUI.put_value_at2(8, 1 << encoders[2]->cur); + GUI.put_string_at(11, "L:"); + GUI.put_value_at2(13, (encoders[3]->cur * 4)); /* GUI.put_value_at1(8,msb); GUI.put_string_at(9,"."); @@ -425,6 +442,7 @@ void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { // to all polyphonic tracks uint8_t param_true = 0; + if (encoders[0]->cur == MONO) { return; } MD.parseCC(channel, param, &track, &track_param); if (grid_page.active_slots[track] != SLOT_RAM_PLAY) { @@ -436,20 +454,18 @@ void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (n != track)) { if (track_param == MODEL_PAN) { if (n < track) { - MD.setTrackParam(n, track_param,127 - value); - //Pan law. - uint8_t lev = ((float)(value - 64) / (float)64) * (float)27 + 100; + MD.setTrackParam(n, track_param, 127 - value); + // Pan law. + uint8_t lev = ((float)(value - 64) / (float)64) * (float)27 + 100; MD.setTrackParam(track, MODEL_VOL, lev); MD.setTrackParam(n, MODEL_VOL, lev); - } - else { - MD.setTrackParam(n, track_param,63 + (64 - value)); + } else { + MD.setTrackParam(n, track_param, 63 + (64 - value)); uint8_t lev = ((float)(64 - value) / (float)64) * (float)27 + 100; MD.setTrackParam(track, MODEL_VOL, lev); MD.setTrackParam(n, MODEL_VOL, lev); } - } - else { + } else { MD.setTrackParam(n, track_param, value); } } @@ -496,16 +512,20 @@ bool RAMPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { if (encoders[0]->cur == 0) { - setup_ram_rec_mono(15, 64, 4 * encoders[3]->cur - 1, 128); + if (page_id == 0) { + setup_ram_rec_mono(15, 64, 4 * encoders[3]->cur - 1, 128); + } else { + setup_ram_rec_mono(14, 64, 4 * encoders[3]->cur - 1, 128); + } } else { - setup_ram_rec_stereo(14, 64, 4 * encoders[3]->cur - 1, 128); - } + setup_ram_rec_stereo(14, 64 - 16, 4 * encoders[3]->cur - 1, 128); + } } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { magic = 1; - if (encoders[0]->cur == 0) { - slice(15, 255); + if (encoders[0]->cur == MONO) { + slice(15 - page_id, 255); } else { slice(14, 15); slice(15, 14); @@ -514,9 +534,9 @@ bool RAMPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON4)) { magic = 0; - if (encoders[0]->cur == 0) { - if (!slice(15, 255)) { - setup_ram_play_mono(15); + if (encoders[0]->cur == MONO) { + if (!slice(15 - page_id, 255)) { + setup_ram_play_mono(15 - page_id); } } else { slice(14, 15); @@ -534,9 +554,17 @@ bool RAMPage::handleEvent(gui_event_t *event) { return false; } -MCLEncoder ram_param1(0, 1, 2); -MCLEncoder ram_param2(0, 255, 2); -MCLEncoder ram_param3(0, 5, 2); -MCLEncoder ram_param4(1, 8, 2); +MCLEncoder ram_a_param1(MONO, 1, 2); +MCLEncoder ram_a_param2(0, 255, 2); +MCLEncoder ram_a_param3(0, 5, 2); +MCLEncoder ram_a_param4(4, 8, 2); + +MCLEncoder ram_b_param1(MONO, 1, 2); +MCLEncoder ram_b_param2(0, 255, 2); +MCLEncoder ram_b_param3(0, 5, 2); +MCLEncoder ram_b_param4(4, 8, 2); -RAMPage ram_page(&ram_param1, &ram_param2, &ram_param3, &ram_param4); +RAMPage ram_page_a((uint8_t)1, &ram_a_param1, &ram_a_param2, &ram_a_param3, + &ram_a_param4); +RAMPage ram_page_b((uint8_t)0, &ram_a_param1, &ram_b_param2, &ram_b_param3, + &ram_b_param4); diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index 3fe8409d6..0bae06dce 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -3,32 +3,45 @@ #ifndef RAMPAGE_H__ #define RAMPAGE_H__ -#include "MCLEncoder.h" #include "GUI.h" +#include "MCLEncoder.h" #define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 #define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 class RAMPage : public LightPage, MidiCallback { public: - RAMPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, - Encoder *e4 = NULL) + RAMPage(uint8_t _page_id, Encoder *e1 = NULL, Encoder *e2 = NULL, + Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { - } + page_id = _page_id; + if (page_id == 0) { + track1 = 15; + track2 = 14; + } + } bool handleEvent(gui_event_t *event); bool midi_state = false; uint8_t magic; uint8_t rec_state; - + uint8_t track1; + uint8_t track2; + uint8_t page_id; + uint16_t transition_step; + uint8_t record_len; void display(); void setup(); void init(); void cleanup(); - void setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, uint8_t len, uint8_t rate, uint8_t pan, uint8_t linked_track = 255); - void setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, uint8_t rate); - void setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, uint8_t rate); - void setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, uint8_t linked_track = 255); + void setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, uint8_t len, + uint8_t rate, uint8_t pan, uint8_t linked_track = 255); + void setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, + uint8_t rate); + void setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, + uint8_t rate); + void setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, + uint8_t linked_track = 255); void setup_ram_play_mono(uint8_t track); void setup_ram_play_stereo(uint8_t track); @@ -43,9 +56,14 @@ class RAMPage : public LightPage, MidiCallback { void onControlChangeCallback_Midi(uint8_t *msg); }; -extern MCLEncoder ram_param1; -extern MCLEncoder ram_param2; -extern MCLEncoder ram_param3; -extern MCLEncoder ram_param4; +extern MCLEncoder ram_a_param1; +extern MCLEncoder ram_a_param2; +extern MCLEncoder ram_a_param3; +extern MCLEncoder ram_a_param4; + +extern MCLEncoder ram_b_param1; +extern MCLEncoder ram_b_param2; +extern MCLEncoder ram_b_param3; +extern MCLEncoder ram_b_param4; #endif /* RAMPAGE_H__ */ From d88d642f1c27f3804dc4b94950c93c38ab444a5d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 16 Oct 2019 14:42:11 +1100 Subject: [PATCH 041/469] Fix page_ids, encoder range, tidy up --- avr/cores/megacommand/MCL/RAMPage.cpp | 60 +++++++++++++-------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index f44381314..481db19d1 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -8,7 +8,11 @@ #define MONO 0 #define LINK 1 -void RAMPage::setup() { DEBUG_PRINT_FN(); } +void RAMPage::setup() { + DEBUG_PRINT_FN(); + encoders[0]->cur = MONO; + encoders[3]->cur = 4; +} void RAMPage::init() { DEBUG_PRINT_FN(); @@ -189,7 +193,7 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { mcl_seq.md_tracks[track].locks[0][n] = 128; } break; - case 6: + case 5: mcl_seq.md_tracks[track].locks[0][n] = sample_inc * (slices - s) + 1; mcl_seq.md_tracks[track].locks[1][n] = @@ -199,7 +203,7 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { } break; - case 7: + case 6: uint8_t t; t = random(0, slices); @@ -214,7 +218,6 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { case 3: case 2: case 1: - case 5: // Revers every 2nd. // uint8_t m = mode; @@ -226,12 +229,6 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { } else { m = s + 1; } - } else if (m == 5) { - if (IS_BIT_SET64(mcl_seq.md_tracks[0].pattern_mask, n)) { - m = s; - } else { - m = m + 1; - } } else { @@ -342,7 +339,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, void RAMPage::setup_ram_play_mono(uint8_t track) { magic = 0; uint8_t model = RAM_P1_MODEL; - if (page_id == 0) { + if (page_id == 1) { model = RAM_P2_MODEL; } setup_ram_play(track, model, 64); @@ -361,7 +358,7 @@ void RAMPage::setup_ram_play_stereo(uint8_t track) { void RAMPage::setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, uint8_t rate) { uint8_t model = RAM_R1_MODEL; - if (page_id == 0) { + if (page_id == 1) { model = RAM_R2_MODEL; } setup_ram_rec(track, model, mlev, len, rate, 63); @@ -388,7 +385,8 @@ void RAMPage::display() { uint8_t x; // GUI.put_string_at(12,"RAM"); GUI.put_string_at(0, "RAM"); - uint8_t n = 15 - page_id; + GUI.put_value_at1(4, page_id + 1); + uint8_t n = 14 + page_id; if (grid_page.active_slots[n] == SLOT_RAM_RECORD) { if ((rec_state == STATE_QUEUE) && (MidiClock.div16th_counter == transition_step + record_len)) { rec_state = STATE_RECORD; @@ -400,13 +398,13 @@ void RAMPage::display() { // in_sysex = 0; switch (rec_state) { case STATE_QUEUE: - GUI.put_string_at(5, "[Queue]"); + GUI.put_string_at(6, "[Queue]"); break; case STATE_RECORD: - GUI.put_string_at(5, "[Recording]"); + GUI.put_string_at(6, "[Record]"); break; case STATE_PLAY: - GUI.put_string_at(5, "[Playback]"); + GUI.put_string_at(6, "[Play]"); break; } @@ -513,9 +511,9 @@ bool RAMPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON1)) { if (encoders[0]->cur == 0) { if (page_id == 0) { - setup_ram_rec_mono(15, 64, 4 * encoders[3]->cur - 1, 128); - } else { setup_ram_rec_mono(14, 64, 4 * encoders[3]->cur - 1, 128); + } else { + setup_ram_rec_mono(15, 64, 4 * encoders[3]->cur - 1, 128); } } else { setup_ram_rec_stereo(14, 64 - 16, 4 * encoders[3]->cur - 1, 128); @@ -525,7 +523,7 @@ bool RAMPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON3)) { magic = 1; if (encoders[0]->cur == MONO) { - slice(15 - page_id, 255); + slice(14 + page_id, 255); } else { slice(14, 15); slice(15, 14); @@ -535,8 +533,8 @@ bool RAMPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON4)) { magic = 0; if (encoders[0]->cur == MONO) { - if (!slice(15 - page_id, 255)) { - setup_ram_play_mono(15 - page_id); + if (!slice(14 + page_id, 255)) { + setup_ram_play_mono(14 + page_id); } } else { slice(14, 15); @@ -554,17 +552,17 @@ bool RAMPage::handleEvent(gui_event_t *event) { return false; } -MCLEncoder ram_a_param1(MONO, 1, 2); -MCLEncoder ram_a_param2(0, 255, 2); -MCLEncoder ram_a_param3(0, 5, 2); -MCLEncoder ram_a_param4(4, 8, 2); +MCLEncoder ram_a_param1(0, 1); +MCLEncoder ram_a_param2(0, 6); +MCLEncoder ram_a_param3(0, 5); +MCLEncoder ram_a_param4(1, 8); -MCLEncoder ram_b_param1(MONO, 1, 2); -MCLEncoder ram_b_param2(0, 255, 2); -MCLEncoder ram_b_param3(0, 5, 2); -MCLEncoder ram_b_param4(4, 8, 2); +MCLEncoder ram_b_param1(0, 1); +MCLEncoder ram_b_param2(0, 6); +MCLEncoder ram_b_param3(0, 5); +MCLEncoder ram_b_param4(1, 8); -RAMPage ram_page_a((uint8_t)1, &ram_a_param1, &ram_a_param2, &ram_a_param3, +RAMPage ram_page_a((uint8_t)0, &ram_a_param1, &ram_a_param2, &ram_a_param3, &ram_a_param4); -RAMPage ram_page_b((uint8_t)0, &ram_a_param1, &ram_b_param2, &ram_b_param3, +RAMPage ram_page_b((uint8_t)1, &ram_a_param1, &ram_b_param2, &ram_b_param3, &ram_b_param4); From d34a574355dde867e529607464a7fb7dc081ee1a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 16 Oct 2019 20:13:46 +1100 Subject: [PATCH 042/469] Added oled display mode for RAMPage. Includes spinning tape reel. Also added the ability to flip bitmaps along vertical and horizontal axis --- .../Adafruit-GFX-Library/Adafruit_GFX.cpp | 1901 +++++++++-------- .../Adafruit-GFX-Library/Adafruit_GFX.h | 4 +- avr/cores/megacommand/MCL/RAMPage.cpp | 135 +- avr/cores/megacommand/MCL/RAMPage.h | 29 + 4 files changed, 1124 insertions(+), 945 deletions(-) diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp index 53c7ad35c..bbff41ddc 100755 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp @@ -34,9 +34,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "Adafruit_GFX.h" #include "glcdfont.c" #ifdef __AVR__ - #include +#include #elif defined(ESP8266) || defined(ESP32) - #include +#include #endif // Many (but maybe not all) non-AVR board installs define macros @@ -44,498 +44,555 @@ POSSIBILITY OF SUCH DAMAGE. // Do our own checks and defines here for good measure... #ifndef pgm_read_byte - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) #endif #ifndef pgm_read_word - #define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#define pgm_read_word(addr) (*(const unsigned short *)(addr)) #endif #ifndef pgm_read_dword - #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) +#define pgm_read_dword(addr) (*(const unsigned long *)(addr)) #endif // Pointers are a peculiar case...typically 16-bit on AVR boards, // 32 bits elsewhere. Try to accommodate both... #if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF) - #define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) +#define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) #else - #define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) +#define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) #endif #ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define min(a, b) (((a) < (b)) ? (a) : (b)) #endif #ifndef _swap_int16_t -#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } +#define _swap_int16_t(a, b) \ + { \ + int16_t t = a; \ + a = b; \ + b = t; \ + } #endif -Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h): -WIDTH(w), HEIGHT(h) -{ - _width = WIDTH; - _height = HEIGHT; - rotation = 0; - cursor_y = cursor_x = 0; - textsize = 1; - textcolor = textbgcolor = 0xFFFF; - wrap = true; - _cp437 = false; - gfxFont = NULL; +Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h) : WIDTH(w), HEIGHT(h) { + _width = WIDTH; + _height = HEIGHT; + rotation = 0; + cursor_y = cursor_x = 0; + textsize = 1; + textcolor = textbgcolor = 0xFFFF; + wrap = true; + _cp437 = false; + gfxFont = NULL; } // Bresenham's algorithm - thx wikpedia void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, - uint16_t color) { - int16_t steep = abs(y1 - y0) > abs(x1 - x0); - if (steep) { - _swap_int16_t(x0, y0); - _swap_int16_t(x1, y1); - } + uint16_t color) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } - if (x0 > x1) { - _swap_int16_t(x0, x1); - _swap_int16_t(y0, y1); - } + if (x0 > x1) { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); - int16_t dx, dy; - dx = x1 - x0; - dy = abs(y1 - y0); + int16_t err = dx / 2; + int16_t ystep; - int16_t err = dx / 2; - int16_t ystep; + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } - if (y0 < y1) { - ystep = 1; + for (; x0 <= x1; x0++) { + if (steep) { + writePixel(y0, x0, color); } else { - ystep = -1; + writePixel(x0, y0, color); } - - for (; x0<=x1; x0++) { - if (steep) { - writePixel(y0, x0, color); - } else { - writePixel(x0, y0, color); - } - err -= dy; - if (err < 0) { - y0 += ystep; - err += dx; - } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; } + } } -void Adafruit_GFX::startWrite(){ - // Overwrite in subclasses if desired! +void Adafruit_GFX::startWrite() { + // Overwrite in subclasses if desired! } -void Adafruit_GFX::writePixel(int16_t x, int16_t y, uint16_t color){ - // Overwrite in subclasses if startWrite is defined! - drawPixel(x, y, color); +void Adafruit_GFX::writePixel(int16_t x, int16_t y, uint16_t color) { + // Overwrite in subclasses if startWrite is defined! + drawPixel(x, y, color); } // (x,y) is topmost point; if unsure, calling function // should sort endpoints or call writeLine() instead -void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y, - int16_t h, uint16_t color) { - // Overwrite in subclasses if startWrite is defined! - // Can be just writeLine(x, y, x, y+h-1, color); - // or writeFillRect(x, y, 1, h, color); - drawFastVLine(x, y, h, color); +void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + // Overwrite in subclasses if startWrite is defined! + // Can be just writeLine(x, y, x, y+h-1, color); + // or writeFillRect(x, y, 1, h, color); + drawFastVLine(x, y, h, color); } // (x,y) is leftmost point; if unsure, calling function // should sort endpoints or call writeLine() instead -void Adafruit_GFX::writeFastHLine(int16_t x, int16_t y, - int16_t w, uint16_t color) { - // Overwrite in subclasses if startWrite is defined! - // Example: writeLine(x, y, x+w-1, y, color); - // or writeFillRect(x, y, w, 1, color); - drawFastHLine(x, y, w, color); +void Adafruit_GFX::writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + // Overwrite in subclasses if startWrite is defined! + // Example: writeLine(x, y, x+w-1, y, color); + // or writeFillRect(x, y, w, 1, color); + drawFastHLine(x, y, w, color); } void Adafruit_GFX::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, - uint16_t color) { - // Overwrite in subclasses if desired! - fillRect(x,y,w,h,color); + uint16_t color) { + // Overwrite in subclasses if desired! + fillRect(x, y, w, h, color); } -void Adafruit_GFX::endWrite(){ - // Overwrite in subclasses if startWrite is defined! +void Adafruit_GFX::endWrite() { + // Overwrite in subclasses if startWrite is defined! } // (x,y) is topmost point; if unsure, calling function // should sort endpoints or call drawLine() instead -void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y, - int16_t h, uint16_t color) { - // Update in subclasses if desired! - startWrite(); - writeLine(x, y, x, y+h-1, color); - endWrite(); +void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + // Update in subclasses if desired! + startWrite(); + writeLine(x, y, x, y + h - 1, color); + endWrite(); } // (x,y) is leftmost point; if unsure, calling function // should sort endpoints or call drawLine() instead -void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y, - int16_t w, uint16_t color) { - // Update in subclasses if desired! - startWrite(); - writeLine(x, y, x+w-1, y, color); - endWrite(); +void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + // Update in subclasses if desired! + startWrite(); + writeLine(x, y, x + w - 1, y, color); + endWrite(); } void Adafruit_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, - uint16_t color) { - // Update in subclasses if desired! - startWrite(); - for (int16_t i=x; i y1) _swap_int16_t(y0, y1); - drawFastVLine(x0, y0, y1 - y0 + 1, color); - } else if(y0 == y1){ - if(x0 > x1) _swap_int16_t(x0, x1); - drawFastHLine(x0, y0, x1 - x0 + 1, color); - } else { - startWrite(); - writeLine(x0, y0, x1, y1, color); - endWrite(); - } + uint16_t color) { + // Update in subclasses if desired! + if (x0 == x1) { + if (y0 > y1) + _swap_int16_t(y0, y1); + drawFastVLine(x0, y0, y1 - y0 + 1, color); + } else if (y0 == y1) { + if (x0 > x1) + _swap_int16_t(x0, x1); + drawFastHLine(x0, y0, x1 - x0 + 1, color); + } else { + startWrite(); + writeLine(x0, y0, x1, y1, color); + endWrite(); + } } // Draw a circle outline void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, - uint16_t color) { - int16_t f = 1 - r; - int16_t ddF_x = 1; - int16_t ddF_y = -2 * r; - int16_t x = 0; - int16_t y = r; - - startWrite(); - writePixel(x0 , y0+r, color); - writePixel(x0 , y0-r, color); - writePixel(x0+r, y0 , color); - writePixel(x0-r, y0 , color); - - while (x= 0) { - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; - - writePixel(x0 + x, y0 + y, color); - writePixel(x0 - x, y0 + y, color); - writePixel(x0 + x, y0 - y, color); - writePixel(x0 - x, y0 - y, color); - writePixel(x0 + y, y0 + x, color); - writePixel(x0 - y, y0 + x, color); - writePixel(x0 + y, y0 - x, color); - writePixel(x0 - y, y0 - x, color); + uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + startWrite(); + writePixel(x0, y0 + r, color); + writePixel(x0, y0 - r, color); + writePixel(x0 + r, y0, color); + writePixel(x0 - r, y0, color); + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; } - endWrite(); -} - -void Adafruit_GFX::drawCircleHelper( int16_t x0, int16_t y0, - int16_t r, uint8_t cornername, uint16_t color) { - int16_t f = 1 - r; - int16_t ddF_x = 1; - int16_t ddF_y = -2 * r; - int16_t x = 0; - int16_t y = r; - - while (x= 0) { - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; - if (cornername & 0x4) { - writePixel(x0 + x, y0 + y, color); - writePixel(x0 + y, y0 + x, color); - } - if (cornername & 0x2) { - writePixel(x0 + x, y0 - y, color); - writePixel(x0 + y, y0 - x, color); - } - if (cornername & 0x8) { - writePixel(x0 - y, y0 + x, color); - writePixel(x0 - x, y0 + y, color); - } - if (cornername & 0x1) { - writePixel(x0 - y, y0 - x, color); - writePixel(x0 - x, y0 - y, color); - } + x++; + ddF_x += 2; + f += ddF_x; + + writePixel(x0 + x, y0 + y, color); + writePixel(x0 - x, y0 + y, color); + writePixel(x0 + x, y0 - y, color); + writePixel(x0 - x, y0 - y, color); + writePixel(x0 + y, y0 + x, color); + writePixel(x0 - y, y0 + x, color); + writePixel(x0 + y, y0 - x, color); + writePixel(x0 - y, y0 - x, color); + } + endWrite(); +} + +void Adafruit_GFX::drawCircleHelper(int16_t x0, int16_t y0, int16_t r, + uint8_t cornername, uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (cornername & 0x4) { + writePixel(x0 + x, y0 + y, color); + writePixel(x0 + y, y0 + x, color); } + if (cornername & 0x2) { + writePixel(x0 + x, y0 - y, color); + writePixel(x0 + y, y0 - x, color); + } + if (cornername & 0x8) { + writePixel(x0 - y, y0 + x, color); + writePixel(x0 - x, y0 + y, color); + } + if (cornername & 0x1) { + writePixel(x0 - y, y0 - x, color); + writePixel(x0 - x, y0 - y, color); + } + } } void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r, - uint16_t color) { - startWrite(); - writeFastVLine(x0, y0-r, 2*r+1, color); - fillCircleHelper(x0, y0, r, 3, 0, color); - endWrite(); + uint16_t color) { + startWrite(); + writeFastVLine(x0, y0 - r, 2 * r + 1, color); + fillCircleHelper(x0, y0, r, 3, 0, color); + endWrite(); } // Used to do circles and roundrects void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, - uint8_t cornername, int16_t delta, uint16_t color) { - - int16_t f = 1 - r; - int16_t ddF_x = 1; - int16_t ddF_y = -2 * r; - int16_t x = 0; - int16_t y = r; - - while (x= 0) { - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; + uint8_t cornername, int16_t delta, + uint16_t color) { + + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; - if (cornername & 0x1) { - writeFastVLine(x0+x, y0-y, 2*y+1+delta, color); - writeFastVLine(x0+y, y0-x, 2*x+1+delta, color); - } - if (cornername & 0x2) { - writeFastVLine(x0-x, y0-y, 2*y+1+delta, color); - writeFastVLine(x0-y, y0-x, 2*x+1+delta, color); - } + if (cornername & 0x1) { + writeFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color); + writeFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color); + } + if (cornername & 0x2) { + writeFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color); + writeFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color); } + } } // Draw a rectangle void Adafruit_GFX::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, - uint16_t color) { - startWrite(); - writeFastHLine(x, y, w, color); - writeFastHLine(x, y+h-1, w, color); - writeFastVLine(x, y, h, color); - writeFastVLine(x+w-1, y, h, color); - endWrite(); + uint16_t color) { + startWrite(); + writeFastHLine(x, y, w, color); + writeFastHLine(x, y + h - 1, w, color); + writeFastVLine(x, y, h, color); + writeFastVLine(x + w - 1, y, h, color); + endWrite(); } // Draw a rounded rectangle -void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, - int16_t h, int16_t r, uint16_t color) { - // smarter version - startWrite(); - writeFastHLine(x+r , y , w-2*r, color); // Top - writeFastHLine(x+r , y+h-1, w-2*r, color); // Bottom - writeFastVLine(x , y+r , h-2*r, color); // Left - writeFastVLine(x+w-1, y+r , h-2*r, color); // Right - // draw four corners - drawCircleHelper(x+r , y+r , r, 1, color); - drawCircleHelper(x+w-r-1, y+r , r, 2, color); - drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color); - drawCircleHelper(x+r , y+h-r-1, r, 8, color); - endWrite(); +void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, + int16_t r, uint16_t color) { + // smarter version + startWrite(); + writeFastHLine(x + r, y, w - 2 * r, color); // Top + writeFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom + writeFastVLine(x, y + r, h - 2 * r, color); // Left + writeFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right + // draw four corners + drawCircleHelper(x + r, y + r, r, 1, color); + drawCircleHelper(x + w - r - 1, y + r, r, 2, color); + drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); + drawCircleHelper(x + r, y + h - r - 1, r, 8, color); + endWrite(); } // Fill a rounded rectangle -void Adafruit_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, - int16_t h, int16_t r, uint16_t color) { - // smarter version - startWrite(); - writeFillRect(x+r, y, w-2*r, h, color); +void Adafruit_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, + int16_t r, uint16_t color) { + // smarter version + startWrite(); + writeFillRect(x + r, y, w - 2 * r, h, color); - // draw four corners - fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color); - fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color); - endWrite(); + // draw four corners + fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color); + fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color); + endWrite(); } // Draw a triangle -void Adafruit_GFX::drawTriangle(int16_t x0, int16_t y0, - int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { - drawLine(x0, y0, x1, y1, color); - drawLine(x1, y1, x2, y2, color); - drawLine(x2, y2, x0, y0, color); +void Adafruit_GFX::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color) { + drawLine(x0, y0, x1, y1, color); + drawLine(x1, y1, x2, y2, color); + drawLine(x2, y2, x0, y0, color); } // Fill a triangle -void Adafruit_GFX::fillTriangle(int16_t x0, int16_t y0, - int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { +void Adafruit_GFX::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color) { - int16_t a, b, y, last; + int16_t a, b, y, last; - // Sort coordinates by Y order (y2 >= y1 >= y0) - if (y0 > y1) { - _swap_int16_t(y0, y1); _swap_int16_t(x0, x1); - } - if (y1 > y2) { - _swap_int16_t(y2, y1); _swap_int16_t(x2, x1); - } - if (y0 > y1) { - _swap_int16_t(y0, y1); _swap_int16_t(x0, x1); - } + // Sort coordinates by Y order (y2 >= y1 >= y0) + if (y0 > y1) { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } + if (y1 > y2) { + _swap_int16_t(y2, y1); + _swap_int16_t(x2, x1); + } + if (y0 > y1) { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } - startWrite(); - if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing - a = b = x0; - if(x1 < a) a = x1; - else if(x1 > b) b = x1; - if(x2 < a) a = x2; - else if(x2 > b) b = x2; - writeFastHLine(a, y0, b-a+1, color); - endWrite(); - return; - } + startWrite(); + if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if (x1 < a) + a = x1; + else if (x1 > b) + b = x1; + if (x2 < a) + a = x2; + else if (x2 > b) + b = x2; + writeFastHLine(a, y0, b - a + 1, color); + endWrite(); + return; + } - int16_t - dx01 = x1 - x0, - dy01 = y1 - y0, - dx02 = x2 - x0, - dy02 = y2 - y0, - dx12 = x2 - x1, - dy12 = y2 - y1; - int32_t - sa = 0, - sb = 0; - - // For upper part of triangle, find scanline crossings for segments - // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 - // is included here (and second loop will be skipped, avoiding a /0 - // error there), otherwise scanline y1 is skipped here and handled - // in the second loop...which also avoids a /0 error here if y0=y1 - // (flat-topped triangle). - if(y1 == y2) last = y1; // Include y1 scanline - else last = y1-1; // Skip it - - for(y=y0; y<=last; y++) { - a = x0 + sa / dy01; - b = x0 + sb / dy02; - sa += dx01; - sb += dx02; - /* longhand: - a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); - b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); - */ - if(a > b) _swap_int16_t(a,b); - writeFastHLine(a, y, b-a+1, color); - } + int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, + dx12 = x2 - x1, dy12 = y2 - y1; + int32_t sa = 0, sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y1 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y0=y1 + // (flat-topped triangle). + if (y1 == y2) + last = y1; // Include y1 scanline + else + last = y1 - 1; // Skip it + + for (y = y0; y <= last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + /* longhand: + a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if (a > b) + _swap_int16_t(a, b); + writeFastHLine(a, y, b - a + 1, color); + } - // For lower part of triangle, find scanline crossings for segments - // 0-2 and 1-2. This loop is skipped if y1=y2. - sa = dx12 * (y - y1); - sb = dx02 * (y - y0); - for(; y<=y2; y++) { - a = x1 + sa / dy12; - b = x0 + sb / dy02; - sa += dx12; - sb += dx02; - /* longhand: - a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); - b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); - */ - if(a > b) _swap_int16_t(a,b); - writeFastHLine(a, y, b-a+1, color); - } - endWrite(); + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y1=y2. + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + for (; y <= y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if (a > b) + _swap_int16_t(a, b); + writeFastHLine(a, y, b - a + 1, color); + } + endWrite(); } // BITMAP / XBITMAP / GRAYSCALE / RGB BITMAP FUNCTIONS --------------------- // Draw a PROGMEM-resident 1-bit image at the specified (x,y) position, // using the specified foreground color (unset bits are transparent). -void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, - const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) { - - int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte - uint8_t byte = 0; - - startWrite(); - for(int16_t j=0; j>= 1; - else byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); - // Nearly identical to drawBitmap(), only the bit order - // is reversed here (left-to-right = LSB to MSB): - if(byte & 0x01) writePixel(x+i, y, color); - } +void Adafruit_GFX::drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], + int16_t w, int16_t h, uint16_t color) { + + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) { + for (int16_t i = 0; i < w; i++) { + if (i & 7) + byte >>= 1; + else + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + // Nearly identical to drawBitmap(), only the bit order + // is reversed here (left-to-right = LSB to MSB): + if (byte & 0x01) + writePixel(x + i, y, color); } - endWrite(); + } + endWrite(); } // Draw a PROGMEM-resident 8-bit image (grayscale) at the specified (x,y) // pos. Specifically for 8-bit display devices such as IS31FL3731; // no color reduction/expansion is performed. void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, - const uint8_t bitmap[], int16_t w, int16_t h) { - startWrite(); - for(int16_t j=0; j= _width) || // Clip right - (y >= _height) || // Clip bottom - ((x + 6 * size - 1) < 0) || // Clip left - ((y + 8 * size - 1) < 0)) // Clip top - return; - - if(!_cp437 && (c >= 176)) c++; // Handle 'classic' charset behavior - - startWrite(); - for(int8_t i=0; i<5; i++ ) { // Char bitmap = 5 columns - uint8_t line = pgm_read_byte(&font[c * 5 + i]); - for(int8_t j=0; j<8; j++, line >>= 1) { - if(line & 1) { - if(size == 1) - writePixel(x+i, y+j, color); - else - writeFillRect(x+i*size, y+j*size, size, size, color); - } else if(bg != color) { - if(size == 1) - writePixel(x+i, y+j, bg); - else - writeFillRect(x+i*size, y+j*size, size, size, bg); - } - } - } - if(bg != color) { // If opaque, draw vertical line for last column - if(size == 1) writeFastVLine(x+5, y, 8, bg); - else writeFillRect(x+5*size, y, size, 8*size, bg); + uint16_t color, uint16_t bg, uint8_t size) { + + if (!gfxFont) { // 'Classic' built-in font + + if ((x >= _width) || // Clip right + (y >= _height) || // Clip bottom + ((x + 6 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; + + if (!_cp437 && (c >= 176)) + c++; // Handle 'classic' charset behavior + + startWrite(); + for (int8_t i = 0; i < 5; i++) { // Char bitmap = 5 columns + uint8_t line = pgm_read_byte(&font[c * 5 + i]); + for (int8_t j = 0; j < 8; j++, line >>= 1) { + if (line & 1) { + if (size == 1) + writePixel(x + i, y + j, color); + else + writeFillRect(x + i * size, y + j * size, size, size, color); + } else if (bg != color) { + if (size == 1) + writePixel(x + i, y + j, bg); + else + writeFillRect(x + i * size, y + j * size, size, size, bg); } - endWrite(); + } + } + if (bg != color) { // If opaque, draw vertical line for last column + if (size == 1) + writeFastVLine(x + 5, y, 8, bg); + else + writeFillRect(x + 5 * size, y, size, 8 * size, bg); + } + endWrite(); - } else { // Custom font + } else { // Custom font - // Character is assumed previously filtered by write() to eliminate - // newlines, returns, non-printable characters, etc. Calling - // drawChar() directly with 'bad' characters of font may cause mayhem! + // Character is assumed previously filtered by write() to eliminate + // newlines, returns, non-printable characters, etc. Calling + // drawChar() directly with 'bad' characters of font may cause mayhem! - c -= (uint8_t)pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); - uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); + c -= (uint8_t)pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); + uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); - uint16_t bo = pgm_read_word(&glyph->bitmapOffset); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height); - int8_t xo = pgm_read_byte(&glyph->xOffset), - yo = pgm_read_byte(&glyph->yOffset); - uint8_t xx, yy, bits = 0, bit = 0; - int16_t xo16 = 0, yo16 = 0; + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits = 0, bit = 0; + int16_t xo16 = 0, yo16 = 0; - if(size > 1) { - xo16 = xo; - yo16 = yo; - } + if (size > 1) { + xo16 = xo; + yo16 = yo; + } - // Todo: Add character clipping here - - // NOTE: THERE IS NO 'BACKGROUND' COLOR OPTION ON CUSTOM FONTS. - // THIS IS ON PURPOSE AND BY DESIGN. The background color feature - // has typically been used with the 'classic' font to overwrite old - // screen contents with new data. This ONLY works because the - // characters are a uniform size; it's not a sensible thing to do with - // proportionally-spaced fonts with glyphs of varying sizes (and that - // may overlap). To replace previously-drawn text when using a custom - // font, use the getTextBounds() function to determine the smallest - // rectangle encompassing a string, erase the area with fillRect(), - // then draw new text. This WILL infortunately 'blink' the text, but - // is unavoidable. Drawing 'background' pixels will NOT fix this, - // only creates a new set of problems. Have an idea to work around - // this (a canvas object type for MCUs that can afford the RAM and - // displays supporting setAddrWindow() and pushColors()), but haven't - // implemented this yet. - - startWrite(); - for(yy=0; yy= 100 @@ -811,102 +882,92 @@ size_t Adafruit_GFX::write(uint8_t c) { #else void Adafruit_GFX::write(uint8_t c) { #endif - if(!gfxFont) { // 'Classic' built-in font - - if(c == '\n') { // Newline? - cursor_x = 0; // Reset x to zero, - cursor_y += textsize * 8; // advance y one line - } else if(c != '\r') { // Ignore carriage returns - if(wrap && ((cursor_x + textsize * 6) > _width)) { // Off right? - cursor_x = 0; // Reset x to zero, - cursor_y += textsize * 8; // advance y one line - } - drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); - cursor_x += textsize * 6; // Advance x one char - } + if (!gfxFont) { // 'Classic' built-in font + + if (c == '\n') { // Newline? + cursor_x = 0; // Reset x to zero, + cursor_y += textsize * 8; // advance y one line + } else if (c != '\r') { // Ignore carriage returns + if (wrap && ((cursor_x + textsize * 6) > _width)) { // Off right? + cursor_x = 0; // Reset x to zero, + cursor_y += textsize * 8; // advance y one line + } + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); + cursor_x += textsize * 6; // Advance x one char + } - } else { // Custom font - - if(c == '\n') { - cursor_x = 0; - cursor_y += (int16_t)textsize * - (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } else if(c != '\r') { - uint8_t first = pgm_read_byte(&gfxFont->first); - if((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) { - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer( - &gfxFont->glyph))[c - first]); - uint8_t w = pgm_read_byte(&glyph->width), - h = pgm_read_byte(&glyph->height); - if((w > 0) && (h > 0)) { // Is there an associated bitmap? - int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic - if(wrap && ((cursor_x + textsize * (xo + w)) > _width)) { - cursor_x = 0; - cursor_y += (int16_t)textsize * - (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } - drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); - } - cursor_x += (uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; - } + } else { // Custom font + + if (c == '\n') { + cursor_x = 0; + cursor_y += + (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else if (c != '\r') { + uint8_t first = pgm_read_byte(&gfxFont->first); + if ((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) { + GFXglyph *glyph = + &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c - first]); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height); + if ((w > 0) && (h > 0)) { // Is there an associated bitmap? + int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic + if (wrap && ((cursor_x + textsize * (xo + w)) > _width)) { + cursor_x = 0; + cursor_y += + (int16_t)textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); } - + cursor_x += + (uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + } } + } #if ARDUINO >= 100 - return 1; + return 1; #endif } void Adafruit_GFX::setCursor(int16_t x, int16_t y) { - cursor_x = x; - cursor_y = y; + cursor_x = x; + cursor_y = y; } -int16_t Adafruit_GFX::getCursorX(void) const { - return cursor_x; -} +int16_t Adafruit_GFX::getCursorX(void) const { return cursor_x; } -int16_t Adafruit_GFX::getCursorY(void) const { - return cursor_y; -} +int16_t Adafruit_GFX::getCursorY(void) const { return cursor_y; } -void Adafruit_GFX::setTextSize(uint8_t s) { - textsize = (s > 0) ? s : 1; -} +void Adafruit_GFX::setTextSize(uint8_t s) { textsize = (s > 0) ? s : 1; } void Adafruit_GFX::setTextColor(uint16_t c) { - // For 'transparent' background, we'll set the bg - // to the same as fg instead of using a flag - textcolor = textbgcolor = c; + // For 'transparent' background, we'll set the bg + // to the same as fg instead of using a flag + textcolor = textbgcolor = c; } void Adafruit_GFX::setTextColor(uint16_t c, uint16_t b) { - textcolor = c; - textbgcolor = b; + textcolor = c; + textbgcolor = b; } -void Adafruit_GFX::setTextWrap(boolean w) { - wrap = w; -} +void Adafruit_GFX::setTextWrap(boolean w) { wrap = w; } -uint8_t Adafruit_GFX::getRotation(void) const { - return rotation; -} +uint8_t Adafruit_GFX::getRotation(void) const { return rotation; } void Adafruit_GFX::setRotation(uint8_t x) { - rotation = (x & 3); - switch(rotation) { - case 0: - case 2: - _width = WIDTH; - _height = HEIGHT; - break; - case 1: - case 3: - _width = HEIGHT; - _height = WIDTH; - break; - } + rotation = (x & 3); + switch (rotation) { + case 0: + case 2: + _width = WIDTH; + _height = HEIGHT; + break; + case 1: + case 3: + _width = HEIGHT; + _height = WIDTH; + break; + } } // Enable (or disable) Code Page 437-compatible charset. @@ -916,209 +977,205 @@ void Adafruit_GFX::setRotation(uint8_t x) { // with the erroneous character indices. By default, the library uses the // original 'wrong' behavior and old sketches will still work. Pass 'true' // to this function to use correct CP437 character values in your code. -void Adafruit_GFX::cp437(boolean x) { - _cp437 = x; -} +void Adafruit_GFX::cp437(boolean x) { _cp437 = x; } void Adafruit_GFX::setFont(const GFXfont *f) { - if(f) { // Font struct pointer passed in? - if(!gfxFont) { // And no current font struct? - // Switching from classic to new font behavior. - // Move cursor pos down 6 pixels so it's on baseline. - cursor_y += 6; - } - } else if(gfxFont) { // NULL passed. Current font struct defined? - // Switching from new to classic font behavior. - // Move cursor pos up 6 pixels so it's at top-left of char. - cursor_y -= 6; + if (f) { // Font struct pointer passed in? + if (!gfxFont) { // And no current font struct? + // Switching from classic to new font behavior. + // Move cursor pos down 6 pixels so it's on baseline. + cursor_y += 6; } - gfxFont = (GFXfont *)f; + } else if (gfxFont) { // NULL passed. Current font struct defined? + // Switching from new to classic font behavior. + // Move cursor pos up 6 pixels so it's at top-left of char. + cursor_y -= 6; + } + gfxFont = (GFXfont *)f; } // Broke this out as it's used by both the PROGMEM- and RAM-resident // getTextBounds() functions. -void Adafruit_GFX::charBounds(char c, int16_t *x, int16_t *y, - int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy) { - - if(gfxFont) { - - if(c == '\n') { // Newline? - *x = 0; // Reset x to zero, advance y by one line - *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } else if(c != '\r') { // Not a carriage return; is normal char - uint8_t first = pgm_read_byte(&gfxFont->first), - last = pgm_read_byte(&gfxFont->last); - if((c >= first) && (c <= last)) { // Char present in this font? - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer( - &gfxFont->glyph))[c - first]); - uint8_t gw = pgm_read_byte(&glyph->width), - gh = pgm_read_byte(&glyph->height), - xa = pgm_read_byte(&glyph->xAdvance); - int8_t xo = pgm_read_byte(&glyph->xOffset), - yo = pgm_read_byte(&glyph->yOffset); - if(wrap && ((*x+(((int16_t)xo+gw)*textsize)) > _width)) { - *x = 0; // Reset x to zero, advance y by one line - *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); - } - int16_t ts = (int16_t)textsize, - x1 = *x + xo * ts, - y1 = *y + yo * ts, - x2 = x1 + gw * ts - 1, - y2 = y1 + gh * ts - 1; - if(x1 < *minx) *minx = x1; - if(y1 < *miny) *miny = y1; - if(x2 > *maxx) *maxx = x2; - if(y2 > *maxy) *maxy = y2; - *x += xa * ts; - } +void Adafruit_GFX::charBounds(char c, int16_t *x, int16_t *y, int16_t *minx, + int16_t *miny, int16_t *maxx, int16_t *maxy) { + + if (gfxFont) { + + if (c == '\n') { // Newline? + *x = 0; // Reset x to zero, advance y by one line + *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } else if (c != '\r') { // Not a carriage return; is normal char + uint8_t first = pgm_read_byte(&gfxFont->first), + last = pgm_read_byte(&gfxFont->last); + if ((c >= first) && (c <= last)) { // Char present in this font? + GFXglyph *glyph = + &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c - first]); + uint8_t gw = pgm_read_byte(&glyph->width), + gh = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + if (wrap && ((*x + (((int16_t)xo + gw) * textsize)) > _width)) { + *x = 0; // Reset x to zero, advance y by one line + *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } + int16_t ts = (int16_t)textsize, x1 = *x + xo * ts, y1 = *y + yo * ts, + x2 = x1 + gw * ts - 1, y2 = y1 + gh * ts - 1; + if (x1 < *minx) + *minx = x1; + if (y1 < *miny) + *miny = y1; + if (x2 > *maxx) + *maxx = x2; + if (y2 > *maxy) + *maxy = y2; + *x += xa * ts; + } + } - } else { // Default font - - if(c == '\n') { // Newline? - *x = 0; // Reset x to zero, - *y += textsize * 8; // advance y one line - // min/max x/y unchaged -- that waits for next 'normal' character - } else if(c != '\r') { // Normal char; ignore carriage returns - if(wrap && ((*x + textsize * 6) > _width)) { // Off right? - *x = 0; // Reset x to zero, - *y += textsize * 8; // advance y one line - } - int x2 = *x + textsize * 6 - 1, // Lower-right pixel of char - y2 = *y + textsize * 8 - 1; - if(x2 > *maxx) *maxx = x2; // Track max x, y - if(y2 > *maxy) *maxy = y2; - if(*x < *minx) *minx = *x; // Track min x, y - if(*y < *miny) *miny = *y; - *x += textsize * 6; // Advance x one char - } + } else { // Default font + + if (c == '\n') { // Newline? + *x = 0; // Reset x to zero, + *y += textsize * 8; // advance y one line + // min/max x/y unchaged -- that waits for next 'normal' character + } else if (c != '\r') { // Normal char; ignore carriage returns + if (wrap && ((*x + textsize * 6) > _width)) { // Off right? + *x = 0; // Reset x to zero, + *y += textsize * 8; // advance y one line + } + int x2 = *x + textsize * 6 - 1, // Lower-right pixel of char + y2 = *y + textsize * 8 - 1; + if (x2 > *maxx) + *maxx = x2; // Track max x, y + if (y2 > *maxy) + *maxy = y2; + if (*x < *minx) + *minx = *x; // Track min x, y + if (*y < *miny) + *miny = *y; + *x += textsize * 6; // Advance x one char } + } } // Pass string and a cursor position, returns UL corner and W,H. -void Adafruit_GFX::getTextBounds(char *str, int16_t x, int16_t y, - int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) { - uint8_t c; // Current character +void Adafruit_GFX::getTextBounds(char *str, int16_t x, int16_t y, int16_t *x1, + int16_t *y1, uint16_t *w, uint16_t *h) { + uint8_t c; // Current character - *x1 = x; - *y1 = y; - *w = *h = 0; + *x1 = x; + *y1 = y; + *w = *h = 0; - int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; + int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; - while((c = *str++)) - charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); + while ((c = *str++)) + charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); - if(maxx >= minx) { - *x1 = minx; - *w = maxx - minx + 1; - } - if(maxy >= miny) { - *y1 = miny; - *h = maxy - miny + 1; - } + if (maxx >= minx) { + *x1 = minx; + *w = maxx - minx + 1; + } + if (maxy >= miny) { + *y1 = miny; + *h = maxy - miny + 1; + } } // Same as above, but for PROGMEM strings -void Adafruit_GFX::getTextBounds(const __FlashStringHelper *str, - int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) { - uint8_t *s = (uint8_t *)str, c; +void Adafruit_GFX::getTextBounds(const __FlashStringHelper *str, int16_t x, + int16_t y, int16_t *x1, int16_t *y1, + uint16_t *w, uint16_t *h) { + uint8_t *s = (uint8_t *)str, c; - *x1 = x; - *y1 = y; - *w = *h = 0; + *x1 = x; + *y1 = y; + *w = *h = 0; - int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; + int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; - while((c = pgm_read_byte(s++))) - charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); + while ((c = pgm_read_byte(s++))) + charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); - if(maxx >= minx) { - *x1 = minx; - *w = maxx - minx + 1; - } - if(maxy >= miny) { - *y1 = miny; - *h = maxy - miny + 1; - } + if (maxx >= minx) { + *x1 = minx; + *w = maxx - minx + 1; + } + if (maxy >= miny) { + *y1 = miny; + *h = maxy - miny + 1; + } } // Return the size of the display (per current rotation) -int16_t Adafruit_GFX::width(void) const { - return _width; -} +int16_t Adafruit_GFX::width(void) const { return _width; } -int16_t Adafruit_GFX::height(void) const { - return _height; -} +int16_t Adafruit_GFX::height(void) const { return _height; } void Adafruit_GFX::invertDisplay(boolean i) { - // Do nothing, must be subclassed if supported by hardware + // Do nothing, must be subclassed if supported by hardware } /***************************************************************************/ // code for the GFX button UI element -Adafruit_GFX_Button::Adafruit_GFX_Button(void) { - _gfx = 0; -} +Adafruit_GFX_Button::Adafruit_GFX_Button(void) { _gfx = 0; } // Classic initButton() function: pass center & size -void Adafruit_GFX_Button::initButton( - Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, - uint16_t outline, uint16_t fill, uint16_t textcolor, - char *label, uint8_t textsize) -{ +void Adafruit_GFX_Button::initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, + uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) { // Tweak arguments and pass to the newer initButtonUL() function... - initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, - textcolor, label, textsize); + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor, + label, textsize); } // Newer function instead accepts upper-left corner & size -void Adafruit_GFX_Button::initButtonUL( - Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, - uint16_t outline, uint16_t fill, uint16_t textcolor, - char *label, uint8_t textsize) -{ - _x1 = x1; - _y1 = y1; - _w = w; - _h = h; +void Adafruit_GFX_Button::initButtonUL(Adafruit_GFX *gfx, int16_t x1, + int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, + uint8_t textsize) { + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; _outlinecolor = outline; - _fillcolor = fill; - _textcolor = textcolor; - _textsize = textsize; - _gfx = gfx; + _fillcolor = fill; + _textcolor = textcolor; + _textsize = textsize; + _gfx = gfx; strncpy(_label, label, 9); } void Adafruit_GFX_Button::drawButton(boolean inverted) { uint16_t fill, outline, text; - if(!inverted) { - fill = _fillcolor; + if (!inverted) { + fill = _fillcolor; outline = _outlinecolor; - text = _textcolor; + text = _textcolor; } else { - fill = _textcolor; + fill = _textcolor; outline = _outlinecolor; - text = _fillcolor; + text = _fillcolor; } uint8_t r = min(_w, _h) / 4; // Corner radius _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); - _gfx->setCursor(_x1 + (_w/2) - (strlen(_label) * 3 * _textsize), - _y1 + (_h/2) - (4 * _textsize)); + _gfx->setCursor(_x1 + (_w / 2) - (strlen(_label) * 3 * _textsize), + _y1 + (_h / 2) - (4 * _textsize)); _gfx->setTextColor(text); _gfx->setTextSize(_textsize); _gfx->print(_label); } boolean Adafruit_GFX_Button::contains(int16_t x, int16_t y) { - return ((x >= _x1) && (x < (_x1 + _w)) && - (y >= _y1) && (y < (_y1 + _h))); + return ((x >= _x1) && (x < (_x1 + _w)) && (y >= _y1) && (y < (_y1 + _h))); } void Adafruit_GFX_Button::press(boolean p) { @@ -1128,7 +1185,9 @@ void Adafruit_GFX_Button::press(boolean p) { boolean Adafruit_GFX_Button::isPressed() { return currstate; } boolean Adafruit_GFX_Button::justPressed() { return (currstate && !laststate); } -boolean Adafruit_GFX_Button::justReleased() { return (!currstate && laststate); } +boolean Adafruit_GFX_Button::justReleased() { + return (!currstate && laststate); +} // ------------------------------------------------------------------------- @@ -1150,199 +1209,207 @@ boolean Adafruit_GFX_Button::justReleased() { return (!currstate && laststate); // NOT EXTENSIVELY TESTED YET. MAY CONTAIN WORST BUGS KNOWN TO HUMANKIND. GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { - uint16_t bytes = ((w + 7) / 8) * h; - if((buffer = (uint8_t *)malloc(bytes))) { - memset(buffer, 0, bytes); - } + uint16_t bytes = ((w + 7) / 8) * h; + if ((buffer = (uint8_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } } GFXcanvas1::~GFXcanvas1(void) { - if(buffer) free(buffer); + if (buffer) + free(buffer); } -uint8_t* GFXcanvas1::getBuffer(void) { - return buffer; -} +uint8_t *GFXcanvas1::getBuffer(void) { return buffer; } void GFXcanvas1::drawPixel(int16_t x, int16_t y, uint16_t color) { #ifdef __AVR__ - // Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR - static const uint8_t PROGMEM - GFXsetBit[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, - GFXclrBit[] = { 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE }; + // Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR + static const uint8_t PROGMEM GFXsetBit[] = {0x80, 0x40, 0x20, 0x10, + 0x08, 0x04, 0x02, 0x01}, + GFXclrBit[] = {0x7F, 0xBF, 0xDF, 0xEF, + 0xF7, 0xFB, 0xFD, 0xFE}; #endif - if(buffer) { - if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return; - - int16_t t; - switch(rotation) { - case 1: - t = x; - x = WIDTH - 1 - y; - y = t; - break; - case 2: - x = WIDTH - 1 - x; - y = HEIGHT - 1 - y; - break; - case 3: - t = x; - x = y; - y = HEIGHT - 1 - t; - break; - } + if (buffer) { + if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) + return; + + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } - uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)]; + uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)]; #ifdef __AVR__ - if(color) *ptr |= pgm_read_byte(&GFXsetBit[x & 7]); - else *ptr &= pgm_read_byte(&GFXclrBit[x & 7]); + if (color) + *ptr |= pgm_read_byte(&GFXsetBit[x & 7]); + else + *ptr &= pgm_read_byte(&GFXclrBit[x & 7]); #else - if(color) *ptr |= 0x80 >> (x & 7); - else *ptr &= ~(0x80 >> (x & 7)); + if (color) + *ptr |= 0x80 >> (x & 7); + else + *ptr &= ~(0x80 >> (x & 7)); #endif - } + } } void GFXcanvas1::fillScreen(uint16_t color) { - if(buffer) { - uint16_t bytes = ((WIDTH + 7) / 8) * HEIGHT; - memset(buffer, color ? 0xFF : 0x00, bytes); - } + if (buffer) { + uint16_t bytes = ((WIDTH + 7) / 8) * HEIGHT; + memset(buffer, color ? 0xFF : 0x00, bytes); + } } GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { - uint32_t bytes = w * h; - if((buffer = (uint8_t *)malloc(bytes))) { - memset(buffer, 0, bytes); - } + uint32_t bytes = w * h; + if ((buffer = (uint8_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } } GFXcanvas8::~GFXcanvas8(void) { - if(buffer) free(buffer); + if (buffer) + free(buffer); } -uint8_t* GFXcanvas8::getBuffer(void) { - return buffer; -} +uint8_t *GFXcanvas8::getBuffer(void) { return buffer; } void GFXcanvas8::drawPixel(int16_t x, int16_t y, uint16_t color) { - if(buffer) { - if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return; - - int16_t t; - switch(rotation) { - case 1: - t = x; - x = WIDTH - 1 - y; - y = t; - break; - case 2: - x = WIDTH - 1 - x; - y = HEIGHT - 1 - y; - break; - case 3: - t = x; - x = y; - y = HEIGHT - 1 - t; - break; - } + if (buffer) { + if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) + return; - buffer[x + y * WIDTH] = color; + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; } + + buffer[x + y * WIDTH] = color; + } } void GFXcanvas8::fillScreen(uint16_t color) { - if(buffer) { - memset(buffer, color, WIDTH * HEIGHT); - } + if (buffer) { + memset(buffer, color, WIDTH * HEIGHT); + } } -void GFXcanvas8::writeFastHLine(int16_t x, int16_t y, - int16_t w, uint16_t color) { +void GFXcanvas8::writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { - if((x >= _width) || (y < 0) || (y >= _height)) return; - int16_t x2 = x + w - 1; - if(x2 < 0) return; + if ((x >= _width) || (y < 0) || (y >= _height)) + return; + int16_t x2 = x + w - 1; + if (x2 < 0) + return; - // Clip left/right - if(x < 0) { - x = 0; - w = x2 + 1; - } - if(x2 >= _width) w = _width - x; - - int16_t t; - switch(rotation) { - case 1: - t = x; - x = WIDTH - 1 - y; - y = t; - break; - case 2: - x = WIDTH - 1 - x; - y = HEIGHT - 1 - y; - break; - case 3: - t = x; - x = y; - y = HEIGHT - 1 - t; - break; - } + // Clip left/right + if (x < 0) { + x = 0; + w = x2 + 1; + } + if (x2 >= _width) + w = _width - x; + + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; + } - memset(buffer + y * WIDTH + x, color, w); + memset(buffer + y * WIDTH + x, color, w); } GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { - uint32_t bytes = w * h * 2; - if((buffer = (uint16_t *)malloc(bytes))) { - memset(buffer, 0, bytes); - } + uint32_t bytes = w * h * 2; + if ((buffer = (uint16_t *)malloc(bytes))) { + memset(buffer, 0, bytes); + } } GFXcanvas16::~GFXcanvas16(void) { - if(buffer) free(buffer); + if (buffer) + free(buffer); } -uint16_t* GFXcanvas16::getBuffer(void) { - return buffer; -} +uint16_t *GFXcanvas16::getBuffer(void) { return buffer; } void GFXcanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) { - if(buffer) { - if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return; - - int16_t t; - switch(rotation) { - case 1: - t = x; - x = WIDTH - 1 - y; - y = t; - break; - case 2: - x = WIDTH - 1 - x; - y = HEIGHT - 1 - y; - break; - case 3: - t = x; - x = y; - y = HEIGHT - 1 - t; - break; - } + if (buffer) { + if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) + return; - buffer[x + y * WIDTH] = color; + int16_t t; + switch (rotation) { + case 1: + t = x; + x = WIDTH - 1 - y; + y = t; + break; + case 2: + x = WIDTH - 1 - x; + y = HEIGHT - 1 - y; + break; + case 3: + t = x; + x = y; + y = HEIGHT - 1 - t; + break; } + + buffer[x + y * WIDTH] = color; + } } void GFXcanvas16::fillScreen(uint16_t color) { - if(buffer) { - uint8_t hi = color >> 8, lo = color & 0xFF; - if(hi == lo) { - memset(buffer, lo, WIDTH * HEIGHT * 2); - } else { - uint32_t i, pixels = WIDTH * HEIGHT; - for(i=0; i> 8, lo = color & 0xFF; + if (hi == lo) { + memset(buffer, lo, WIDTH * HEIGHT * 2); + } else { + uint32_t i, pixels = WIDTH * HEIGHT; + for (i = 0; i < pixels; i++) + buffer[i] = color; } + } } - diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h index c71b8fcfa..8e8fe8331 100755 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h @@ -65,9 +65,9 @@ class Adafruit_GFX : public Print { fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color), drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], - int16_t w, int16_t h, uint16_t color), + int16_t w, int16_t h, uint16_t color, bool flip_vert = false, bool flip_horiz = false), drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], - int16_t w, int16_t h, uint16_t color, uint16_t bg), + int16_t w, int16_t h, uint16_t color, uint16_t bg, bool flip_vert = false, bool flip_horiz = false), drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color), drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 481db19d1..42c1e25f7 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -17,14 +17,14 @@ void RAMPage::setup() { void RAMPage::init() { DEBUG_PRINT_FN(); #ifdef OLED_DISPLAY - // classic_display = false; + classic_display = false; oled_display.clearDisplay(); oled_display.setFont(); #endif encoders[0]->cur = MONO; md_exploit.off(); if (page_id == 0) { - setup_callbacks(); + setup_callbacks(); } } @@ -84,12 +84,12 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, if (linked_track == 255) { md_track.machine.trigGroup = 255; md_track.seq_data.pattern_mask = 1; - // md_track.seq_data.conditional[0] = 14; + // md_track.seq_data.conditional[0] = 14; } else if (track > linked_track) { md_track.machine.trigGroup = linked_track; md_track.seq_data.pattern_mask = 1; // oneshot - //md_track.seq_data.conditional[0] = 14; + // md_track.seq_data.conditional[0] = 14; } else { md_track.machine.trigGroup = 255; md_track.seq_data.pattern_mask = 0; @@ -112,7 +112,7 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); transition_step = next_step; - record_len = (uint8_t) steps; + record_len = (uint8_t)steps; /* if (MD.kit.models[track] == md_track.machine.model) { mcl_actions.send_machine[track] = 1; } else { @@ -329,7 +329,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, // mcl_actions.transition_level[track] = TRANSITION_MUTE; mcl_actions.next_transitions[track] = next_step; transition_step = next_step; - record_len = (uint8_t) steps; + record_len = (uint8_t)steps; mcl_actions.calc_next_transition(); EmptyTrack empty_track; @@ -373,6 +373,21 @@ void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, setup_ram_rec(track + 1, RAM_R2_MODEL, mlev, len, rate, 127, track); } +void RAMPage::loop() { + uint8_t n = 14 + page_id; + if (grid_page.active_slots[n] == SLOT_RAM_RECORD) { + if ((rec_state == STATE_QUEUE) && + (MidiClock.div16th_counter == transition_step + record_len)) { + rec_state = STATE_RECORD; + } + // else if ((rec_state == STATE_RECORD) && (MidiClock.div16th_counter >= + // transition_step + record_len + record_len)) { + } else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && + (MidiClock.div16th_counter >= transition_step + record_len)) { + rec_state = STATE_PLAY; + } +} + void RAMPage::display() { if (!classic_display) { @@ -380,22 +395,14 @@ void RAMPage::display() { oled_display.clearDisplay(); #endif } +#ifndef OLED_DISPLAY GUI.clearLines(); GUI.setLine(GUI.LINE1); uint8_t x; - // GUI.put_string_at(12,"RAM"); + GUI.put_string_at(0, "RAM"); GUI.put_value_at1(4, page_id + 1); - uint8_t n = 14 + page_id; - if (grid_page.active_slots[n] == SLOT_RAM_RECORD) { - if ((rec_state == STATE_QUEUE) && (MidiClock.div16th_counter == transition_step + record_len)) { - rec_state = STATE_RECORD; - } - //else if ((rec_state == STATE_RECORD) && (MidiClock.div16th_counter >= transition_step + record_len + record_len)) { - } else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (MidiClock.div16th_counter >= transition_step + record_len)) { - rec_state = STATE_PLAY; - } - // in_sysex = 0; + switch (rec_state) { case STATE_QUEUE: GUI.put_string_at(6, "[Queue]"); @@ -421,13 +428,87 @@ void RAMPage::display() { GUI.put_value_at2(8, 1 << encoders[2]->cur); GUI.put_string_at(11, "L:"); GUI.put_value_at2(13, (encoders[3]->cur * 4)); -/* -GUI.put_value_at1(8,msb); -GUI.put_string_at(9,"."); -GUI.put_value_at1(10,mantissa / 10); -GUI.put_value_at1(11,mantissa % 10); -*/ +#endif #ifdef OLED_DISPLAY + + oled_display.setCursor(0,0); + + oled_display.print("RAM"); + oled_display.print(page_id + 1); + switch (rec_state) { + case STATE_QUEUE: + oled_display.print(" [Queue]"); + break; + case STATE_RECORD: + oled_display.print(" [Record]"); + break; + case STATE_PLAY: + oled_display.print(" [Play]"); + break; + } + + +oled_display.setCursor(0,15); + if (encoders[0]->cur == 0) { + oled_display.print("MON "); + } else { + oled_display.print("LNK "); + } + + oled_display.print(encoders[1]->cur); + oled_display.print(" S:"); + oled_display.print(1 << encoders[2]->cur); + oled_display.print(" Q:"); + oled_display.print(encoders[3]->cur * 4); + + uint8_t w_x = 100, w_y = 4; + oled_display.drawPixel(w_x + 21, w_y + 24, WHITE); + oled_display.drawCircle(w_x + 21, w_y + 24, 2, WHITE); + oled_display.drawLine(w_x + 21, w_y + 9, w_x + 23, +w_y + 23, WHITE); + oled_display.drawLine(w_x + 9, w_y + 21, w_x + 21, +w_y + 26, WHITE); + + switch (wheel_spin) { + case 0: + oled_display.drawBitmap(w_x, w_y, wheel_top, 21, 21, WHITE); + break; + case 1: + oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE); + break; + case 2: + oled_display.drawBitmap(w_x, w_y, wheel_side, 21, 21, WHITE); + break; + case 3: + oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE, false, true); + break; + case 4: + oled_display.drawBitmap(w_x, w_y, wheel_top, 21, 21, WHITE, false, true); + break; + case 5: + oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE, true, true); + break; + case 6: + oled_display.drawBitmap(w_x, w_y, wheel_side, 21, 21, WHITE, true, false); + break; + case 7: + oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE, true, false); + break; + } + if ((wheel_spin_last_clock != MidiClock.div16th_counter) && + ((rec_state == STATE_RECORD) || (rec_state == STATE_PLAY))) { + if (magic == 1) { + if (wheel_spin == 0) { + wheel_spin = 8; + } + wheel_spin--; + } else { + wheel_spin++; + if (wheel_spin == 8) { + wheel_spin = 0; + } + } + wheel_spin_last_clock = MidiClock.div16th_counter; + } + oled_display.display(); #endif } void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { @@ -440,7 +521,9 @@ void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { // to all polyphonic tracks uint8_t param_true = 0; - if (encoders[0]->cur == MONO) { return; } + if (encoders[0]->cur == MONO) { + return; + } MD.parseCC(channel, param, &track, &track_param); if (grid_page.active_slots[track] != SLOT_RAM_PLAY) { @@ -516,8 +599,8 @@ bool RAMPage::handleEvent(gui_event_t *event) { setup_ram_rec_mono(15, 64, 4 * encoders[3]->cur - 1, 128); } } else { - setup_ram_rec_stereo(14, 64 - 16, 4 * encoders[3]->cur - 1, 128); - } + setup_ram_rec_stereo(14, 64 - 16, 4 * encoders[3]->cur - 1, 128); + } } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index 0bae06dce..6884efd29 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -9,6 +9,31 @@ #define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 #define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 +// 'wheel top', 21x21px +const unsigned char wheel_top[] PROGMEM = { + 0x01, 0xfc, 0x00, 0x06, 0x03, 0x00, 0x08, 0xf8, 0x80, 0x10, 0xf8, + 0x40, 0x20, 0xf8, 0x20, 0x40, 0x70, 0x10, 0x40, 0x70, 0x10, 0x80, + 0x20, 0x08, 0x80, 0x00, 0x08, 0x80, 0x20, 0x08, 0x80, 0x50, 0x08, + 0x80, 0x20, 0x08, 0x83, 0x8e, 0x08, 0xcf, 0x8f, 0x88, 0x5f, 0x8f, + 0xd0, 0x4f, 0x07, 0x90, 0x27, 0x07, 0x20, 0x12, 0x04, 0x40, 0x08, + 0x00, 0x80, 0x06, 0x03, 0x00, 0x01, 0xfc, 0x00}; +// 'wheel angle', 21x21px +const unsigned char wheel_angle[] PROGMEM = { + 0x01, 0xfc, 0x00, 0x06, 0x03, 0x00, 0x08, 0x00, 0x80, 0x10, 0x02, + 0x40, 0x20, 0x07, 0x20, 0x40, 0x07, 0x90, 0x40, 0x0f, 0xd0, 0x80, + 0x0f, 0x88, 0xb8, 0x0e, 0x08, 0xbe, 0x20, 0x08, 0xbf, 0x50, 0x08, + 0xbe, 0x20, 0x08, 0xb8, 0x00, 0x08, 0x80, 0x20, 0x08, 0x40, 0x70, + 0x10, 0x40, 0x70, 0x10, 0x20, 0xf8, 0x20, 0x10, 0xf8, 0x40, 0x08, + 0xf8, 0x80, 0x06, 0x03, 0x00, 0x01, 0xfc, 0x00}; +// 'wheel side', 21x21px +const unsigned char wheel_side[] PROGMEM = { + 0x01, 0xfc, 0x00, 0x06, 0x03, 0x00, 0x08, 0x00, 0x80, 0x12, 0x00, + 0x40, 0x27, 0x00, 0x20, 0x4f, 0x00, 0x10, 0x5f, 0x80, 0x10, 0x8f, + 0x80, 0x08, 0x83, 0x80, 0xe8, 0x80, 0x23, 0xe8, 0x80, 0x57, 0xe8, + 0x80, 0x23, 0xe8, 0x83, 0x80, 0xe8, 0x8f, 0x80, 0x08, 0x5f, 0x80, + 0x10, 0x4f, 0x00, 0x10, 0x27, 0x00, 0x20, 0x12, 0x00, 0x40, 0x08, + 0x00, 0x80, 0x06, 0x03, 0x00, 0x01, 0xfc, 0x00}; + class RAMPage : public LightPage, MidiCallback { public: RAMPage(uint8_t _page_id, Encoder *e1 = NULL, Encoder *e2 = NULL, @@ -30,9 +55,13 @@ class RAMPage : public LightPage, MidiCallback { uint8_t page_id; uint16_t transition_step; uint8_t record_len; + + uint8_t wheel_spin; + uint16_t wheel_spin_last_clock; void display(); void setup(); void init(); + void loop(); void cleanup(); void setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, uint8_t len, uint8_t rate, uint8_t pan, uint8_t linked_track = 255); From 3ba7a7b0e177491942b77bb0e17586a7398a8c2b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 16 Oct 2019 20:58:27 +1100 Subject: [PATCH 043/469] Add progress bar. needs work --- avr/cores/megacommand/MCL/RAMPage.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 42c1e25f7..b99a2d2ac 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -385,7 +385,7 @@ void RAMPage::loop() { } else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && (MidiClock.div16th_counter >= transition_step + record_len)) { rec_state = STATE_PLAY; - } + } } void RAMPage::display() { @@ -402,8 +402,7 @@ void RAMPage::display() { GUI.put_string_at(0, "RAM"); GUI.put_value_at1(4, page_id + 1); - - switch (rec_state) { + ≈ switch (rec_state) { case STATE_QUEUE: GUI.put_string_at(6, "[Queue]"); break; @@ -430,8 +429,23 @@ void RAMPage::display() { GUI.put_value_at2(13, (encoders[3]->cur * 4)); #endif #ifdef OLED_DISPLAY + float remain; + if (rec_state != STATE_NOSTATE) { + if (transition_step > MidiClock.div16th_counter) { - oled_display.setCursor(0,0); + remain = + ((float)(transition_step) / + (float)MidiClock.div16th_counter); + } + // else if (rec_state == STATE_RECORD{ + else { + uint8_t n = 14 + page_id; + remain = (float)mcl_seq.md_tracks[n].step_count / + (float)mcl_seq.md_tracks[n].length; + } + oled_display.fillRect(0, 28, remain * 50, 4, WHITE); + } + oled_display.setCursor(0, 0); oled_display.print("RAM"); oled_display.print(page_id + 1); @@ -447,8 +461,7 @@ void RAMPage::display() { break; } - -oled_display.setCursor(0,15); + oled_display.setCursor(0, 15); if (encoders[0]->cur == 0) { oled_display.print("MON "); } else { From 648f19c9971ac5721b14d4bfad9cfb25cdd76813 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Wed, 16 Oct 2019 19:33:07 +0800 Subject: [PATCH 044/469] wraping up. remove debug flag --- .../megacommand/Design/Menu_scratchpad.png | Bin 0 -> 13116 bytes avr/cores/megacommand/Design/icons.xcf | Bin 0 -> 3489 bytes avr/cores/megacommand/Design/menu_scratch.xcf | Bin 0 -> 10657 bytes avr/cores/megacommand/WProgram.h | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 avr/cores/megacommand/Design/Menu_scratchpad.png create mode 100644 avr/cores/megacommand/Design/icons.xcf create mode 100644 avr/cores/megacommand/Design/menu_scratch.xcf diff --git a/avr/cores/megacommand/Design/Menu_scratchpad.png b/avr/cores/megacommand/Design/Menu_scratchpad.png new file mode 100644 index 0000000000000000000000000000000000000000..9dade4eb1e4c3f40af463cb22ad2be15dcaa7eee GIT binary patch literal 13116 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 literal 0 HcmV?d00001 diff --git a/avr/cores/megacommand/Design/icons.xcf b/avr/cores/megacommand/Design/icons.xcf new file mode 100644 index 0000000000000000000000000000000000000000..e3bf0631cdf77ed7383f277ef9c1798e9010366c GIT binary patch literal 3489 zcmeHJPfrs;6o0!#*p}8}R3O2_AQXyPL*=5z2tltV8V{b!cDrsU-P#rdr`@mMLBD_} zuYLlregVIMCu5?CAwj|M_hx2cQHa4rOiW+;o8NEWyf-tQf4hFL))pIXLp+_YRtXjZ z0$_UqoB=Eg#aY0)Q~W-`fIbHd0E57&(Kh%z;xB+ld^BAN)@r_2@jF2sEwb^I_^ah86&? zL-B4WY`49-SYB^5ybe6X)6T|A!Al~6N!E)oN-P_|eooIC4Wf%F%}d5HY&js&BBh5@ zI-k;olrBPZe~Ar2a9iAd$!HKwvz9RMR7xk?D(N3pZ1!u-s*F(zz{J?^pkdB?&8m!1 z3Z=;2((LfIRTj}Jbi4@Mlr~vl2Yf|-!_-O2N{e#l?@Ep)}*&B(7j5=mn+gYoRc;UN-@g%bHk#zRZBwVG%QVAtIgJ2Ye%kL(=i6$cOQ~`4 znCr5?K>Y>c1J^%6>LC8f3Ci5UNt%)qzs0N^Q^!*(=9_DbBI}KeGWTVk7H~Z{zyc1i z11!iKkf&?=z@LVn6^H0G1oqoOd;=GDazsqTF@O`A3?GqQUgbg>?y=_KJ=QN+Y7>$xL510^2RdXb9<*Sf^U-ji(GsB?^uDHq|MVt zTVNiF+4(b&{3)BZIx5_VTI?KRT-lcN>Sm7;b)rp;Ed8KdE*GU Mgjr)-0@RHC05_HD&Hw-a literal 0 HcmV?d00001 diff --git a/avr/cores/megacommand/Design/menu_scratch.xcf b/avr/cores/megacommand/Design/menu_scratch.xcf new file mode 100644 index 0000000000000000000000000000000000000000..37351ff93b105bb66fc0e25a54b72836201e48ad GIT binary patch literal 10657 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 Date: Wed, 16 Oct 2019 20:44:15 +0800 Subject: [PATCH 045/469] update design scratchpads --- avr/cores/megacommand/Design/menu_scratch.xcf | Bin 10657 -> 15202 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/avr/cores/megacommand/Design/menu_scratch.xcf b/avr/cores/megacommand/Design/menu_scratch.xcf index 37351ff93b105bb66fc0e25a54b72836201e48ad..c14fb8e14419dcae93e1aa2160d50abe7917ccc8 100644 GIT binary patch delta 3968 zcmeHKJ8u&~5Z>E!&Nzt;j;+`^Tb8Z(5s;$>@F(>q>s@Ty=B;VE& zC`hqKdSMyqd11v09K#QMN>IPZ69JwY!l{Bs{BwXy8l;+mi<#y);0HL6cf*+Hbx=O4 z+$pmle=O*c=T51&=YN#B2l*dY?tkW=HJfZ$jS3q(kFcfo@twL+fyTdXORv-34EApr z^NtU5I@5e2I$3S;Lbg0}lq_?M6i$9SP&Mf6EMzETr{$O4^H82I15TMTy$W0eSbza;0JrbnTk)@@%9ik#Q!kRHUqG&Zxl*_h z=u?W7FbN@{tZM&l37xTk?#BKOD@>$PCbQU&F zd)<~|P~{<0I8J+1*`+S-Fa}JK^+BEHSz?!rtCOH3kma>miXJxc<~&TtlAJiri5oqh zXY1Jo@T0VgBn%-K^0P%P3w)sp+5-v6CPT#V@+K;qtm0ugRym@Ip@3shqvk-#&=O*v zZm*i+;$(YB;iondG)yVJ=@g+rI`kkIcI0qI8%EGX1?D^At5$0AnSeUQzZsaNZ-^GH zpVRb~_{O}FTg^Grxa`N|d~Y|=0_tPb+fBXQ)T>QLQkxe4x;BY2+R!Dglh!B2GDh#r1mF2sTZz)TNRd4GH2R+WgnbbdQW-cb$*IZB`D% zJXx*53N^9`Fa`mW7*7MJMiXeuQitY1oQDWKimNolI~oL0?V4hJ{YSbQC_Ac3{IXvH=|ukEvrckbqv3go+{{gm_Po2IaBLv I&vRSx-*Ow;i~s-t delta 136 zcmaD Date: Thu, 17 Oct 2019 17:09:03 +1100 Subject: [PATCH 046/469] Encoder 1 is now input select. Link mode is set in aux menu. Inverted wheel colors. Added png images. --- art/wheel1.png | Bin 0 -> 270 bytes art/wheel2.png | Bin 0 -> 277 bytes art/wheel3.png | Bin 0 -> 297 bytes avr/cores/megacommand/MCL/MCLMenus.h | 15 +- avr/cores/megacommand/MCL/MCLSysConfig.cpp | 2 +- avr/cores/megacommand/MCL/RAMPage.cpp | 215 ++++++++++++++++----- avr/cores/megacommand/MCL/RAMPage.h | 54 +++--- 7 files changed, 214 insertions(+), 72 deletions(-) create mode 100644 art/wheel1.png create mode 100644 art/wheel2.png create mode 100644 art/wheel3.png diff --git a/art/wheel1.png b/art/wheel1.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c0c1718768fd5ee8fcb693b1c5ce64fb4589d9 GIT binary patch literal 270 zcmV+p0rCEcP)Px#$Vo&&R5%f>lK~FHAPfWPwEus(T~MzwNnkkWRl4t65eVl#G_0?!=-G`6}sTU}|DhxlBT(2I=6){`& zn%@VHLOCr9=^=iNpL}?wXkesz5Yy2o*L@}q!kxx^ZPx#&q+il7R}tFbG7`(f|MDtrlK5jV4*QLQzle#XyeM+WY$d`kf2fG3l4f zm?>+{VDX&6|B4e=y$b|3%tz=jkQ{0fK%(<0)HMoNCJ$hRW`;}yqCKYpeYz!J{CCtR zrKd53sfy{GLePx#<4Ht8R5%f>l7SAxAP7Y5vj6|(O^nZ=98QxpX%M)3QteYp`PBEX&-(}M+4R$4 z%%qw-*t$>fzg9)f0S2+qS_%DVuTTU4iNlaA@U^yw(l`aJeEh{jO{9RTwgRUdD7z3R z(M8hy>X!n1I-%F4PzD1|Zf0NeFf=Xl`BPCO009w&w1kV2qFS^G0h`88Z29!4GezL1 zF~|xc_qyg)&>&)$Bmy68&J+-F2rd`R4-{|MwKh}@y^J2pl-H0>#vg8<24JF`$QALm vl9{GL7oNgQ9{d@!v$y9NVBVBA5#7cw;DjM9tdn1+00000NkvXXu0mjf2D5%u literal 0 HcmV?d00001 diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 21e5b9dfd..74f83868f 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -22,6 +22,7 @@ extern MCLEncoder config_param3; extern MCLEncoder config_param4; extern MCLEncoder config_param5; extern MCLEncoder config_param6; +extern MCLEncoder config_param7; extern MenuPage system_page; extern MenuPage midi_config_page; @@ -29,6 +30,7 @@ extern MenuPage md_config_page; extern MenuPage mcl_config_page; extern MenuPage chain_config_page; extern MenuPage aux_config_page; +extern MenuPage ram_config_page; extern MCLEncoder input_encoder1; extern MCLEncoder input_encoder2; @@ -54,11 +56,22 @@ const menu_t auxconfig_menu_layout PROGMEM = { "AUX PAGES", 1, { - {"RAM Page" ,0, 0, 0, (uint8_t *) NULL, (Page*) &load_proj_page, (void*)NULL, {}}, + {"RAM Page" ,0, 0, 0, (uint8_t *) NULL, (Page*) &ram_config_page, (void*)NULL, {}}, }, (void*) NULL, }; +const menu_t rampage1_menu_layout PROGMEM = { + "RAM PAGE", + 1, + { + {"LINK:", 0, 2, 2, (uint8_t *) &mcl_cfg.ram_page_mode, (Page*) NULL, (void*)NULL, {{0, "MONO"},{1, "STEREO"}}}, + }, + + (void*) NULL, + +}; + const menu_t midiconfig_menu_layout PROGMEM = { "MIDI", diff --git a/avr/cores/megacommand/MCL/MCLSysConfig.cpp b/avr/cores/megacommand/MCL/MCLSysConfig.cpp index 79e47cb87..6aec2a7b6 100644 --- a/avr/cores/megacommand/MCL/MCLSysConfig.cpp +++ b/avr/cores/megacommand/MCL/MCLSysConfig.cpp @@ -93,7 +93,7 @@ bool MCLSysConfig::cfg_init() { chain_mode = 2; chain_rand_min = 0; chain_rand_max = 1; - + ram_page_mode = 0; cfgfile.close(); ret = write_cfg(); diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index b99a2d2ac..0e62a56fb 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -8,9 +8,14 @@ #define MONO 0 #define LINK 1 +#define SOURCE_MAIN 0 +#define SOURCE_INPA 1 +#define SOURCE_INPB 2 + +#define SOURCE_INP 1 + void RAMPage::setup() { DEBUG_PRINT_FN(); - encoders[0]->cur = MONO; encoders[3]->cur = 4; } @@ -21,8 +26,12 @@ void RAMPage::init() { oled_display.clearDisplay(); oled_display.setFont(); #endif - encoders[0]->cur = MONO; md_exploit.off(); + if (mcl_cfg.ram_page_mode == MONO) { + ((MCLEncoder *)encoders[0])->max = 2; + } else { + ((MCLEncoder *)encoders[0])->max = 1; + } if (page_id == 0) { setup_callbacks(); } @@ -43,9 +52,9 @@ void RAMPage::setup_sequencer(uint8_t track) { CLEAR_LOCK(); } -void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, - uint8_t len, uint8_t rate, uint8_t pan, - uint8_t linked_track) { +void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t lev, + uint8_t source, uint8_t len, uint8_t rate, + uint8_t pan, uint8_t linked_track) { MDTrackLight md_track; memset(&(md_track.seq_data), 0, sizeof(MDSeqTrackData)); @@ -55,9 +64,19 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, rec_state = STATE_QUEUE; md_track.active = MD_TRACK_TYPE; md_track.machine.model = model; - md_track.machine.params[RAM_R_MLEV] = mlev; - md_track.machine.params[RAM_R_MBAL] = pan; - md_track.machine.params[RAM_R_ILEV] = 0; + + if (source == SOURCE_MAIN) { + md_track.machine.params[RAM_R_MLEV] = lev; + md_track.machine.params[RAM_R_MBAL] = pan; + md_track.machine.params[RAM_R_ILEV] = 0; + } + + if (source >= SOURCE_INPA) { + md_track.machine.params[RAM_R_MLEV] = 0; + md_track.machine.params[RAM_R_IBAL] = pan; + md_track.machine.params[RAM_R_ILEV] = lev; + } + md_track.machine.params[RAM_R_LEN] = encoders[3]->cur * 16; if (md_track.machine.params[RAM_R_LEN] > 127) { md_track.machine.params[RAM_R_LEN] = 127; @@ -124,7 +143,9 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, mcl_actions.transition_level[track] = TRANSITION_UNMUTE; mcl_actions.calc_next_transition(); - + DEBUG_PRINTLN("my next step"); + DEBUG_PRINTLN(next_step); + DEBUG_PRINTLN(mcl_actions.next_transition); EmptyTrack empty_track; mcl_actions.calc_latency(&empty_track); } @@ -355,22 +376,35 @@ void RAMPage::setup_ram_play_stereo(uint8_t track) { setup_ram_play(track + 1, RAM_P2_MODEL, 127, track); } -void RAMPage::setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, - uint8_t rate) { +void RAMPage::setup_ram_rec_mono(uint8_t track, uint8_t lev, uint8_t source, + uint8_t len, uint8_t rate) { uint8_t model = RAM_R1_MODEL; if (page_id == 1) { model = RAM_R2_MODEL; } - setup_ram_rec(track, model, mlev, len, rate, 63); + uint8_t pan = 63; + if (source == SOURCE_INPA) { + pan = 0; + } + if (source == SOURCE_INPB) { + pan = 127; + } + setup_ram_rec(track, model, lev, source, len, rate, pan); } -void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, - uint8_t rate) { +void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t lev, uint8_t source, + uint8_t len, uint8_t rate) { if (track == 15) { return; } - setup_ram_rec(track, RAM_R1_MODEL, mlev, len, rate, 0, track + 1); - setup_ram_rec(track + 1, RAM_R2_MODEL, mlev, len, rate, 127, track); + + setup_ram_rec(track, RAM_R1_MODEL, lev, source, len, rate, 0, track + 1); + uint8_t source_link_track = source; + if (source >= SOURCE_INPA) { + uint8_t source_link_track = SOURCE_INPB; + } + setup_ram_rec(track + 1, RAM_R2_MODEL, lev, source_link_track, len, rate, 127, + track); } void RAMPage::loop() { @@ -402,7 +436,7 @@ void RAMPage::display() { GUI.put_string_at(0, "RAM"); GUI.put_value_at1(4, page_id + 1); - ≈ switch (rec_state) { + switch (rec_state) { case STATE_QUEUE: GUI.put_string_at(6, "[Queue]"); break; @@ -415,13 +449,51 @@ void RAMPage::display() { } GUI.setLine(GUI.LINE2); - - if (encoders[0]->cur == 0) { - GUI.put_string_at(0, "MON"); - } else { - GUI.put_string_at(0, "LNK"); + /* + if (mcl_cfg.ram_page_mode == 0) { + GUI.put_string_at(0, "MON"); + } else { + GUI.put_string_at(0, "LNK"); + } + */ + switch (encoders[0]->cur) { + case SOURCE_MAIN: + if (mcl_cfg.ram_page_mode == LINK) { + if (page_id == 0) { + GUI.put_string_at(0, "L"); + } + if (page_id == 1) { + GUI.put_string_at(0, "R"); + } + } else { + GUI.put_string_at(0, "M"); + } + break; + case SOURCE_INPA: + if (mcl_cfg.ram_page_mode == LINK) { + if (page_id == 0) { + GUI.put_string_at(0, "IA"); + } + if (page_id == 1) { + GUI.put_string_at(0, "IB"); + } + } else { + GUI.put_string_at(0, "IA"); + } + break; + case SOURCE_INPB: + if (mcl_cfg.ram_page_mode == LINK) { + if (page_id == 0) { + GUI.put_string_at(0, "IA"); + } + if (page_id == 1) { + GUI.put_string_at(0, "IB"); + } + } else { + GUI.put_string_at(0, "IB"); + } + break; } - GUI.put_value_at1(4, encoders[1]->cur); GUI.put_string_at(6, "S:"); GUI.put_value_at2(8, 1 << encoders[2]->cur); @@ -431,11 +503,11 @@ void RAMPage::display() { #ifdef OLED_DISPLAY float remain; if (rec_state != STATE_NOSTATE) { - if (transition_step > MidiClock.div16th_counter) { + if (MidiClock.clock_less_than(transition_step + record_len, + MidiClock.div16th_counter)) { - remain = - ((float)(transition_step) / - (float)MidiClock.div16th_counter); + remain = ((float)(MidiClock.div16th_counter) / + (float)(transition_step + record_len)); } // else if (rec_state == STATE_RECORD{ else { @@ -443,8 +515,10 @@ void RAMPage::display() { remain = (float)mcl_seq.md_tracks[n].step_count / (float)mcl_seq.md_tracks[n].length; } - oled_display.fillRect(0, 28, remain * 50, 4, WHITE); + oled_display.drawRect(100, 28, 20, 4, WHITE); + oled_display.fillRect(100, 28, remain * 20, 4, WHITE); } + oled_display.setFont(); oled_display.setCursor(0, 0); oled_display.print("RAM"); @@ -461,24 +535,66 @@ void RAMPage::display() { break; } - oled_display.setCursor(0, 15); - if (encoders[0]->cur == 0) { - oled_display.print("MON "); + oled_display.setFont(&TomThumb); + + oled_display.setCursor(0, 16); + if (mcl_cfg.ram_page_mode == 0) { + oled_display.print("MONO "); } else { - oled_display.print("LNK "); + oled_display.print("STEREO "); + } + oled_display.setFont(); + oled_display.setCursor(0, 20); + switch (encoders[0]->cur) { + case SOURCE_MAIN: + if (mcl_cfg.ram_page_mode == LINK) { + if (page_id == 0) { + oled_display.print("LEFT "); + } + if (page_id == 1) { + oled_display.print("RIGHT "); + } + } else { + oled_display.print("MAIN "); + } + break; + case SOURCE_INPA: + if (mcl_cfg.ram_page_mode == LINK) { + if (page_id == 0) { + oled_display.print("INPUTA "); + } + if (page_id == 1) { + oled_display.print("INPUTB "); + } + } else { + oled_display.print("INPUTA "); + } + break; + case SOURCE_INPB: + if (mcl_cfg.ram_page_mode == LINK) { + if (page_id == 0) { + oled_display.print("INPUTA "); + } + if (page_id == 1) { + oled_display.print("INPUTB "); + } + } else { + oled_display.print("INPUTB "); + } + break; } oled_display.print(encoders[1]->cur); oled_display.print(" S:"); oled_display.print(1 << encoders[2]->cur); - oled_display.print(" Q:"); + oled_display.print(" LEN:"); oled_display.print(encoders[3]->cur * 4); - uint8_t w_x = 100, w_y = 4; + uint8_t w_x = 100, w_y = 0; oled_display.drawPixel(w_x + 21, w_y + 24, WHITE); oled_display.drawCircle(w_x + 21, w_y + 24, 2, WHITE); - oled_display.drawLine(w_x + 21, w_y + 9, w_x + 23, +w_y + 23, WHITE); - oled_display.drawLine(w_x + 9, w_y + 21, w_x + 21, +w_y + 26, WHITE); + oled_display.drawLine(w_x + 21, w_y + 9, w_x + 23, w_y + 23, WHITE); + oled_display.drawLine(w_x + 9, w_y + 21, w_x + 21, w_y + 26, WHITE); switch (wheel_spin) { case 0: @@ -534,7 +650,7 @@ void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { // to all polyphonic tracks uint8_t param_true = 0; - if (encoders[0]->cur == MONO) { + if (mcl_cfg.ram_page_mode == MONO) { return; } MD.parseCC(channel, param, &track, &track_param); @@ -605,20 +721,33 @@ bool RAMPage::handleEvent(gui_event_t *event) { GUI.setPage(&grid_page); } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { - if (encoders[0]->cur == 0) { + if (mcl_cfg.ram_page_mode == MONO) { + uint8_t lev = 64 - 32; + if (encoders[0]->cur == SOURCE_MAIN) { + lev = 64; + } if (page_id == 0) { - setup_ram_rec_mono(14, 64, 4 * encoders[3]->cur - 1, 128); + setup_ram_rec_mono(14, lev, encoders[0]->cur, 4 * encoders[3]->cur - 1, + 128); } else { - setup_ram_rec_mono(15, 64, 4 * encoders[3]->cur - 1, 128); + setup_ram_rec_mono(15, lev, encoders[0]->cur, 4 * encoders[3]->cur - 1, + 128); } } else { - setup_ram_rec_stereo(14, 64 - 16, 4 * encoders[3]->cur - 1, 128); + if (encoders[0]->cur == SOURCE_MAIN) { + setup_ram_rec_stereo(14, 64 - 16, encoders[0]->cur, + 4 * encoders[3]->cur - 1, 128); + } + if (encoders[0]->cur == SOURCE_INP) { + setup_ram_rec_stereo(14, 64 - 32, encoders[0]->cur, + 4 * encoders[3]->cur - 1, 128); + } } } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { magic = 1; - if (encoders[0]->cur == MONO) { + if (mcl_cfg.ram_page_mode == MONO) { slice(14 + page_id, 255); } else { slice(14, 15); @@ -628,7 +757,7 @@ bool RAMPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON4)) { magic = 0; - if (encoders[0]->cur == MONO) { + if (mcl_cfg.ram_page_mode == MONO) { if (!slice(14 + page_id, 255)) { setup_ram_play_mono(14 + page_id); } diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index 6884efd29..3baa13f69 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -9,30 +9,27 @@ #define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 #define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 -// 'wheel top', 21x21px -const unsigned char wheel_top[] PROGMEM = { - 0x01, 0xfc, 0x00, 0x06, 0x03, 0x00, 0x08, 0xf8, 0x80, 0x10, 0xf8, - 0x40, 0x20, 0xf8, 0x20, 0x40, 0x70, 0x10, 0x40, 0x70, 0x10, 0x80, - 0x20, 0x08, 0x80, 0x00, 0x08, 0x80, 0x20, 0x08, 0x80, 0x50, 0x08, - 0x80, 0x20, 0x08, 0x83, 0x8e, 0x08, 0xcf, 0x8f, 0x88, 0x5f, 0x8f, - 0xd0, 0x4f, 0x07, 0x90, 0x27, 0x07, 0x20, 0x12, 0x04, 0x40, 0x08, - 0x00, 0x80, 0x06, 0x03, 0x00, 0x01, 0xfc, 0x00}; -// 'wheel angle', 21x21px -const unsigned char wheel_angle[] PROGMEM = { - 0x01, 0xfc, 0x00, 0x06, 0x03, 0x00, 0x08, 0x00, 0x80, 0x10, 0x02, - 0x40, 0x20, 0x07, 0x20, 0x40, 0x07, 0x90, 0x40, 0x0f, 0xd0, 0x80, - 0x0f, 0x88, 0xb8, 0x0e, 0x08, 0xbe, 0x20, 0x08, 0xbf, 0x50, 0x08, - 0xbe, 0x20, 0x08, 0xb8, 0x00, 0x08, 0x80, 0x20, 0x08, 0x40, 0x70, - 0x10, 0x40, 0x70, 0x10, 0x20, 0xf8, 0x20, 0x10, 0xf8, 0x40, 0x08, - 0xf8, 0x80, 0x06, 0x03, 0x00, 0x01, 0xfc, 0x00}; -// 'wheel side', 21x21px -const unsigned char wheel_side[] PROGMEM = { - 0x01, 0xfc, 0x00, 0x06, 0x03, 0x00, 0x08, 0x00, 0x80, 0x12, 0x00, - 0x40, 0x27, 0x00, 0x20, 0x4f, 0x00, 0x10, 0x5f, 0x80, 0x10, 0x8f, - 0x80, 0x08, 0x83, 0x80, 0xe8, 0x80, 0x23, 0xe8, 0x80, 0x57, 0xe8, - 0x80, 0x23, 0xe8, 0x83, 0x80, 0xe8, 0x8f, 0x80, 0x08, 0x5f, 0x80, - 0x10, 0x4f, 0x00, 0x10, 0x27, 0x00, 0x20, 0x12, 0x00, 0x40, 0x08, - 0x00, 0x80, 0x06, 0x03, 0x00, 0x01, 0xfc, 0x00}; +// 'wheel1', 21x21px +const unsigned char wheel_top [] PROGMEM = { + 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x07, 0x07, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x07, 0xc0, 0x3f, + 0x8f, 0xe0, 0x3f, 0x8f, 0xe0, 0x7f, 0xdf, 0xf0, 0x7f, 0xff, 0xf0, 0x7f, 0xdf, 0xf0, 0x7f, 0xaf, + 0xf0, 0x7f, 0xdf, 0xf0, 0x7c, 0x71, 0xf0, 0x30, 0x70, 0x70, 0x20, 0x70, 0x20, 0x30, 0xf8, 0x60, + 0x18, 0xf8, 0xc0, 0x0d, 0xfb, 0x80, 0x07, 0xff, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00 +}; +// 'wheel2', 21x21px +const unsigned char wheel_angle [] PROGMEM = { + 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x07, 0xff, 0x00, 0x0f, 0xfd, 0x80, 0x1f, 0xf8, 0xc0, 0x3f, + 0xf8, 0x60, 0x3f, 0xf0, 0x20, 0x7f, 0xf0, 0x70, 0x47, 0xf1, 0xf0, 0x41, 0xdf, 0xf0, 0x40, 0xaf, + 0xf0, 0x41, 0xdf, 0xf0, 0x47, 0xff, 0xf0, 0x7f, 0xdf, 0xf0, 0x3f, 0x8f, 0xe0, 0x3f, 0x8f, 0xe0, + 0x1f, 0x07, 0xc0, 0x0f, 0x07, 0x80, 0x07, 0x07, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00 +}; +// 'wheel3', 21x21px +const unsigned char wheel_side [] PROGMEM = { + 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x07, 0xff, 0x00, 0x0d, 0xff, 0x80, 0x18, 0xff, 0xc0, 0x30, + 0xff, 0xe0, 0x20, 0x7f, 0xe0, 0x70, 0x7f, 0xf0, 0x7c, 0x7f, 0x10, 0x7f, 0xdc, 0x10, 0x7f, 0xa8, + 0x10, 0x7f, 0xdc, 0x10, 0x7c, 0x7f, 0x10, 0x70, 0x7f, 0xf0, 0x20, 0x7f, 0xe0, 0x30, 0xff, 0xe0, + 0x18, 0xff, 0xc0, 0x0d, 0xff, 0x80, 0x07, 0xff, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00 +}; class RAMPage : public LightPage, MidiCallback { public: @@ -48,6 +45,9 @@ class RAMPage : public LightPage, MidiCallback { bool handleEvent(gui_event_t *event); bool midi_state = false; + + uint8_t record_mode; + uint8_t magic; uint8_t rec_state; uint8_t track1; @@ -63,11 +63,11 @@ class RAMPage : public LightPage, MidiCallback { void init(); void loop(); void cleanup(); - void setup_ram_rec(uint8_t track, uint8_t model, uint8_t mlev, uint8_t len, + void setup_ram_rec(uint8_t track, uint8_t model, uint8_t lev, uint8_t source, uint8_t len, uint8_t rate, uint8_t pan, uint8_t linked_track = 255); - void setup_ram_rec_mono(uint8_t track, uint8_t mlev, uint8_t len, + void setup_ram_rec_mono(uint8_t track, uint8_t lev, uint8_t source, uint8_t len, uint8_t rate); - void setup_ram_rec_stereo(uint8_t track, uint8_t mlev, uint8_t len, + void setup_ram_rec_stereo(uint8_t track, uint8_t lev, uint8_t source, uint8_t len, uint8_t rate); void setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, uint8_t linked_track = 255); From 98150d86e24571b17e6ec4907feb831d75ce0a02 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 17 Oct 2019 18:23:02 +1100 Subject: [PATCH 047/469] Fix gfx and labeling --- art/wheel1.png | Bin 270 -> 245 bytes art/wheel2.png | Bin 277 -> 260 bytes art/wheel3.png | Bin 297 -> 265 bytes avr/cores/megacommand/MCL/RAMPage.cpp | 87 ++++++------ avr/cores/megacommand/MCL/RAMPage.d | 186 ++++++++++++++++++++++++++ avr/cores/megacommand/MCL/RAMPage.h | 30 ++--- avr/cores/megacommand/MCL/RAMPage.o | Bin 0 -> 132188 bytes 7 files changed, 245 insertions(+), 58 deletions(-) create mode 100644 avr/cores/megacommand/MCL/RAMPage.d create mode 100644 avr/cores/megacommand/MCL/RAMPage.o diff --git a/art/wheel1.png b/art/wheel1.png index f0c0c1718768fd5ee8fcb693b1c5ce64fb4589d9..6ec20fd68239cef265adbd08a7eabd7f3e3eb4e7 100644 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S1|*9D%+3HQ#^NA%Cx&(BWL^R}>pfi@Lo5Wh zPP)x^Sb@j+<-h;)ce5Uyn5?SFpS`r}(dkC6bz86ftzP|nmCN;0eWF(qI;xIk{q0}h z(%5`qnc-8(X+50^abFm?7jo~id&VGqZ1?3?3io0^PWiWeE@R8b66V}@99Lvbr(0fr zSuI{>YLRf?CV%^@ol~Ax12rniRhb|DQ9dEu_xUt#UV{sV8Qvzk)O}~%+{YxirsCN_ t_k3={k77=uE?ci`O%l?ubv%7ed_kd%X#UGdK0v24c)I$ztaD0e0svzYW&HpE literal 270 zcmV+p0rCEcP)Px#$Vo&&R5%f>lK~FHAPfWPwEus(T~MzwNnkkWRl4t65eVl#G_0?!=-G`6}sTU}|DhxlBT(2I=6){`& zn%@VHLOCr9=^=iNpL}?wXkesz5Yy2o*L@}q!kxx^Z zPGaOcY{0>MJM;hlBJ));Ml73hqLyeKyx~6en@!*2D*LNe+JzaZ$C!3*Y)Yv+VHZ>` z&Z6=|R7@(fV_L%vTlO=ozPWRl1#Y;#+a?gUKl}c^iR`VPx#&q+il7R}tFbG7`(f|MDtrlK5jV4*QLQzle#XyeM+WY$d`kf2fG3l4f zm?>+{VDX&6|B4e=y$b|3%tz=jkQ{0fK%(<0)HMoNCJ$hRW`;}yqCKYpeYz!J{CCtR zrKd53sfy{GLePx#!%0LzR5%fpk^v6GAPfUx|NnEhL|kP|Ky1>KVa^BY+FNVS?^}I(6^^XzM8P*s z68BHtkL@CwAY*h1pP99l220)#}BuE1;!LsuOEwdsO*`g!E P00000NkvXXu0mjf95Zbg literal 297 zcmV+^0oMMBP)Px#<4Ht8R5%f>l7SAxAP7Y5vj6|(O^nZ=98QxpX%M)3QteYp`PBEX&-(}M+4R$4 z%%qw-*t$>fzg9)f0S2+qS_%DVuTTU4iNlaA@U^yw(l`aJeEh{jO{9RTwgRUdD7z3R z(M8hy>X!n1I-%F4PzD1|Zf0NeFf=Xl`BPCO009w&w1kV2qFS^G0h`88Z29!4GezL1 zF~|xc_qyg)&>&)$Bmy68&J+-F2rd`R4-{|MwKh}@y^J2pl-H0>#vg8<24JF`$QALm vl9{GL7oNgQ9{d@!v$y9NVBVBA5#7cw;DjM9tdn1+00000NkvXXu0mjf2D5%u diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 0e62a56fb..d233b01bf 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -456,41 +456,41 @@ void RAMPage::display() { GUI.put_string_at(0, "LNK"); } */ - switch (encoders[0]->cur) { + + switch (encoders[0]->cur) { case SOURCE_MAIN: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - GUI.put_string_at(0, "L"); + GUI.put_string_at(0,"L"); } if (page_id == 1) { - GUI.put_string_at(0, "R"); + GUI.put_string_at(0,"R"); } } else { - GUI.put_string_at(0, "M"); + GUI.put_string_at(0,"M"); } break; case SOURCE_INPA: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - GUI.put_string_at(0, "IA"); - } - if (page_id == 1) { - GUI.put_string_at(0, "IB"); + GUI.put_string_at(0,"INA"); + } else { + GUI.put_string_at(0,"INB"); } } else { - GUI.put_string_at(0, "IA"); + GUI.put_string_at(0,"INA"); } + break; case SOURCE_INPB: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - GUI.put_string_at(0, "IA"); - } - if (page_id == 1) { - GUI.put_string_at(0, "IB"); + GUI.put_string_at(0,"INA"); + } else { + GUI.put_string_at(0,"INB"); } } else { - GUI.put_string_at(0, "IB"); + GUI.put_string_at(0,"INB"); } break; } @@ -502,7 +502,8 @@ void RAMPage::display() { #endif #ifdef OLED_DISPLAY float remain; - if (rec_state != STATE_NOSTATE) { + oled_display.drawRoundRect(105, 28, 20, 4 , 1, WHITE); + if ((rec_state != STATE_NOSTATE)) { if (MidiClock.clock_less_than(transition_step + record_len, MidiClock.div16th_counter)) { @@ -515,8 +516,9 @@ void RAMPage::display() { remain = (float)mcl_seq.md_tracks[n].step_count / (float)mcl_seq.md_tracks[n].length; } - oled_display.drawRect(100, 28, 20, 4, WHITE); - oled_display.fillRect(100, 28, remain * 20, 4, WHITE); + uint8_t width = remain * 18; + if (width <= 3) { width = 3; } + oled_display.fillRoundRect(105, 28, width, 4, 1, WHITE); } oled_display.setFont(); oled_display.setCursor(0, 0); @@ -549,37 +551,36 @@ void RAMPage::display() { case SOURCE_MAIN: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - oled_display.print("LEFT "); + oled_display.print("LEFT "); } if (page_id == 1) { - oled_display.print("RIGHT "); + oled_display.print("RIGHT"); } } else { - oled_display.print("MAIN "); + oled_display.print("MAIN "); } break; case SOURCE_INPA: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - oled_display.print("INPUTA "); - } - if (page_id == 1) { - oled_display.print("INPUTB "); + oled_display.print("INPA "); + } else { + oled_display.print("INPB "); } } else { - oled_display.print("INPUTA "); + oled_display.print("INPA"); } + break; case SOURCE_INPB: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - oled_display.print("INPUTA "); - } - if (page_id == 1) { - oled_display.print("INPUTB "); + oled_display.print("INPA "); + } else { + oled_display.print("INPB "); } } else { - oled_display.print("INPUTB "); + oled_display.print("INPB"); } break; } @@ -590,36 +591,36 @@ void RAMPage::display() { oled_display.print(" LEN:"); oled_display.print(encoders[3]->cur * 4); - uint8_t w_x = 100, w_y = 0; - oled_display.drawPixel(w_x + 21, w_y + 24, WHITE); - oled_display.drawCircle(w_x + 21, w_y + 24, 2, WHITE); - oled_display.drawLine(w_x + 21, w_y + 9, w_x + 23, w_y + 23, WHITE); - oled_display.drawLine(w_x + 9, w_y + 21, w_x + 21, w_y + 26, WHITE); + uint8_t w_x = 104, w_y = 0; + oled_display.drawPixel(w_x + 19, w_y + 24, WHITE); + oled_display.drawCircle(w_x + 19, w_y + 24, 2, WHITE); + oled_display.drawLine(w_x + 18, w_y + 9, w_x + 21, w_y + 23, WHITE); + oled_display.drawLine(w_x + 7, w_y + 19, w_x + 19, w_y + 26, WHITE); switch (wheel_spin) { case 0: - oled_display.drawBitmap(w_x, w_y, wheel_top, 21, 21, WHITE); + oled_display.drawBitmap(w_x, w_y, wheel_top, 19, 19, WHITE); break; case 1: - oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE); + oled_display.drawBitmap(w_x, w_y, wheel_angle, 19, 19, WHITE); break; case 2: - oled_display.drawBitmap(w_x, w_y, wheel_side, 21, 21, WHITE); + oled_display.drawBitmap(w_x, w_y, wheel_side, 19, 19, WHITE); break; case 3: - oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE, false, true); + oled_display.drawBitmap(w_x, w_y, wheel_angle, 19, 19, WHITE, false, true); break; case 4: - oled_display.drawBitmap(w_x, w_y, wheel_top, 21, 21, WHITE, false, true); + oled_display.drawBitmap(w_x, w_y, wheel_top, 19, 19, WHITE, false, true); break; case 5: - oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE, true, true); + oled_display.drawBitmap(w_x, w_y, wheel_angle, 19, 19, WHITE, true, true); break; case 6: - oled_display.drawBitmap(w_x, w_y, wheel_side, 21, 21, WHITE, true, false); + oled_display.drawBitmap(w_x, w_y, wheel_side, 19, 19, WHITE, true, false); break; case 7: - oled_display.drawBitmap(w_x, w_y, wheel_angle, 21, 21, WHITE, true, false); + oled_display.drawBitmap(w_x, w_y, wheel_angle, 19, 19, WHITE, true, false); break; } if ((wheel_spin_last_clock != MidiClock.div16th_counter) && diff --git a/avr/cores/megacommand/MCL/RAMPage.d b/avr/cores/megacommand/MCL/RAMPage.d new file mode 100644 index 000000000..3aeaedbf7 --- /dev/null +++ b/avr/cores/megacommand/MCL/RAMPage.d @@ -0,0 +1,186 @@ +/Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RAMPage.o: \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RAMPage.cpp \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCL.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/midi-common.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiNotes.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/WProgram.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Core.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/wiring_private.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Arduino.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/binary.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/WCharacter.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/WString.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/HardwareSerial.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Stream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Print.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Printable.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/variants/mega/pins_arduino.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/helpers.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/BitArray.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/helpers.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Task.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/helpers.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/mididuino_private.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/memory.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/LCD.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/LCDParent.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/OLED.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Arduino.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Print.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/gfxfont.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI_private.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MidiUart.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiUartParent.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Callback.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Vector.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiID.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/RingBuffer.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiClock.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/midi-common.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Stack.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/GUI.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Task.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Encoders.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI_private.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/RecordingEncoder.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Events.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Pages.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/ModalGui.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/GUI.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Sketch.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/Midi.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/ListPool.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSysex.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MidiUart.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/WMath.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/Elektron.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/MNMDataEncoder.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/DataEncoder.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/DataEncoderUnchecking.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/ElektronDataEncoder.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Messages.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Pattern.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/ElektronPattern.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Params.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Sysex.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSysex.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MD.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDEncoders.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDParams.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDMessages.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDPattern.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDSysex.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MD.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDTask.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLGfx.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLSd.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdFat.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SysCall.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SPI/SPI.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/BlockDriver.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/BaseBlockDriver.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatLibConfig.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdFatConfig.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SysCall.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/SdInfo.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/../FatLib/BaseBlockDriver.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/../SpiDriver/SdSpiDriver.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/../SpiDriver/SdSpiBaseDriver.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatLib.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ArduinoFiles.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatFile.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatApiConstants.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatStructs.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatVolume.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/BlockDriver.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ArduinoStream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/bufstream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/iostream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/istream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ios.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ostream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatFileSystem.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/StdioStream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/fstream.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/SdioCard.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLSysConfig.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/PolyPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Project.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/ProjectPages.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLEncoder.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Encoders.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/NewProjectPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/LoadProjectPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/FileBrowserPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SoundBrowserPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Grid.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridPages.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridEncoder.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridRowHeader.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridSavePage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridIOPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridWritePage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Menu.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MenuPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridChain.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridTrack.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/A4Track.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/ExtTrack.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/ExtSeqTrack.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDTrack.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Bank1Object.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLMemory.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDSeqTrack.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDSeqTrackData.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridTask.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/EmptyTrack.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/LoudnessPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLActions.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLActionsEvents.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLGUI.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/TextInputPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLSeq.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqPages.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLMenus.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqParamPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqPtcPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MidiActivePeering.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiID.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MidiTools/Scales.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqRlckPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqRtrkPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqStepPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqExtStepPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDExploit.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDSound.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiIDSysex.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/Midi.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSDS.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Wav.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSDSSysex.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSDSMessages.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSds.hh \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MidiSetup.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/NoteInterface.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/TurboLight.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/AuxPages.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MixerPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RoutePage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RAMPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Osc.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/OscMixerPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/OscPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Wav.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/WavDesigner.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/DSP.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/PageSelectPage.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Shared.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.h \ + /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/Elektrothic.h diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index 3baa13f69..15fa8801e 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -9,26 +9,26 @@ #define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 #define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 -// 'wheel1', 21x21px +// 'wheel1', 19x19px const unsigned char wheel_top [] PROGMEM = { - 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x07, 0x07, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x07, 0xc0, 0x3f, - 0x8f, 0xe0, 0x3f, 0x8f, 0xe0, 0x7f, 0xdf, 0xf0, 0x7f, 0xff, 0xf0, 0x7f, 0xdf, 0xf0, 0x7f, 0xaf, - 0xf0, 0x7f, 0xdf, 0xf0, 0x7c, 0x71, 0xf0, 0x30, 0x70, 0x70, 0x20, 0x70, 0x20, 0x30, 0xf8, 0x60, - 0x18, 0xf8, 0xc0, 0x0d, 0xfb, 0x80, 0x07, 0xff, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00 + 0x03, 0xf8, 0x00, 0x0e, 0x0e, 0x00, 0x1e, 0x0f, 0x00, 0x3e, 0x0f, 0x80, 0x7f, 0x1f, 0xc0, 0x7f, + 0x1f, 0xc0, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0xff, 0x5f, 0xe0, 0xff, 0xbf, + 0xe0, 0xf8, 0xe3, 0xe0, 0x60, 0xe0, 0xe0, 0x40, 0xe0, 0x40, 0x61, 0xf0, 0xc0, 0x31, 0xf1, 0x80, + 0x1b, 0xf7, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 }; -// 'wheel2', 21x21px +// 'wheel2', 19x19px const unsigned char wheel_angle [] PROGMEM = { - 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x07, 0xff, 0x00, 0x0f, 0xfd, 0x80, 0x1f, 0xf8, 0xc0, 0x3f, - 0xf8, 0x60, 0x3f, 0xf0, 0x20, 0x7f, 0xf0, 0x70, 0x47, 0xf1, 0xf0, 0x41, 0xdf, 0xf0, 0x40, 0xaf, - 0xf0, 0x41, 0xdf, 0xf0, 0x47, 0xff, 0xf0, 0x7f, 0xdf, 0xf0, 0x3f, 0x8f, 0xe0, 0x3f, 0x8f, 0xe0, - 0x1f, 0x07, 0xc0, 0x0f, 0x07, 0x80, 0x07, 0x07, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00 + 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1f, 0xfb, 0x00, 0x3f, 0xf1, 0x80, 0x7f, 0xf0, 0xc0, 0x7f, + 0xe0, 0x40, 0xff, 0xe0, 0xe0, 0x8f, 0xe3, 0xe0, 0x83, 0xbf, 0xe0, 0x81, 0x5f, 0xe0, 0x83, 0xbf, + 0xe0, 0x8f, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0x7f, 0x1f, 0xc0, 0x7f, 0x1f, 0xc0, 0x3e, 0x0f, 0x80, + 0x1e, 0x0f, 0x00, 0x0e, 0x0e, 0x00, 0x03, 0xf8, 0x00 }; -// 'wheel3', 21x21px +// 'wheel3', 19x19px const unsigned char wheel_side [] PROGMEM = { - 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x07, 0xff, 0x00, 0x0d, 0xff, 0x80, 0x18, 0xff, 0xc0, 0x30, - 0xff, 0xe0, 0x20, 0x7f, 0xe0, 0x70, 0x7f, 0xf0, 0x7c, 0x7f, 0x10, 0x7f, 0xdc, 0x10, 0x7f, 0xa8, - 0x10, 0x7f, 0xdc, 0x10, 0x7c, 0x7f, 0x10, 0x70, 0x7f, 0xf0, 0x20, 0x7f, 0xe0, 0x30, 0xff, 0xe0, - 0x18, 0xff, 0xc0, 0x0d, 0xff, 0x80, 0x07, 0xff, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00 + 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1b, 0xff, 0x00, 0x31, 0xff, 0x80, 0x61, 0xff, 0xc0, 0x40, + 0xff, 0xc0, 0xe0, 0xff, 0xe0, 0xf8, 0xfe, 0x20, 0xff, 0xb8, 0x20, 0xff, 0x50, 0x20, 0xff, 0xb8, + 0x20, 0xf8, 0xfe, 0x20, 0xe0, 0xff, 0xe0, 0x40, 0xff, 0xc0, 0x61, 0xff, 0xc0, 0x31, 0xff, 0x80, + 0x1b, 0xff, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 }; class RAMPage : public LightPage, MidiCallback { diff --git a/avr/cores/megacommand/MCL/RAMPage.o b/avr/cores/megacommand/MCL/RAMPage.o new file mode 100644 index 0000000000000000000000000000000000000000..44384286b2049991b59ad8baed4c333c8661bcda GIT binary patch literal 132188 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

IvDAn~FvZ$7NWR8~FOWvQV6VIUa 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 Date: Thu, 17 Oct 2019 19:11:43 +1100 Subject: [PATCH 048/469] fix text alignment, fix input record levels --- avr/cores/megacommand/MCL/RAMPage.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index d233b01bf..f6a2c0528 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -546,7 +546,7 @@ void RAMPage::display() { oled_display.print("STEREO "); } oled_display.setFont(); - oled_display.setCursor(0, 20); + oled_display.setCursor(0, 24); switch (encoders[0]->cur) { case SOURCE_MAIN: if (mcl_cfg.ram_page_mode == LINK) { @@ -568,7 +568,7 @@ void RAMPage::display() { oled_display.print("INPB "); } } else { - oled_display.print("INPA"); + oled_display.print("INPA "); } break; @@ -580,7 +580,7 @@ void RAMPage::display() { oled_display.print("INPB "); } } else { - oled_display.print("INPB"); + oled_display.print("INPB "); } break; } @@ -723,7 +723,7 @@ bool RAMPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { if (mcl_cfg.ram_page_mode == MONO) { - uint8_t lev = 64 - 32; + uint8_t lev = 64; if (encoders[0]->cur == SOURCE_MAIN) { lev = 64; } @@ -740,7 +740,7 @@ bool RAMPage::handleEvent(gui_event_t *event) { 4 * encoders[3]->cur - 1, 128); } if (encoders[0]->cur == SOURCE_INP) { - setup_ram_rec_stereo(14, 64 - 32, encoders[0]->cur, + setup_ram_rec_stereo(14, 64 - 16, encoders[0]->cur, 4 * encoders[3]->cur - 1, 128); } } From fdce6809014defa43024fd8b30486fd224026df2 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 00:07:28 +0800 Subject: [PATCH 049/469] grid page visual improvements --- avr/cores/megacommand/MCL/GridPage.cpp | 99 +++++++++++++++----------- avr/cores/megacommand/MCL/MCLGUI.cpp | 1 + avr/cores/megacommand/MCL/MCLGUI.h | 6 ++ 3 files changed, 66 insertions(+), 40 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 932d4f29f..7d654419a 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -1,5 +1,5 @@ -#include "GUI.h" #include "GridPage.h" +#include "GUI.h" #include "GridPages.h" #include "MCL.h" void GridPage::init() { @@ -35,6 +35,7 @@ void GridPage::cleanup() { oled_display.setTextColor(WHITE, BLACK); #endif } + void GridPage::loop() { midi_active_peering.check(); int8_t diff, new_val; @@ -372,62 +373,74 @@ void GridPage::display_grid() { PGM_P tmp; encoders[1]->handler = NULL; // oled_display.setFont(&Org_01); + + oled_display.setCursor(x_offset - 5, y_offset + cur_row * 8); + oled_display.print(">"); + for (uint8_t y = 0; y < MAX_VISIBLE_ROWS; y++) { - if ((y == cur_row)) { - oled_display.setCursor(x_offset - 5, y_offset + y * 8); - oled_display.print(">"); - } - oled_display.setCursor(x_offset, y_offset + y * 8); + auto cur_posx = x_offset; + auto cur_posy = y_offset + y * 8; for (uint8_t x = 0; x < MAX_VISIBLE_COLS; x++) { - uint8_t track_type = row_headers[y].track_type[x + getCol() - cur_col]; - uint8_t model = row_headers[y].model[x + getCol() - cur_col]; - if (track_type == MD_TRACK_TYPE) { + oled_display.setCursor(cur_posx, cur_posy); + + auto track_idx = x + getCol() - cur_col; + auto row_idx = y + getRow() - cur_row; + uint8_t track_type = row_headers[y].track_type[track_idx]; + uint8_t model = row_headers[y].model[track_idx]; + + bool blink = false; + auto active_cue_color = WHITE; + + // Set cell label + switch (track_type) { + case MD_TRACK_TYPE: tmp = getMachineNameShort(model, 2); m_strncpy_p(str, tmp, 3); - } - if (track_type == A4_TRACK_TYPE) { + break; + case A4_TRACK_TYPE: str[0] = 'A'; str[1] = (x + getCol() - cur_col - 16) + '0'; - } - if (track_type == EXT_TRACK_TYPE) { + break; + case EXT_TRACK_TYPE: str[0] = 'M'; str[1] = (x + getCol() - cur_col - 16) + '0'; + break; } - if (((slot_apply > 0) && - ((x <= cur_col + slot_apply - 1) && (x > cur_col)) || - (x == cur_col)) && - (y == cur_row)) { - oled_display.fillRect(oled_display.getCursorX() - 1, - oled_display.getCursorY() - 6, 9, 7, WHITE); + // Highlight the current cursor position + slot menu apply range + if (x >= cur_col && x < cur_col + max(1, slot_apply) && y == cur_row) { + oled_display.fillRect(cur_posx - 1, cur_posy - 6, 9, 7, WHITE); oled_display.setTextColor(BLACK, WHITE); + active_cue_color = BLACK; } else { oled_display.setTextColor(WHITE, BLACK); } - if ((((MidiClock.step_counter == 2) || (MidiClock.step_counter == 3)) && - (MidiClock.state == 2)) && - ((y + getRow() - cur_row) == active_slots[x + getCol() - cur_col])) { - oled_display.setCursor(oled_display.getCursorX() + 8, - oled_display.getCursorY()); + + if ((MidiClock.step_counter == 2 || MidiClock.step_counter == 3) && + MidiClock.state == 2 && row_idx == active_slots[track_idx]) { + // blink, don't print + blink = true; + } else if (model == 0) { + oled_display.print("--"); } else { - if (model == 0) { - oled_display.print("--"); - } else { - oled_display.print(str); - } + oled_display.print(str); } - if ((x + 1 + getCol() - cur_col) % 4 == 0) { - oled_display.setTextColor(WHITE, BLACK); - oled_display.print(" | "); - } else { - oled_display.print(" "); + if (row_idx == active_slots[track_idx] && !blink) { + // a gentle visual cue for active slots + oled_display.drawPixel(cur_posx - 1, cur_posy - 6, active_cue_color); } - if ((x == getCol()) && (y == encoders[1]->cur)) { - // oled_display.drawRect(xpos_old, y_offset + 2 + (y - 1) * 8, 8, 6, - // WHITE); + // tomThumb is 4x6 + if (track_idx % 4 == 3) { + if (y == 0) { + // draw vertical separator + mcl_gui.draw_vertical_dashline(cur_posx + 9); + } + cur_posx += 12; + } else { + cur_posx += 10; } } } @@ -459,6 +472,7 @@ void GridPage::display_oled() { oled_display.display(); #endif } + void GridPage::display() { tick_frames(); @@ -590,15 +604,20 @@ void GridPage::apply_slot_changes() { uint8_t count = 0; for (n = 0; n < GRID_WIDTH; n++) { if (IS_BIT_SET32(slot_buffer_mask, n)) { - if (first == 255) { first = n; } + if (first == 255) { + first = n; + } count++; } } bool destination_same = false; - if (count == 1) { destination_same = true; } + if (count == 1) { + destination_same = true; + } for (n = 0; n + first < GRID_WIDTH && getCol() + n < GRID_WIDTH; n++) { if (IS_BIT_SET32(slot_buffer_mask, n + first)) { - grid.copy_slot(n + first, slot_buffer_row, getCol() + n, getRow(), destination_same); + grid.copy_slot(n + first, slot_buffer_row, getCol() + n, getRow(), + destination_same); } } row_headers[cur_row].write(getRow()); diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index fad287453..89cfb705a 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -71,6 +71,7 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display) { } void MCLGUI::clear_popup() { + // XXX too slow oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, s_menu_h - 5, BLACK); } diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 4b0be9532..50ea6c4af 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -17,6 +17,9 @@ class MCLGUI { void draw_popup(const char* title, bool deferred_display = false); void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); + void clear_leftpane(); + void clear_rightpane(); + static constexpr uint8_t s_menu_w = 96; static constexpr uint8_t s_menu_h = 24; static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; @@ -24,6 +27,9 @@ class MCLGUI { static constexpr uint8_t s_title_x = 31; static constexpr uint8_t s_title_w = 64; + static constexpr uint8_t s_rightpane_offset_x = 43; + static constexpr uint8_t s_rightpane_offset_y = 8; + }; extern MCLGUI mcl_gui; From 215b663771da5c05a3dcc283861ec6c549824dfa Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 00:23:49 +0800 Subject: [PATCH 050/469] gridpage visual improvements --- avr/cores/megacommand/MCL/GridPage.cpp | 9 +++++++-- avr/cores/megacommand/MCL/MCLGUI.cpp | 4 ++-- avr/cores/megacommand/MCL/MCLGUI.h | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 7d654419a..c4a8be3fc 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -374,7 +374,7 @@ void GridPage::display_grid() { encoders[1]->handler = NULL; // oled_display.setFont(&Org_01); - oled_display.setCursor(x_offset - 5, y_offset + cur_row * 8); + oled_display.setCursor(x_offset - 6, y_offset + cur_row * 8); oled_display.print(">"); for (uint8_t y = 0; y < MAX_VISIBLE_ROWS; y++) { @@ -436,7 +436,7 @@ void GridPage::display_grid() { if (track_idx % 4 == 3) { if (y == 0) { // draw vertical separator - mcl_gui.draw_vertical_dashline(cur_posx + 9); + mcl_gui.draw_vertical_dashline(cur_posx + 9, 3); } cur_posx += 12; } else { @@ -444,6 +444,11 @@ void GridPage::display_grid() { } } } + + // optionally, draw the first separator + if ((getCol() - cur_col) % 4 == 0) { + mcl_gui.draw_vertical_dashline(x_offset - 2, 3); + } #endif } void GridPage::display_slot_menu() { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 89cfb705a..eaa03c877 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -10,8 +10,8 @@ bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { return text_input_page.return_state; } -void MCLGUI::draw_vertical_dashline(uint8_t x) { - for (uint8_t y = 1; y < 32; y += 2) { +void MCLGUI::draw_vertical_dashline(uint8_t x, uint8_t from) { + for (uint8_t y = from; y < 32; y += 2) { oled_display.drawPixel(x, y, WHITE); } } diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 50ea6c4af..40b0a1ad4 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -9,7 +9,7 @@ class MCLGUI { public: bool wait_for_input(char *dst, const char *title, uint8_t len); void draw_infobox(const char* line1, const char* line2); - void draw_vertical_dashline(uint8_t x); + void draw_vertical_dashline(uint8_t x, uint8_t from = 1); void draw_vertical_separator(uint8_t x); void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); /// Clear the content area of a popup From c4e139e6021fe89306ba10dc41091e810fefd9b2 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 02:26:19 +0800 Subject: [PATCH 051/469] seqpage: SHIFT2+E4 switch track --- avr/cores/megacommand/MCL/SeqPage.cpp | 140 +++++++++++++++++----- avr/cores/megacommand/MCL/SeqPage.h | 1 + avr/cores/megacommand/MCL/SeqPages.cpp | 2 + avr/cores/megacommand/MCL/SeqPages.h | 1 + avr/cores/megacommand/MCL/SeqStepPage.cpp | 80 ++++++------- 5 files changed, 150 insertions(+), 74 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index dfecf53b0..8a508ac01 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "SeqPage.h" +#include "MCL.h" uint8_t SeqPage::page_select = 0; @@ -39,15 +39,45 @@ void SeqPage::cleanup() { note_interface.init_notes(); } +void SeqPage::select_track(uint8_t device, uint8_t track) { + if (device == DEVICE_MD) { + last_md_track = track; +#ifdef EXT_TRACK + if (GUI.currentPage() == &seq_extstep_page) { + GUI.setPage(&seq_step_page); + } +#endif + GUI.currentPage()->redisplay = true; + GUI.currentPage()->config(); + encoders[2]->old = encoders[2]->cur; + } +#ifdef EXT_TRACK + else { + last_ext_track = track - 16; + if (GUI.currentPage() == &seq_step_page) { + GUI.setPage(&seq_extstep_page); + } + GUI.currentPage()->redisplay = true; + GUI.currentPage()->config(); + encoders[2]->old = encoders[2]->cur; + } +#endif +} + bool SeqPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { uint8_t port = event->port; uint8_t device = midi_active_peering.get_device(port); uint8_t track = event->source - 128; + // TI + SHIFT1: adjust track seq length. + // Ignore SHIFT1 release event so it won't trigger + // a seq page select action. if (BUTTON_DOWN(Buttons.BUTTON2)) { + // calculate the intended seq length. uint8_t step = track; step += 1 + page_select * 16; + // Further, if SHIFT2 is pressed, set all tracks. if (SeqPage::midi_device == DEVICE_MD) { if (BUTTON_DOWN(Buttons.BUTTON3)) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { @@ -55,7 +85,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { } } mcl_seq.md_tracks[last_md_track].length = step; - } + } #ifdef EXT_TRACKS else { if (BUTTON_DOWN(Buttons.BUTTON3)) { @@ -74,38 +104,18 @@ bool SeqPage::handleEvent(gui_event_t *event) { return true; //} } + + // TI + SHIFT2 = select track. if (BUTTON_DOWN(Buttons.BUTTON3)) { - if (device == DEVICE_MD) { - last_md_track = track; -#ifdef EXT_TRACK - if (GUI.currentPage() == &seq_extstep_page) { - GUI.setPage(&seq_step_page); - } -#endif - GUI.currentPage()->redisplay = true; - GUI.currentPage()->config(); - encoders[2]->old = encoders[2]->cur; - return true; - } -#ifdef EXT_TRACK - else { - last_ext_track = track - 16; - if (GUI.currentPage() == &seq_step_page) { - GUI.setPage(&seq_extstep_page); - } - GUI.currentPage()->redisplay = true; - GUI.currentPage()->config(); - encoders[2]->old = encoders[2]->cur; - return true; - } -#endif - if (event->mask == EVENT_BUTTON_RELEASED) { - note_interface.notes[track] = 0; - ignore_button_release = 2; - } + select_track(device, track); return true; } - } + + // notify derived class about unhandled TI event + return false; + } // end TI events + + // if no TI button pressed, enable page switching. if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { if (EVENT_PRESSED(event, Buttons.ENCODER1)) { GUI.setPage(&seq_step_page); @@ -128,6 +138,8 @@ bool SeqPage::handleEvent(gui_event_t *event) { return false; } } + + // A not-ignored BUTTON2 release event triggers sequence page select if (EVENT_RELEASED(event, Buttons.BUTTON2)) { if (ignore_button_release != 2) { ignore_button_release = 255; @@ -143,6 +155,25 @@ bool SeqPage::handleEvent(gui_event_t *event) { } return false; } + + // SHIFT2 changes ENC4 to track select + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + encoders[3] = &trackselect_enc; + if (midi_device == DEVICE_MD) { + trackselect_enc.cur = trackselect_enc.old = last_md_track; + } +#ifdef EXT_TRACKS + else if (midi_device == DEVICE_A4) { + trackselect_enc.cur = trackselect_enc.old = last_ext_track; + } +#endif + } + else if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + encoders[3] = &seq_param4; + } + + + /* #ifdef OLED_DISPLAY @@ -188,6 +219,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { */ return false; } + void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { GUI.setLine(GUI.LINE2); @@ -404,7 +436,9 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, } void pattern_len_handler(Encoder *enc) { MCLEncoder *enc_ = (MCLEncoder *)enc; - if (!enc_->hasChanged()) { return; } + if (!enc_->hasChanged()) { + return; + } if (SeqPage::midi_device == DEVICE_MD) { DEBUG_PRINTLN("under 16"); if (BUTTON_DOWN(Buttons.BUTTON3)) { @@ -445,8 +479,28 @@ void SeqPage::loop() { } #endif track_menu_page.loop(); + return; + } + + if (trackselect_enc.hasChanged()) { + + auto plus = trackselect_enc.cur > trackselect_enc.old; + auto track = last_md_track; +#ifdef EXT_TRACKS + if (midi_device == DEVICE_A4) { + track = last_ext_track; + } +#endif + if(plus && track < 15) { + ++track; + }else if (!plus && track > 0) { + --track; + } + select_track(midi_device, track); + trackselect_enc.old = trackselect_enc.cur = track; } } + void SeqPage::display() { for (uint8_t i = 0; i < 2; i++) { @@ -461,6 +515,28 @@ void SeqPage::display() { } GUI.setLine(GUI.LINE1); #ifdef OLED_DISPLAY + + // Display current active track + oled_display.fillRect(110, 0, 18, 32, BLACK); + oled_display.setCursor(110, 0); + oled_display.print("TRK"); + oled_display.setCursor(110, 8); + oled_display.setFont(&TomThumb); + if (midi_device == DEVICE_MD) { + oled_display.print("MD "); + oled_display.print(last_md_track + 1); + } +#ifdef EXT_TRACKS + else if (midi_device == DEVICE_A4) { + oled_display.print("A4 "); + oled_display.print(last_ext_track + 1); + } + else { + oled_display.print("MI "); + oled_display.print(last_ext_track + 1); + } +#endif + if (show_track_menu) { uint8_t x_offset = 43; uint8_t y_offset = 8; diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 9b8570471..1316d4577 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -39,6 +39,7 @@ class SeqPage : public LightPage { void loop(); void display(); void setup(); + void select_track(uint8_t device, uint8_t track); void init(); void cleanup(); }; diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index fb9551973..5b28c4da6 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -5,6 +5,8 @@ MCLEncoder seq_param2(0, 64, ENCODER_RES_SEQ); MCLEncoder seq_param3(0, 10, ENCODER_RES_SEQ); MCLEncoder seq_param4(0, 64, ENCODER_RES_SEQ); +MCLEncoder trackselect_enc(0, 15, ENCODER_RES_SEQ); + MCLEncoder seq_lock1(0, 127, ENCODER_RES_PARAM); MCLEncoder seq_lock2(0, 127, ENCODER_RES_PARAM); diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 5b7b92496..36419ad33 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -22,6 +22,7 @@ extern MCLEncoder seq_param1; extern MCLEncoder seq_param2; extern MCLEncoder seq_param3; extern MCLEncoder seq_param4; +extern MCLEncoder trackselect_enc; extern MCLEncoder seq_lock1; extern MCLEncoder seq_lock2; diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 98efa8354..8c902473b 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "SeqStepPage.h" +#include "MCL.h" #define MIDI_OMNI_MODE 17 @@ -35,6 +35,7 @@ void SeqStepPage::cleanup() { MD.setTrackParam(last_md_track, 0, MD.kit.params[last_md_track][0]); } } + void SeqStepPage::display() { GUI.setLine(GUI.LINE1); GUI.put_string_at(0, " "); @@ -98,57 +99,60 @@ void SeqStepPage::display() { SeqPage::display(); } + void SeqStepPage::loop() { SeqPage::loop(); if (seq_param1.hasChanged() || seq_param2.hasChanged() || seq_param4.hasChanged()) { tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); + auto &active_track = mcl_seq.md_tracks[last_md_track]; for (uint8_t n = 0; n < 16; n++) { if (note_interface.notes[n] == 1) { uint8_t step = n + (page_select * 16); - if (step < mcl_seq.md_tracks[last_md_track].length) { + if (step < active_track.length) { uint8_t utiming = (seq_param2.cur + 0); uint8_t condition = seq_param1.cur; // timing = 3; // condition = 3; - mcl_seq.md_tracks[last_md_track].conditional[step] = condition; - mcl_seq.md_tracks[last_md_track].timing[step] = utiming; // upper + active_track.conditional[step] = condition; + active_track.timing[step] = utiming; // upper - if (!IS_BIT_SET64(mcl_seq.md_tracks[last_md_track].pattern_mask, - step)) { - - SET_BIT64(mcl_seq.md_tracks[last_md_track].pattern_mask, step); + if (!IS_BIT_SET64(active_track.pattern_mask, step)) { + SET_BIT64(active_track.pattern_mask, step); } if ((seq_param4.cur > 0) && (last_md_track < 15) && (tuning != NULL)) { uint8_t base = tuning->base; uint8_t note_num = seq_param4.cur; uint8_t machine_pitch = pgm_read_byte(&tuning->tuning[note_num]); - mcl_seq.md_tracks[last_md_track].set_track_pitch(step, - machine_pitch); + active_track.set_track_pitch(step, machine_pitch); } } } } } } + bool SeqStepPage::handleEvent(gui_event_t *event) { if (SeqPage::handleEvent(event)) { - return; + return true; } + auto &active_track = mcl_seq.md_tracks[last_md_track]; + if (note_interface.is_event(event)) { uint8_t mask = event->mask; uint8_t port = event->port; uint8_t device = midi_active_peering.get_device(port); - uint8_t track = event->source - 128; + uint8_t trackid = event->source - 128; + uint8_t step = trackid + (page_select * 16); midi_device = device; if (event->mask == EVENT_BUTTON_PRESSED) { @@ -160,23 +164,19 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { if (last_md_track < 15) { show_pitch = true; } - if ((track + (page_select * 16)) >= - mcl_seq.md_tracks[last_md_track].length) { + if (step >= active_track.length) { return true; } ((MCLEncoder *)encoders[1])->max = 23; - uint8_t step = track + (page_select * 16); - int8_t utiming = mcl_seq.md_tracks[last_md_track].timing[step]; // upper - uint8_t condition = - mcl_seq.md_tracks[last_md_track].conditional[step]; // lower - uint8_t pitch = - mcl_seq.md_tracks[last_md_track].get_track_lock(step, 0) - 1; + int8_t utiming = active_track.timing[step]; // upper + uint8_t condition = active_track.conditional[step]; // lower + uint8_t pitch = active_track.get_track_lock(step, 0) - 1; // Cond seq_param1.cur = condition; uint8_t note_num = 255; - // SET_BIT64(mcl_seq.md_tracks[last_md_track].pattern_mask, step); + // SET_BIT64(active_track.pattern_mask, step); tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); if (tuning) { for (uint8_t i = 0; i < tuning->len && note_num == 255; i++) { @@ -197,6 +197,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { } seq_param2.cur = utiming; } + if (event->mask == EVENT_BUTTON_RELEASED) { if (device == DEVICE_A4) { @@ -207,20 +208,18 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { if (last_md_track < 15) { show_pitch = false; } - if ((track + (page_select * 16)) >= - mcl_seq.md_tracks[last_md_track].length) { + if (step >= active_track.length) { return true; } - uint8_t step = track + (page_select * 16); /* uint8_t utiming = (seq_param2.cur + 0); uint8_t condition = seq_param1.cur; uint8_t step = track + (page_select * 16); // timing = 3; // condition = 3; - mcl_seq.md_tracks[last_md_track].conditional[step] = condition; - mcl_seq.md_tracks[last_md_track].timing[step] = utiming; // upper + active_track.conditional[step] = condition; + active_track.timing[step] = utiming; // upper if ((seq_param4.cur > 0) && (last_md_track < 15)) { tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); @@ -229,27 +228,27 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { uint8_t note_num = seq_param4.cur; uint8_t machine_pitch = pgm_read_byte(&tuning->tuning[note_num]); - mcl_seq.md_tracks[last_md_track].set_track_pitch(step, + active_track.set_track_pitch(step, machine_pitch); } }*/ // conditional_timing[cur_col][(track + (seq_param2.cur * 16))] = // condition; //lower - if (!IS_BIT_SET64(mcl_seq.md_tracks[last_md_track].pattern_mask, step)) { + if (!IS_BIT_SET64(active_track.pattern_mask, step)) { uint8_t utiming = (seq_param2.cur + 0); uint8_t condition = seq_param1.cur; - mcl_seq.md_tracks[last_md_track].conditional[step] = condition; - mcl_seq.md_tracks[last_md_track].timing[step] = utiming; // upper - mcl_seq.md_tracks[last_md_track].clear_step_locks(step); - SET_BIT64(mcl_seq.md_tracks[last_md_track].pattern_mask, step); + active_track.conditional[step] = condition; + active_track.timing[step] = utiming; // upper + active_track.clear_step_locks(step); + SET_BIT64(active_track.pattern_mask, step); } else { DEBUG_PRINTLN("Trying to clear"); - if (clock_diff(note_interface.note_hold,slowclock) < TRIG_HOLD_TIME) { - CLEAR_BIT64(mcl_seq.md_tracks[last_md_track].pattern_mask, step); - mcl_seq.md_tracks[last_md_track].conditional[step] = 0; - mcl_seq.md_tracks[last_md_track].timing[step] = 12; // upper + if (clock_diff(note_interface.note_hold, slowclock) < TRIG_HOLD_TIME) { + CLEAR_BIT64(active_track.pattern_mask, step); + active_track.conditional[step] = 0; + active_track.timing[step] = 12; // upper } } // Cond @@ -260,7 +259,8 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { return true; } return true; - } + } // end TI events + if (EVENT_PRESSED(event, Buttons.ENCODER1)) { if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { GUI.setPage(&grid_page); @@ -276,17 +276,13 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { return true; } if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - mcl_seq.md_tracks[last_md_track].clear_track(); + active_track.clear_track(); return true; } #ifdef EXT_TRACKS if (EVENT_RELEASED(event, Buttons.BUTTON1)) { GUI.setPage(&seq_extstep_page); return true; - /* - - return true; - */ } #endif return false; From 576789cfce111c332c5ae911ef63ce01bb44bea6 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 04:03:43 +0800 Subject: [PATCH 052/469] gridpage: shift2+encoder quick edit slot --- avr/cores/megacommand/MCL/GridPage.cpp | 29 +++++++++++++ avr/cores/megacommand/MCL/GridSavePage.cpp | 1 + avr/cores/megacommand/MCL/GridWritePage.cpp | 1 + avr/cores/megacommand/MCL/MDExploit.cpp | 46 ++++++++++++--------- avr/cores/megacommand/MCL/MDExploit.h | 1 + avr/cores/megacommand/MCL/SeqPage.cpp | 34 ++++++++------- 6 files changed, 76 insertions(+), 36 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index c4a8be3fc..81032269a 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -730,6 +730,7 @@ bool GridPage::handleEvent(gui_event_t *event) { merge_md = 0; return true; } + if (EVENT_RELEASED(event, Buttons.BUTTON3)) { apply_slot_changes(); return true; @@ -773,6 +774,34 @@ bool GridPage::handleEvent(gui_event_t *event) { return true; } #endif + + if (BUTTON_DOWN(Buttons.BUTTON3) && + (EVENT_PRESSED(event, Buttons.ENCODER1) || + EVENT_PRESSED(event, Buttons.ENCODER2) || + EVENT_PRESSED(event, Buttons.ENCODER3) || + EVENT_PRESSED(event, Buttons.ENCODER4))) { + show_slot_menu = false; + encoders[0] = ¶m1; + encoders[1] = ¶m2; + auto col = getCol(); + for (int i = 0; i < 20; ++i) { + note_interface.notes[i] = 0; + } + note_interface.notes[col] = 3; + mcl_actions.write_tracks(0, getRow()); + if (col < 16) { + last_md_track = col; + trackselect_enc.cur = trackselect_enc.old = col; + } +#ifdef EXT_TRACKS + else { + last_ext_track = col - 16; + trackselect_enc.cur = trackselect_enc.old = col - 16; + } +#endif + md_exploit.ignore_last_track_once = true; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1)) { seq_step_page.isSetup = false; prepare(); diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 573c7089b..e4679bce3 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -53,6 +53,7 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); GUI.put_value_at2(14, step_count); } + bool GridSavePage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index 6607ecf67..ba80e7793 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -106,6 +106,7 @@ bool GridWritePage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + // write the whole row md_exploit.off(); for (int i = 0; i < 20; i++) { diff --git a/avr/cores/megacommand/MCL/MDExploit.cpp b/avr/cores/megacommand/MCL/MDExploit.cpp index 6b042ef27..0c9a62986 100644 --- a/avr/cores/megacommand/MCL/MDExploit.cpp +++ b/avr/cores/megacommand/MCL/MDExploit.cpp @@ -1,6 +1,6 @@ /* Copyright 2018, Justin Mammarella jmamma@gmail.com */ -#include "MCL.h" #include "MDExploit.h" +#include "MCL.h" uint8_t last_md_track = 0; void MDExploit::setup() {} @@ -14,7 +14,7 @@ void MDExploit::setup_global(uint8_t global_num) { /** Stores the audio output for each track. **/ for (uint8_t track_n = 0; track_n < 16; track_n++) { - MD.global.drumRouting[track_n] = mcl_cfg.routing[track_n]; + MD.global.drumRouting[track_n] = mcl_cfg.routing[track_n]; } // baseChannel @@ -58,11 +58,11 @@ void MDExploit::send_globals() { ElektronDataToSysexEncoder encoder(&MidiUart); ElektronDataToSysexEncoder encoder2(&MidiUart); setup_global(0); - //in_sysex = 1; + // in_sysex = 1; MD.global.toSysex(encoder); setup_global(1); MD.global.toSysex(encoder2); - // in_sysex = 0; + // in_sysex = 0; // } } @@ -75,9 +75,9 @@ void MDExploit::switch_global(uint8_t global_page) { MD.global.baseChannel = 9; } uint8_t data[] = {0x56, (uint8_t)global_page & 0x7F}; - //in_sysex = 1; + // in_sysex = 1; MD.sendSysex(data, countof(data)); - // in_sysex = 0; + // in_sysex = 0; } bool MDExploit::on(bool switch_tracks) { // DEBUG_PRINTLN("Exploit on"); @@ -94,8 +94,12 @@ bool MDExploit::on(bool switch_tracks) { start_clock = slowclock; return false; } - state = true; - last_md_track = MD.getCurrentTrack(CALLBACK_TIMEOUT); + state = true; + if (ignore_last_track_once) { + ignore_last_track_once = false; + } else { + last_md_track = MD.getCurrentTrack(CALLBACK_TIMEOUT); + } // if (MidiClock.state == 2) { // last_md_track = MD.currentTrack; MD.clear_all_windows(); @@ -104,13 +108,15 @@ bool MDExploit::on(bool switch_tracks) { uint8_t n = NUM_MD_TRACKS - 1; track_with_nolocks = 255; while (n && (track_with_nolocks == 255)) { - if (mcl_seq.md_tracks[n].lock_mask == 0) { - track_with_nolocks = n; + if (mcl_seq.md_tracks[n].lock_mask == 0) { + track_with_nolocks = n; + } + n--; } - n--; + if (track_with_nolocks == 255) { + track_with_nolocks = 15; } - if (track_with_nolocks == 255) { track_with_nolocks = 15; } - MD.setStatus(0x22,track_with_nolocks); + MD.setStatus(0x22, track_with_nolocks); } //} // MD.getBlockingGlobal(0); @@ -205,7 +211,7 @@ bool MDExploit::off(bool switch_tracks) { if ((switch_tracks)) { MD.getCurrentTrack(); if (MD.currentTrack == track_with_nolocks) { - MD.setStatus(0x22,last_md_track); + MD.setStatus(0x22, last_md_track); } } state = false; @@ -311,11 +317,11 @@ void MDExploitMidiEvents::onNoteOffCallback_Midi(uint8_t *msg) { void MDExploitMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { if ((msg[1] < 120) && (msg[1] > 15)) { - if (md_exploit.state) { - route_page.update_globals(); - } - if (md_exploit.off()) { - GUI.setPage(&grid_page); + if (md_exploit.state) { + route_page.update_globals(); + } + if (md_exploit.off()) { + GUI.setPage(&grid_page); } } } @@ -326,7 +332,7 @@ void MDExploitCallbacks::onMidiStartCallback() { md_exploit.start_clock = slowclock; note_interface.note_proceed = false; // } - //mcl_actions.start_clock32th = 0; + // mcl_actions.start_clock32th = 0; } MDExploit md_exploit; diff --git a/avr/cores/megacommand/MCL/MDExploit.h b/avr/cores/megacommand/MCL/MDExploit.h index 06bdf7c54..b8ad08402 100644 --- a/avr/cores/megacommand/MCL/MDExploit.h +++ b/avr/cores/megacommand/MCL/MDExploit.h @@ -52,6 +52,7 @@ class MDExploit { uint16_t start_clock = 0; MDExploitCallbacks md_exploit_callbacks; MDExploitMidiEvents md_exploit_midievents; + bool ignore_last_track_once = false; MDExploit() {} void setup(); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 8a508ac01..f0b4fe1ab 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -223,29 +223,30 @@ bool SeqPage::handleEvent(gui_event_t *event) { void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { GUI.setLine(GUI.LINE2); + auto &active_track = mcl_seq.md_tracks[last_md_track]; char str[17] = "----------------"; /*uint8_t step_count = (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - - (mcl_seq.md_tracks[last_md_track].length * + (active_track.length * ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / - mcl_seq.md_tracks[last_md_track].length)); */ - uint8_t step_count = mcl_seq.md_tracks[last_md_track].step_count; + active_track.length)); */ + uint8_t step_count = active_track.step_count; for (int i = 0; i < 16; i++) { - if (i + offset >= mcl_seq.md_tracks[last_md_track].length) { + if (i + offset >= active_track.length) { str[i] = ' '; } else if ((show_current_step) && (step_count == i + offset) && (MidiClock.state == 2)) { str[i] = ' '; } else { - if (IS_BIT_SET64(mcl_seq.md_tracks[last_md_track].lock_mask, + if (IS_BIT_SET64(active_track.lock_mask, i + offset)) { str[i] = 'x'; } - if (IS_BIT_SET64(mcl_seq.md_tracks[last_md_track].pattern_mask, + if (IS_BIT_SET64(active_track.pattern_mask, i + offset) && - !IS_BIT_SET64(mcl_seq.md_tracks[last_md_track].lock_mask, + !IS_BIT_SET64(active_track.lock_mask, i + offset)) { #ifdef OLED_DISPLAY str[i] = (char)0xF8; @@ -253,9 +254,9 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { str[i] = (char)165; #endif } - if (IS_BIT_SET64(mcl_seq.md_tracks[last_md_track].pattern_mask, + if (IS_BIT_SET64(active_track.pattern_mask, i + offset) && - IS_BIT_SET64(mcl_seq.md_tracks[last_md_track].lock_mask, + IS_BIT_SET64(active_track.lock_mask, i + offset)) { #ifdef OLED_DISPLAY str[i] = (char)2; @@ -283,7 +284,8 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, char mystr[17] = " "; - uint64_t pattern_mask = mcl_seq.md_tracks[last_md_track].pattern_mask; + auto &active_track = mcl_seq.md_tracks[last_md_track]; + uint64_t pattern_mask = active_track.pattern_mask; int8_t note_held = 0; if (device == DEVICE_MD) { @@ -298,21 +300,21 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, #endif /* uint8_t step_count = (count_16th - mcl_actions_callbacks.start_clock96th / 5) - - (mcl_seq.md_tracks[last_md_track].length * + (active_track.length * ((count_16th - mcl_actions_callbacks.start_clock96th / 5) / - mcl_seq.md_tracks[last_md_track].length));*/ + active_track.length));*/ /* uint8_t step_count = (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - - (mcl_seq.md_tracks[last_md_track].length * + (active_track.length * ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / - mcl_seq.md_tracks[last_md_track].length)); */ + active_track.length)); */ - uint8_t step_count = mcl_seq.md_tracks[last_md_track].step_count; + uint8_t step_count = active_track.step_count; #ifdef OLED_DISPLAY #endif - if (i + offset >= mcl_seq.md_tracks[last_md_track].length) { + if (i + offset >= active_track.length) { mystr[i] = ' '; } else if ((show_current_step) && (step_count == i + offset) && (MidiClock.state == 2)) { From 4f5822c5cea6446f1527f176202a043c223dd40a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 05:15:31 +0800 Subject: [PATCH 053/469] fix up rampage --- avr/cores/megacommand/MCL/MCLMenus.cpp | 1 + avr/cores/megacommand/MCL/MCLSysConfig.h | 1 + 2 files changed, 2 insertions(+) diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index 4fc4f7b92..3c57da47f 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -12,6 +12,7 @@ MCLEncoder config_param5(0, 17, ENCODER_RES_SYS); MCLEncoder config_param6(0, 17, ENCODER_RES_SYS); MenuPage aux_config_page(&auxconfig_menu_layout, &config_param1, &config_param6); +MenuPage ram_config_page(&rampage1_menu_layout, &config_param1, &config_param6); MenuPage system_page(&system_menu_layout, &options_param1, &options_param2); MenuPage midi_config_page(&midiconfig_menu_layout, &config_param1, &config_param3); diff --git a/avr/cores/megacommand/MCL/MCLSysConfig.h b/avr/cores/megacommand/MCL/MCLSysConfig.h index 9f3903585..b047ba0c5 100644 --- a/avr/cores/megacommand/MCL/MCLSysConfig.h +++ b/avr/cores/megacommand/MCL/MCLSysConfig.h @@ -38,6 +38,7 @@ class MCLSysConfigData { uint8_t chain_rand_min; uint8_t chain_rand_max; uint8_t auto_normalize; + uint8_t ram_page_mode; }; class MCLSysConfig : public MCLSysConfigData { From c899e05dc4a96f060bd97559b08824649d8d7f5b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 09:48:21 +1100 Subject: [PATCH 054/469] Forgot to check-in --- avr/cores/megacommand/MCL/MCLMenus.cpp | 4 ++++ avr/cores/megacommand/MCL/MCLSysConfig.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index 4fc4f7b92..dddb03da6 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -10,6 +10,7 @@ MCLEncoder config_param3(0, 17, ENCODER_RES_SYS); MCLEncoder config_param4(0, 17, ENCODER_RES_SYS); MCLEncoder config_param5(0, 17, ENCODER_RES_SYS); MCLEncoder config_param6(0, 17, ENCODER_RES_SYS); +MCLEncoder config_param7(0, 17, ENCODER_RES_SYS); MenuPage aux_config_page(&auxconfig_menu_layout, &config_param1, &config_param6); MenuPage system_page(&system_menu_layout, &options_param1, &options_param2); @@ -19,6 +20,9 @@ MenuPage md_config_page(&mdconfig_menu_layout, &config_param1, &config_param4); MenuPage chain_config_page(&chain_menu_layout, &config_param1, &config_param6); MenuPage mcl_config_page(&mclconfig_menu_layout, &config_param1, &config_param5); +MenuPage ram_config_page(&rampage1_menu_layout, &config_param1, + &config_param7); + MCLEncoder input_encoder1(0, 127, ENCODER_RES_SYS); MCLEncoder input_encoder2(0, 127, ENCODER_RES_SYS); diff --git a/avr/cores/megacommand/MCL/MCLSysConfig.h b/avr/cores/megacommand/MCL/MCLSysConfig.h index 9f3903585..a894d76a4 100644 --- a/avr/cores/megacommand/MCL/MCLSysConfig.h +++ b/avr/cores/megacommand/MCL/MCLSysConfig.h @@ -4,7 +4,7 @@ #define MCLSYSCONFIG_H__ #include "SdFat.h" -#define CONFIG_VERSION 2027 +#define CONFIG_VERSION 2028 #define MIDI_OMNI_MODE 17 #define MIDI_LOCAL_MODE 0 @@ -38,6 +38,7 @@ class MCLSysConfigData { uint8_t chain_rand_min; uint8_t chain_rand_max; uint8_t auto_normalize; + uint8_t ram_page_mode; }; class MCLSysConfig : public MCLSysConfigData { From 8e239172b6e09078890f25d2ac2461858e1702b9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 16:22:34 +1100 Subject: [PATCH 055/469] improve progress bar, reel should not spin backwards on record --- avr/cores/megacommand/MCL/RAMPage.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index f6a2c0528..4b4f1b6e9 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -516,9 +516,10 @@ void RAMPage::display() { remain = (float)mcl_seq.md_tracks[n].step_count / (float)mcl_seq.md_tracks[n].length; } - uint8_t width = remain * 18; - if (width <= 3) { width = 3; } + uint8_t width = remain * 20; + if (width >= 3) { oled_display.fillRoundRect(105, 28, width, 4, 1, WHITE); + } } oled_display.setFont(); oled_display.setCursor(0, 0); @@ -625,7 +626,7 @@ void RAMPage::display() { } if ((wheel_spin_last_clock != MidiClock.div16th_counter) && ((rec_state == STATE_RECORD) || (rec_state == STATE_PLAY))) { - if (magic == 1) { + if ((magic == 1) && (rec_state != STATE_RECORD)) { if (wheel_spin == 0) { wheel_spin = 8; } From 74bd073fff77a02d85e56f24ccbfc2eaa19e0c33 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 16:33:54 +1100 Subject: [PATCH 056/469] Fix menu page bug with first element changing --- avr/cores/megacommand/MCL/MenuPage.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 0c07dfd50..9e69cc627 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -3,10 +3,20 @@ #include "MenuPage.h" void MenuPage::init() { - uint8_t *dest_var = menu.get_dest_variable(encoders[1]->cur); + ((MCLEncoder *)encoders[1])->max = menu.get_number_of_items() - 1; + if (((MCLEncoder *)encoders[1])->cur > ((MCLEncoder *)encoders[1])->max) { + ((MCLEncoder *)encoders[1])->cur = 0; + } + ((MCLEncoder *)encoders[0])->max = + menu.get_option_range(encoders[1]->cur) - 1; + ((MCLEncoder *)encoders[0])->min = menu.get_option_min(encoders[1]->cur); + + uint8_t *dest_var = menu.get_dest_variable(encoders[1]->cur); if (dest_var != NULL) { encoders[0]->setValue(*dest_var); } + encoders[0]->old = encoders[0]->cur; + encoders[1]->old = encoders[1]->cur; } void MenuPage::setup() { #ifdef OLED_DISPLAY @@ -15,12 +25,12 @@ void MenuPage::setup() { } void MenuPage::loop() { - ((MCLEncoder *)encoders[1])->max = menu.get_number_of_items() - 1; + + if (encoders[1]->hasChanged()) { ((MCLEncoder *)encoders[0])->max = menu.get_option_range(encoders[1]->cur) - 1; ((MCLEncoder *)encoders[0])->min = menu.get_option_min(encoders[1]->cur); - if (encoders[1]->hasChanged()) { uint8_t diff = encoders[1]->cur - encoders[1]->old; int8_t new_val = cur_row + diff; From d641cb4404ae76cd2abec54f57bbf3c8c960df07 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 16:51:01 +1100 Subject: [PATCH 057/469] Use chain_mode quantization formula for setting transition steps of record/play --- avr/cores/megacommand/MCL/RAMPage.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 4b4f1b6e9..e136f99a9 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -127,8 +127,10 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t lev, uint8_t m = mcl_seq.md_tracks[track].length; - uint16_t next_step = - MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); +// uint16_t next_step = + // MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); + + uint16_t next_step = (MidiClock.div16th_counter / steps) * steps + steps; transition_step = next_step; record_len = (uint8_t)steps; @@ -341,11 +343,10 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, mcl_actions.chains[track].loops = 1; mcl_actions.send_machine[track] = 0; - uint16_t next_step; uint8_t m = mcl_seq.md_tracks[track].length; - next_step = - MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); + //uint16_t next_step = MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); + uint16_t next_step = (MidiClock.div16th_counter / steps) * steps + steps; grid_page.active_slots[track] = 0x7FFF; // mcl_actions.transition_level[track] = TRANSITION_MUTE; mcl_actions.next_transitions[track] = next_step; From 52fef1a0fd1911f8c6f0e63be6217e6f1c15e8e7 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 14:29:50 +0800 Subject: [PATCH 058/469] implement question dialog box --- avr/cores/megacommand/MCL/MCLGUI.cpp | 29 ++++---- avr/cores/megacommand/MCL/MCLGUI.h | 14 +++- .../megacommand/MCL/QuestionDialogPage.cpp | 67 +++++++++++++++++++ .../megacommand/MCL/QuestionDialogPage.h | 24 +++++++ avr/cores/megacommand/MCL/SDDrivePage.cpp | 5 ++ 5 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 avr/cores/megacommand/MCL/QuestionDialogPage.cpp create mode 100644 avr/cores/megacommand/MCL/QuestionDialogPage.h diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index fad287453..0d4a60429 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -10,6 +10,16 @@ bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { return text_input_page.return_state; } +bool MCLGUI::wait_for_confirm(const char *title, const char* text) +{ + questiondialog_page.init(title, text); + GUI.pushPage(&questiondialog_page); + while (GUI.currentPage() == &questiondialog_page) { + GUI.loop(); + } + return questiondialog_page.return_state; +} + void MCLGUI::draw_vertical_dashline(uint8_t x) { for (uint8_t y = 1; y < 32; y += 2) { oled_display.drawPixel(x, y, WHITE); @@ -51,7 +61,7 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display) { oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, s_menu_h + 2, BLACK); oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, s_menu_h, WHITE); - oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 3, WHITE); + oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 4, WHITE); // draw the title '____/**********\____' part oled_display.drawRect(s_title_x, s_menu_y - 3, s_title_w, 3, BLACK); @@ -62,7 +72,7 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display) { oled_display.setTextColor(BLACK); //auto len = strlen(title_buf) * 5; //oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); - oled_display.setCursor(s_title_x + 2, s_menu_y + 3); + oled_display.setCursor(s_title_x + 2, s_menu_y + 4); oled_display.println(title_buf); oled_display.setTextColor(WHITE); if (!deferred_display) { @@ -110,18 +120,8 @@ void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, } // ref: Design/infobox.png -void MCLGUI::draw_infobox(const char* line1, const char* line2) +void MCLGUI::draw_infobox(const char* line1, const char* line2, const int line2_offset) { - constexpr auto info_y1 = 2; - constexpr auto info_y2 = 27; - constexpr auto info_x1 = 12; - constexpr auto info_x2 = 124; - constexpr auto circle_x = info_x1 + 10; - constexpr auto circle_y = info_y1 + 15; - - constexpr auto info_w = info_x2 - info_x1 + 1; - constexpr auto info_h = info_y2 - info_y1 + 1; - auto oldfont = oled_display.getFont(); oled_display.fillRect(info_x1 - 1, info_y1 - 1, info_w + 3, info_h + 3, BLACK); @@ -142,8 +142,9 @@ void MCLGUI::draw_infobox(const char* line1, const char* line2) oled_display.println(title_buf); oled_display.setTextColor(WHITE); - oled_display.setCursor(info_x1 + 23, info_y1 + 17); + oled_display.setCursor(info_x1 + 23, info_y1 + 17 + line2_offset); oled_display.println(line2); oled_display.setFont(oldfont); } + diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 4b0be9532..2e7e43669 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -4,11 +4,13 @@ #define MCLGUI_H__ #include "TextInputPage.h" +#include "QuestionDialogPage.h" class MCLGUI { public: bool wait_for_input(char *dst, const char *title, uint8_t len); - void draw_infobox(const char* line1, const char* line2); + bool wait_for_confirm(const char *title, const char* text); + void draw_infobox(const char* line1, const char* line2, const int line2_offset = 0); void draw_vertical_dashline(uint8_t x); void draw_vertical_separator(uint8_t x); void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); @@ -24,6 +26,16 @@ class MCLGUI { static constexpr uint8_t s_title_x = 31; static constexpr uint8_t s_title_w = 64; + static constexpr auto info_y1 = 2; + static constexpr auto info_y2 = 27; + static constexpr auto info_x1 = 12; + static constexpr auto info_x2 = 124; + static constexpr auto circle_x = info_x1 + 10; + static constexpr auto circle_y = info_y1 + 15; + + static constexpr auto info_w = info_x2 - info_x1 + 1; + static constexpr auto info_h = info_y2 - info_y1 + 1; + }; extern MCLGUI mcl_gui; diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp new file mode 100644 index 000000000..6edde3b3d --- /dev/null +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -0,0 +1,67 @@ +#include "MCL.h" +#include "QuestionDialogPage.h" + +void QuestionDialogPage::init(const char* title, const char* text) { +#ifdef OLED_DISPLAY + mcl_gui.draw_infobox(title, text, -1); + oled_display.drawFastHLine(MCLGUI::info_x1 + 1, MCLGUI::info_y2, MCLGUI::info_w - 2, BLACK); + + auto oldfont = oled_display.getFont(); + + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + + oled_display.setCursor(MCLGUI::info_x2 - 86, MCLGUI::info_y1 + 24); + oled_display.print("2 YES"); + + oled_display.setCursor(MCLGUI::info_x2 - 55, MCLGUI::info_y1 + 24); + oled_display.print("3 NO"); + + oled_display.drawRect(MCLGUI::info_x2 - 88, MCLGUI::info_y1 + 17, 21, 8, WHITE); + oled_display.drawRect(MCLGUI::info_x2 - 57, MCLGUI::info_y1 + 17, 19, 8, WHITE); + + oled_display.fillRect(MCLGUI::info_x2 - 87, MCLGUI::info_y1 + 18, 5, 6, INVERT); + oled_display.fillRect(MCLGUI::info_x2 - 56, MCLGUI::info_y1 + 18, 5, 6, INVERT); + + oled_display.setFont(oldfont); + oled_display.display(); +#else + // TODO +#endif +} + +bool QuestionDialogPage::handleEvent(gui_event_t *event) { + if (note_interface.is_event(event)) { + return false; + } + + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + oled_display.fillRect(MCLGUI::info_x2 - 82, MCLGUI::info_y1 + 18, 12, 6, INVERT); + oled_display.display(); + return true; + } + + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + oled_display.fillRect(MCLGUI::info_x2 - 51, MCLGUI::info_y1 + 18, 12, 6, INVERT); + oled_display.display(); + return true; + } + + if (EVENT_RELEASED(event, Buttons.BUTTON2)) { + return_state = true; + GUI.popPage(); + return true; + } + + if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + return_state = false; + GUI.popPage(); + return true; + } + + //GUI.popPage(); + + return false; +} + +QuestionDialogPage questiondialog_page; diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.h b/avr/cores/megacommand/MCL/QuestionDialogPage.h new file mode 100644 index 000000000..9629f49c7 --- /dev/null +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.h @@ -0,0 +1,24 @@ +/* Yatao Li yatao.li@live.com 2019 */ + +#ifndef QUESTIONDIALOGPAGE_H__ +#define QUESTIONDIALOGPAGE_H__ + +//#include "Pages.hh" +#include "GUI.h" + +class QuestionDialogPage : public LightPage { +public: + + QuestionDialogPage() : LightPage() { } + + bool handleEvent(gui_event_t *event); + void init(const char* title, const char* text); + void display() {} + void setup() {} + + bool return_state = false; +}; + +extern QuestionDialogPage questiondialog_page; + +#endif /* QUESTIONDIALOGPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 5984f84f0..398c9fb16 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -27,6 +27,11 @@ void SDDrivePage::save_snapshot() { char entry_name[] = " "; + if (mcl_gui.wait_for_confirm("Confirm", "Save snapshot?")) + { + gfx.alert("Confirmed", "Save"); + } + if (mcl_gui.wait_for_input(entry_name, "Snapshot Name", 8)) { if (file.isOpen()) { file.close(); From 438c9933d412857809938914080a5e10d7b674ef Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 14:30:05 +0800 Subject: [PATCH 059/469] cleanup --- avr/cores/megacommand/MCL/SDDrivePage.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 398c9fb16..5984f84f0 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -27,11 +27,6 @@ void SDDrivePage::save_snapshot() { char entry_name[] = " "; - if (mcl_gui.wait_for_confirm("Confirm", "Save snapshot?")) - { - gfx.alert("Confirmed", "Save"); - } - if (mcl_gui.wait_for_input(entry_name, "Snapshot Name", 8)) { if (file.isOpen()) { file.close(); From 42a662b78dd416da11e551a5fb4fe4a9c27a910e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 17:44:57 +1100 Subject: [PATCH 060/469] Improve linking of stereo tracks --- avr/cores/megacommand/MCL/RAMPage.cpp | 97 +++++++++++++++++---------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index e136f99a9..eb580565e 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -14,6 +14,9 @@ #define SOURCE_INP 1 +uint8_t RAMPage::rec_states[NUM_RAM_PAGES]; +uint8_t RAMPage::slice_modes[NUM_RAM_PAGES]; + void RAMPage::setup() { DEBUG_PRINT_FN(); encoders[3]->cur = 4; @@ -35,6 +38,12 @@ void RAMPage::init() { if (page_id == 0) { setup_callbacks(); } + if (mcl_cfg.ram_page_mode == LINK) { + for (uint8_t n = 0; n < 4; n++) { + if (page_id == 0) { encoders[n]->cur = ram_page_b.encoders[n]->cur; } + else { encoders[n]->cur = ram_page_a.encoders[n]->cur; } + } + } } void RAMPage::cleanup() { @@ -61,7 +70,10 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t lev, memset(&(md_track.machine.params), 255, 24); uint16_t steps = encoders[3]->cur * 4; - rec_state = STATE_QUEUE; + RAMPage::rec_states[page_id] = STATE_QUEUE; + if (mcl_cfg.ram_page_mode == LINK) { + RAMPage::rec_states[0] = RAMPage::rec_states[1] = STATE_QUEUE; + } md_track.active = MD_TRACK_TYPE; md_track.machine.model = model; @@ -127,7 +139,7 @@ void RAMPage::setup_ram_rec(uint8_t track, uint8_t model, uint8_t lev, uint8_t m = mcl_seq.md_tracks[track].length; -// uint16_t next_step = + // uint16_t next_step = // MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); uint16_t next_step = (MidiClock.div16th_counter / steps) * steps + steps; @@ -159,11 +171,11 @@ void RAMPage::reverse(uint8_t track) { model != RAM_P4_MODEL) { return; } - if (magic == 0) { + if (RAMPage::slice_modes[page_id] == 0) { MD.setTrackParam(track, ROM_STRT, 127); MD.setTrackParam(track, ROM_END, 0); } - if (magic == 1) { + if (RAMPage::slice_modes[page_id] == 1) { MD.setTrackParam(track, ROM_STRT, 0); MD.setTrackParam(track, ROM_END, 127); } @@ -200,7 +212,7 @@ bool RAMPage::slice(uint8_t track, uint8_t linked_track) { mcl_seq.md_tracks[linked_track].locks[0][n]; mcl_seq.md_tracks[track].locks[1][n] = mcl_seq.md_tracks[linked_track].locks[1][n]; - } else if (magic == 0) { + } else if (RAMPage::slice_modes[page_id] == 0) { mcl_seq.md_tracks[track].locks[0][n] = sample_inc * s + 1; mcl_seq.md_tracks[track].locks[1][n] = (sample_inc) * (s + 1) + 1; if (mcl_seq.md_tracks[track].locks[1][n] > 128) { @@ -288,7 +300,11 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, uint16_t steps = encoders[3]->cur * 4; - rec_state = STATE_QUEUE; + RAMPage::rec_states[page_id] = STATE_QUEUE; + if (mcl_cfg.ram_page_mode == LINK) { + RAMPage::rec_states[0] = RAMPage::rec_states[1] = STATE_QUEUE; + } + md_track.active = MD_TRACK_TYPE; md_track.machine.model = model; /* @@ -345,7 +361,8 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, uint8_t m = mcl_seq.md_tracks[track].length; - //uint16_t next_step = MidiClock.div16th_counter + (m - mcl_seq.md_tracks[track].step_count); + // uint16_t next_step = MidiClock.div16th_counter + (m - + // mcl_seq.md_tracks[track].step_count); uint16_t next_step = (MidiClock.div16th_counter / steps) * steps + steps; grid_page.active_slots[track] = 0x7FFF; // mcl_actions.transition_level[track] = TRANSITION_MUTE; @@ -359,7 +376,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, } void RAMPage::setup_ram_play_mono(uint8_t track) { - magic = 0; + RAMPage::slice_modes[page_id] = 0; uint8_t model = RAM_P1_MODEL; if (page_id == 1) { model = RAM_P2_MODEL; @@ -372,7 +389,7 @@ void RAMPage::setup_ram_play_stereo(uint8_t track) { return; } - magic = 0; + RAMPage::slice_modes[page_id] = 0; setup_ram_play(track, RAM_P1_MODEL, 0, track + 1); setup_ram_play(track + 1, RAM_P2_MODEL, 127, track); } @@ -411,15 +428,25 @@ void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t lev, uint8_t source, void RAMPage::loop() { uint8_t n = 14 + page_id; if (grid_page.active_slots[n] == SLOT_RAM_RECORD) { - if ((rec_state == STATE_QUEUE) && + if ((RAMPage::rec_states[page_id] == STATE_QUEUE) && (MidiClock.div16th_counter == transition_step + record_len)) { - rec_state = STATE_RECORD; + if (mcl_cfg.ram_page_mode == LINK) { + RAMPage::rec_states[0] = RAMPage::rec_states[1] = STATE_RECORD; + } else { + RAMPage::rec_states[page_id] = STATE_RECORD; + } + // else if ((RAMPage::rec_states[page_id] == STATE_RECORD) && + // (MidiClock.div16th_counter >= transition_step + record_len + + // record_len)) { } - // else if ((rec_state == STATE_RECORD) && (MidiClock.div16th_counter >= - // transition_step + record_len + record_len)) { } else if ((grid_page.active_slots[n] == SLOT_RAM_PLAY) && + (MidiClock.div16th_counter >= transition_step + record_len)) { - rec_state = STATE_PLAY; + if (mcl_cfg.ram_page_mode == LINK) { + RAMPage::rec_states[0] = RAMPage::rec_states[1] = STATE_PLAY; + } else { + RAMPage::rec_states[page_id] = STATE_PLAY; + } } } @@ -437,7 +464,7 @@ void RAMPage::display() { GUI.put_string_at(0, "RAM"); GUI.put_value_at1(4, page_id + 1); - switch (rec_state) { + switch (RAMPage::rec_states[page_id]) { case STATE_QUEUE: GUI.put_string_at(6, "[Queue]"); break; @@ -458,40 +485,40 @@ void RAMPage::display() { } */ - switch (encoders[0]->cur) { + switch (encoders[0]->cur) { case SOURCE_MAIN: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - GUI.put_string_at(0,"L"); + GUI.put_string_at(0, "L"); } if (page_id == 1) { - GUI.put_string_at(0,"R"); + GUI.put_string_at(0, "R"); } } else { - GUI.put_string_at(0,"M"); + GUI.put_string_at(0, "M"); } break; case SOURCE_INPA: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - GUI.put_string_at(0,"INA"); + GUI.put_string_at(0, "INA"); } else { - GUI.put_string_at(0,"INB"); + GUI.put_string_at(0, "INB"); } } else { - GUI.put_string_at(0,"INA"); + GUI.put_string_at(0, "INA"); } break; case SOURCE_INPB: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - GUI.put_string_at(0,"INA"); + GUI.put_string_at(0, "INA"); } else { - GUI.put_string_at(0,"INB"); + GUI.put_string_at(0, "INB"); } } else { - GUI.put_string_at(0,"INB"); + GUI.put_string_at(0, "INB"); } break; } @@ -503,15 +530,15 @@ void RAMPage::display() { #endif #ifdef OLED_DISPLAY float remain; - oled_display.drawRoundRect(105, 28, 20, 4 , 1, WHITE); - if ((rec_state != STATE_NOSTATE)) { + oled_display.drawRoundRect(105, 28, 20, 4, 1, WHITE); + if ((RAMPage::rec_states[page_id] != STATE_NOSTATE)) { if (MidiClock.clock_less_than(transition_step + record_len, MidiClock.div16th_counter)) { remain = ((float)(MidiClock.div16th_counter) / (float)(transition_step + record_len)); } - // else if (rec_state == STATE_RECORD{ + // else if (RAMPage::rec_states[page_id] == STATE_RECORD{ else { uint8_t n = 14 + page_id; remain = (float)mcl_seq.md_tracks[n].step_count / @@ -519,7 +546,7 @@ void RAMPage::display() { } uint8_t width = remain * 20; if (width >= 3) { - oled_display.fillRoundRect(105, 28, width, 4, 1, WHITE); + oled_display.fillRoundRect(105, 28, width, 4, 1, WHITE); } } oled_display.setFont(); @@ -527,7 +554,7 @@ void RAMPage::display() { oled_display.print("RAM"); oled_display.print(page_id + 1); - switch (rec_state) { + switch (RAMPage::rec_states[page_id]) { case STATE_QUEUE: oled_display.print(" [Queue]"); break; @@ -626,8 +653,10 @@ void RAMPage::display() { break; } if ((wheel_spin_last_clock != MidiClock.div16th_counter) && - ((rec_state == STATE_RECORD) || (rec_state == STATE_PLAY))) { - if ((magic == 1) && (rec_state != STATE_RECORD)) { + ((RAMPage::rec_states[page_id] == STATE_RECORD) || + (RAMPage::rec_states[page_id] == STATE_PLAY))) { + if ((RAMPage::slice_modes[page_id] == 1) && + (RAMPage::rec_states[page_id] != STATE_RECORD)) { if (wheel_spin == 0) { wheel_spin = 8; } @@ -749,7 +778,7 @@ bool RAMPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { - magic = 1; + RAMPage::slice_modes[page_id] = 1; if (mcl_cfg.ram_page_mode == MONO) { slice(14 + page_id, 255); } else { @@ -759,7 +788,7 @@ bool RAMPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON4)) { - magic = 0; + RAMPage::slice_modes[page_id] = 0; if (mcl_cfg.ram_page_mode == MONO) { if (!slice(14 + page_id, 255)) { setup_ram_play_mono(14 + page_id); From de53daf45882aa463ceea21c2f837fe54bcd07c8 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 18:02:42 +1100 Subject: [PATCH 061/469] Prevent number of slices from exceeding number of steps --- avr/cores/megacommand/MCL/RAMPage.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index eb580565e..9fa5927be 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -426,6 +426,17 @@ void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t lev, uint8_t source, } void RAMPage::loop() { + + //Prevent number of slices exceeding number of steps. + uint8_t steps = encoders[3]->cur * 4; + uint8_t slices = 1 << encoders[2]->cur; + + while (slices > steps) { + encoders[2]->cur--; + // encoders[2]->old = encoders[2]->cur; + slices = 1 << encoders[2]->cur; + } + uint8_t n = 14 + page_id; if (grid_page.active_slots[n] == SLOT_RAM_RECORD) { if ((RAMPage::rec_states[page_id] == STATE_QUEUE) && @@ -617,7 +628,7 @@ void RAMPage::display() { oled_display.print(encoders[1]->cur); oled_display.print(" S:"); oled_display.print(1 << encoders[2]->cur); - oled_display.print(" LEN:"); + oled_display.print(" L:"); oled_display.print(encoders[3]->cur * 4); uint8_t w_x = 104, w_y = 0; From 5c71971cc0d66213cb15fbf8c747dd5664db118f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 15:09:14 +0800 Subject: [PATCH 062/469] cleanup --- avr/cores/megacommand/MCL/RAMPage.d | 186 ---------------------------- avr/cores/megacommand/MCL/RAMPage.o | Bin 132188 -> 0 bytes 2 files changed, 186 deletions(-) delete mode 100644 avr/cores/megacommand/MCL/RAMPage.d delete mode 100644 avr/cores/megacommand/MCL/RAMPage.o diff --git a/avr/cores/megacommand/MCL/RAMPage.d b/avr/cores/megacommand/MCL/RAMPage.d deleted file mode 100644 index 3aeaedbf7..000000000 --- a/avr/cores/megacommand/MCL/RAMPage.d +++ /dev/null @@ -1,186 +0,0 @@ -/Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RAMPage.o: \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RAMPage.cpp \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCL.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/midi-common.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiNotes.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/WProgram.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Core.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/wiring_private.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Arduino.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/binary.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/WCharacter.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/WString.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/HardwareSerial.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Stream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Print.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Printable.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/variants/mega/pins_arduino.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/helpers.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/BitArray.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/helpers.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Task.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/helpers.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/mididuino_private.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/memory.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/LCD.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/LCDParent.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/OLED.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Arduino.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Print.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/gfxfont.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI_private.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MidiUart.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiUartParent.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Callback.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Vector.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiID.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/RingBuffer.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiClock.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/midi-common.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Stack.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/GUI.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/Task.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Encoders.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI_private.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/RecordingEncoder.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Events.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Pages.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/ModalGui.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/GUI.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Sketch.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/Midi.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/ListPool.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSysex.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MidiUart.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/WMath.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/Elektron.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/MNMDataEncoder.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/DataEncoder.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/CommonTools/DataEncoderUnchecking.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/ElektronDataEncoder.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Messages.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Pattern.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Elektron/ElektronPattern.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Params.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4Sysex.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/A4/A4.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSysex.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MD.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDEncoders.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDParams.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDMessages.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDPattern.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDSysex.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MD.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MD/MDTask.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLGfx.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLSd.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdFat.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SysCall.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SPI/SPI.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/BlockDriver.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/BaseBlockDriver.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatLibConfig.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdFatConfig.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SysCall.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/SdInfo.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/../FatLib/BaseBlockDriver.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/../SpiDriver/SdSpiDriver.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/../SpiDriver/SdSpiBaseDriver.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatLib.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ArduinoFiles.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatFile.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatApiConstants.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatStructs.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatVolume.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/BlockDriver.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ArduinoStream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/bufstream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/iostream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/istream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ios.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/ostream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/FatFileSystem.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/StdioStream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/FatLib/fstream.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/SdFat/SdCard/SdioCard.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLSysConfig.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/PolyPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Project.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/ProjectPages.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLEncoder.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/GUI/Encoders.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/NewProjectPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/LoadProjectPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/FileBrowserPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SoundBrowserPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Grid.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridPages.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridEncoder.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridRowHeader.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridSavePage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridIOPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridWritePage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Menu.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MenuPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridChain.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridTrack.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/A4Track.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/ExtTrack.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/ExtSeqTrack.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDTrack.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Bank1Object.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLMemory.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDSeqTrack.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDSeqTrackData.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/GridTask.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/EmptyTrack.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/LoudnessPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLActions.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLActionsEvents.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLGUI.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/TextInputPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLSeq.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqPages.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MCLMenus.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqParamPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqPtcPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MidiActivePeering.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiID.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MidiTools/Scales.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqRlckPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqRtrkPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqStepPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/SeqExtStepPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDExploit.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MDSound.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiIDSysex.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/Midi.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSDS.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Wav.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSDSSysex.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSDSMessages.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Midi/MidiSds.hh \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MidiSetup.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/NoteInterface.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/TurboLight.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/AuxPages.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/MixerPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RoutePage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/RAMPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Osc.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/OscMixerPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/OscPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Wav.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/WavDesigner.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/DSP.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/PageSelectPage.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/MCL/Shared.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.h \ - /Applications/Arduino.app/Contents/Java/hardware/valence/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/Elektrothic.h diff --git a/avr/cores/megacommand/MCL/RAMPage.o b/avr/cores/megacommand/MCL/RAMPage.o deleted file mode 100644 index 44384286b2049991b59ad8baed4c333c8661bcda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132188 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

IvDAn~FvZ$7NWR8~FOWvQV6VIUa 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 Date: Fri, 18 Oct 2019 20:18:21 +1100 Subject: [PATCH 063/469] Check-in --- avr/cores/megacommand/MCL/MCL.h | 2 ++ avr/cores/megacommand/MCL/RAMPage.h | 4 ++-- avr/cores/megacommand/MCL/RAMPage.o | Bin 132188 -> 0 bytes 3 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 avr/cores/megacommand/MCL/RAMPage.o diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index 4db6641bd..b8fb59d04 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -16,6 +16,8 @@ #define SOUND_PAGE #endif +#define NUM_RAM_PAGES 2 + #include "MCLGfx.h" #include "MCLSd.h" #include "MCLSysConfig.h" diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index 15fa8801e..8f7f77dd0 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -46,9 +46,9 @@ class RAMPage : public LightPage, MidiCallback { bool handleEvent(gui_event_t *event); bool midi_state = false; - uint8_t record_mode; + static uint8_t rec_states[NUM_RAM_PAGES]; + static uint8_t slice_modes[NUM_RAM_PAGES]; - uint8_t magic; uint8_t rec_state; uint8_t track1; uint8_t track2; diff --git a/avr/cores/megacommand/MCL/RAMPage.o b/avr/cores/megacommand/MCL/RAMPage.o deleted file mode 100644 index 44384286b2049991b59ad8baed4c333c8661bcda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132188 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

IvDAn~FvZ$7NWR8~FOWvQV6VIUa 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 Date: Fri, 18 Oct 2019 20:33:38 +1100 Subject: [PATCH 064/469] fix missing define --- avr/cores/megacommand/MCL/MCL.h | 2 -- avr/cores/megacommand/MCL/RAMPage.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index 7c5621d4d..ad24ab910 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -16,8 +16,6 @@ #define SOUND_PAGE #endif -#define NUM_RAM_PAGES 2 - #include "MCLGfx.h" #include "MCLSd.h" #include "MCLSysConfig.h" diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index 8f7f77dd0..bf93a4c67 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -6,6 +6,7 @@ #include "GUI.h" #include "MCLEncoder.h" +#define NUM_RAM_PAGES 2 #define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 #define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 From e44255c157b71ee4735c35cd15474216cbfa470d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 21:25:03 +1100 Subject: [PATCH 065/469] DEBUGMODE was defined by default. now disabled --- avr/cores/megacommand/WProgram.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/WProgram.h b/avr/cores/megacommand/WProgram.h index 885c19a11..fe5a2e29a 100644 --- a/avr/cores/megacommand/WProgram.h +++ b/avr/cores/megacommand/WProgram.h @@ -11,7 +11,7 @@ #include "wiring_private.h" -#define DEBUGMODE +//#define DEBUGMODE #ifdef MEGACOMMAND #define SD_CS 53 //PB0 From f47a05702893ef66df8f4abca773a755ea753b14 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 18 Oct 2019 21:44:01 +1100 Subject: [PATCH 066/469] improve rampage progess bar --- avr/cores/megacommand/MCL/RAMPage.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 9fa5927be..efe564c65 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -541,7 +541,7 @@ void RAMPage::display() { #endif #ifdef OLED_DISPLAY float remain; - oled_display.drawRoundRect(105, 28, 20, 4, 1, WHITE); + oled_display.drawRoundRect(104, 28, 22, 4, 1, WHITE); if ((RAMPage::rec_states[page_id] != STATE_NOSTATE)) { if (MidiClock.clock_less_than(transition_step + record_len, MidiClock.div16th_counter)) { @@ -555,10 +555,8 @@ void RAMPage::display() { remain = (float)mcl_seq.md_tracks[n].step_count / (float)mcl_seq.md_tracks[n].length; } - uint8_t width = remain * 20; - if (width >= 3) { - oled_display.fillRoundRect(105, 28, width, 4, 1, WHITE); - } + uint8_t width = remain * 21; + oled_display.fillRect(105, 28, width, 4, WHITE); } oled_display.setFont(); oled_display.setCursor(0, 0); From a615cac56ca15aa25fd207d0dd61fb2ac3480b55 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 18 Oct 2019 20:28:29 +0800 Subject: [PATCH 067/469] remove ram_config_page dup --- avr/cores/megacommand/MCL/MCLMenus.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index 051c2c9e4..dddb03da6 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -13,7 +13,6 @@ MCLEncoder config_param6(0, 17, ENCODER_RES_SYS); MCLEncoder config_param7(0, 17, ENCODER_RES_SYS); MenuPage aux_config_page(&auxconfig_menu_layout, &config_param1, &config_param6); -MenuPage ram_config_page(&rampage1_menu_layout, &config_param1, &config_param6); MenuPage system_page(&system_menu_layout, &options_param1, &options_param2); MenuPage midi_config_page(&midiconfig_menu_layout, &config_param1, &config_param3); From 3ac1cf7a9a69cb678bc89ce836eaf0c5f1f9e1e4 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 02:08:49 +0800 Subject: [PATCH 068/469] file menu POC --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 109 +++++++++++++++--- avr/cores/megacommand/MCL/FileBrowserPage.h | 20 +++- avr/cores/megacommand/MCL/LoadProjectPage.cpp | 9 +- avr/cores/megacommand/MCL/Menu.cpp | 54 +++++++-- avr/cores/megacommand/MCL/Menu.h | 7 +- avr/cores/megacommand/MCL/SDDrivePage.cpp | 11 +- avr/cores/megacommand/MCL/SDDrivePage.h | 1 + .../megacommand/MCL/SoundBrowserPage.cpp | 7 +- 8 files changed, 182 insertions(+), 36 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index a1b72128c..b69004990 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -1,5 +1,23 @@ #include "FileBrowserPage.h" -#include "MCL.h" + +const menu_t file_menu PROGMEM = { + "File", + 5, + { + {"NEW FOLDER", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"DELETE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"RENAME", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"OVERWRITE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"CANCEL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + }, + NULL, + (Page *)NULL, +}; + +MCLEncoder file_menu_encoder(0, 4, ENCODER_RES_PAT); +MCLEncoder file_menu_encoder2(0, 1, ENCODER_RES_PAT); +MenuPage file_menu_page((menu_t *)&file_menu, &file_menu_encoder, + &file_menu_encoder2); void FileBrowserPage::setup() { #ifdef OLED_DISPLAY @@ -23,6 +41,16 @@ void FileBrowserPage::init() { DEBUG_PRINT_FN(); char temp_entry[16]; + // config menu + file_menu_page.menu.enable_entry(0, show_new_folder); + file_menu_page.menu.enable_entry(1, true); // delete + file_menu_page.menu.enable_entry(2, true); // rename + file_menu_page.menu.enable_entry(3, show_overwrite); + file_menu_page.menu.enable_entry(4, true); // cancel + file_menu_encoder.cur = file_menu_encoder.old = 0; + file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; + filemenu_active = false; + int index = 0; // reset directory pointer SD.vwd()->rewind(); @@ -33,11 +61,6 @@ void FileBrowserPage::init() { add_entry(&create_new[0]); } - if (show_new_folder) { - char folder[16] = "[ NEW FOLDER ]"; - add_entry(&folder[0]); - } - char up_one_dir[3] = ".."; SD.vwd()->getName(temp_entry, 16); DEBUG_DUMP(temp_entry); @@ -164,6 +187,11 @@ void FileBrowserPage::draw_scrollbar(uint8_t x_offset) { void FileBrowserPage::loop() { + if (filemenu_active) { + file_menu_page.loop(); + return; + } + if (encoders[1]->hasChanged()) { uint8_t diff = encoders[1]->cur - encoders[1]->old; @@ -195,9 +223,8 @@ bool FileBrowserPage::create_folder() { return true; } -void FileBrowserPage::_calcindices(int &saveidx, int &newfolderidx) { +void FileBrowserPage::_calcindices(int &saveidx) { saveidx = show_save ? 0 : -1; - newfolderidx = show_new_folder ? (saveidx + 1) : -1; } void FileBrowserPage::_cd_up() { @@ -238,6 +265,47 @@ void FileBrowserPage::_cd(const char *child) { init(); } +void FileBrowserPage::_handle_filemenu() { + char buf1[16]; + uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; + volatile uint8_t *ptr = (uint8_t *)pos; + memcpy_bank1(&buf1[0], ptr, 16); + + char buf2[32] = { '\0' }; + + switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { + case 0: // new folder + create_folder(); + break; + case 1: // delete + strcat(buf2, "Delete "); + strcat(buf2, buf1); + strcat(buf2, "?"); + if(mcl_gui.wait_for_confirm("CONFIRM", buf2)) + { + on_delete(buf1); + } + break; + case 2: // overwrite + strcat(buf2, "Overwrite "); + strcat(buf2, buf1); + strcat(buf2, "?"); + if(mcl_gui.wait_for_confirm("CONFIRM", buf2)) + { + file.open(buf1, O_READ); + on_select(buf1); + } + break; + case 3: + strcat(buf2, buf1); + if(mcl_gui.wait_for_input(buf2, "RENAME TO:", 16)) + { + on_rename(buf1, buf2); + } + break; + } +} + bool FileBrowserPage::handleEvent(gui_event_t *event) { DEBUG_PRINT_FN(); @@ -246,24 +314,35 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { return false; } + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + filemenu_active = true; + encoders[0] = &file_menu_encoder; + encoders[1] = &file_menu_encoder2; + file_menu_page.init(); + return false; + } + + if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + filemenu_active = false; + encoders[0] = param1; + encoders[1] = param2; + _handle_filemenu(); + return false; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { - int i_save, i_newfolder; - _calcindices(i_save, i_newfolder); + int i_save; + _calcindices(i_save); if (encoders[1]->getValue() == i_save) { on_new(); return true; } - if (encoders[1]->getValue() == i_newfolder) { - create_folder(); - return false; - } - char temp_entry[16]; uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; volatile uint8_t *ptr = (uint8_t *)pos; diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 09d05b250..df798c223 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -3,6 +3,7 @@ #ifndef FILEBROWSERPAGE_H__ #define FILEBROWSERPAGE_H__ +#include "MCL.h" #include "GUI.h" #include "MCLEncoder.h" #include "SdFat.h" @@ -30,16 +31,25 @@ class FileBrowserPage : public LightPage { uint8_t cur_col = 0; uint8_t cur_row = 0; uint8_t cur_file = 0; + char title[12]; + File file; + + // configuration, should be set before calling base init() bool show_dirs = false; bool show_save = true; bool show_parent = true; + bool show_filemenu; bool show_new_folder = true; - char title[12]; - File file; + bool show_overwrite; + + bool filemenu_active; + + Encoder* param1; + Encoder* param2; FileBrowserPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4) {} + : LightPage(e1, e2, e3, e4), param1(e1), param2(e2) {} virtual bool handleEvent(gui_event_t *event); virtual void display(); void add_entry(char *entry); @@ -58,7 +68,9 @@ class FileBrowserPage : public LightPage { virtual void on_cancel() { GUI.popPage(); } private: - void _calcindices(int &, int &); + + void _handle_filemenu(); + void _calcindices(int &); void _cd_up(); void _cd(const char *); }; diff --git a/avr/cores/megacommand/MCL/LoadProjectPage.cpp b/avr/cores/megacommand/MCL/LoadProjectPage.cpp index f8e8251e6..d1ce354de 100644 --- a/avr/cores/megacommand/MCL/LoadProjectPage.cpp +++ b/avr/cores/megacommand/MCL/LoadProjectPage.cpp @@ -4,14 +4,17 @@ void LoadProjectPage::init() { DEBUG_PRINT_FN(); - show_save = false; - show_dirs = false; - show_new_folder = false; strcpy(match, ".mcl"); strcpy(title, "Project"); strcpy(lwd, "/"); SD.chdir("/"); + show_save = false; + show_dirs = false; + show_filemenu = true; + show_new_folder = false; + show_overwrite = false; + FileBrowserPage::init(); } diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index 2c8ea5bbd..ad6c13553 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -1,8 +1,26 @@ -#include "MCL.h" #include "Menu.h" +Menu::Menu() { memset(entry_mask, 0xFF, sizeof(entry_mask)); } + void Menu::set_layout(menu_t *menu_layout) { layout = menu_layout; } +void Menu::enable_entry(uint8_t entry_index, bool en) { + auto midx = entry_index / 8; + auto bit = entry_index % 8; + + if (en) { + entry_mask[midx] |= _BV(bit); + } else { + entry_mask[midx] &= ~_BV(bit); + } +} + +bool Menu::is_entry_enable(uint8_t entry_index) { + auto midx = entry_index / 8; + auto bit = entry_index % 8; + return bit_is_set(entry_mask[midx], bit); +} + PGM_P Menu::get_name() { return layout->name; } /* Page *Menu::get_exit_page_callback() { @@ -15,21 +33,36 @@ FP Menu::get_row_function(uint8_t item_n) { return pgm_read_word(&(item->row_function)); } - -FP Menu::get_exit_function() { - return pgm_read_word(&(layout->exit_function)); -} - +FP Menu::get_exit_function() { return pgm_read_word(&(layout->exit_function)); } uint8_t Menu::get_number_of_items() { - return pgm_read_byte(&(layout->number_of_items)); + uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); + uint8_t item_cnt = 0; + for (auto i = 0; i < entry_cnt; ++i) { + if (is_entry_enable(i)) + ++item_cnt; + } + return item_cnt; } menu_item_t *Menu::get_item(uint8_t item_n) { - if (item_n > get_number_of_items()) { - return &(layout->items[get_number_of_items() - 1]); + uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); + for(uint8_t idx = 0; idx < entry_cnt; ++idx) { + if(is_entry_enable(idx)) { + if (item_n == 0) { + return &layout->items[idx]; + }else { + --item_n; + } + } } - return &(layout->items[item_n]); + return nullptr; +} + +uint8_t Menu::get_item_index(uint8_t item_n) +{ + auto pentry = get_item(item_n); + return pentry - &layout->items[0]; } PGM_P Menu::get_item_name(uint8_t item_n) { @@ -54,7 +87,6 @@ uint8_t Menu::get_option_min(uint8_t item_n) { return pgm_read_byte(&(item->min)); } - uint8_t Menu::get_number_of_options(uint8_t item_n) { menu_item_t *item = get_item(item_n); return pgm_read_byte(&(item->number_of_options)); diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 4ffa9ca9d..72292eca0 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -1,5 +1,6 @@ #ifndef MENU_H__ #define MENU_H__ +#include "MCL.h" #define MAX_MENU_ITEMS 16 #define MAX_MENU_OPTIONS 16 @@ -35,15 +36,19 @@ class Menu { public: menu_t *layout; uint8_t values[MAX_MENU_ITEMS]; + uint8_t entry_mask[(MAX_MENU_ITEMS + 7)/8]; - Menu() {} + Menu(); void set_layout(menu_t *menu_layout); + void enable_entry(uint8_t entry_index, bool en); + bool is_entry_enable(uint8_t entry_index); PGM_P get_name(); uint8_t get_number_of_items(); menu_item_t *get_item(uint8_t item_n); + uint8_t get_item_index(uint8_t item_n); PGM_P get_item_name(uint8_t item_n); Page *get_page_callback(uint8_t item_n); //Page *get_exit_page_callback(); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 5984f84f0..2b154ecbf 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -17,8 +17,13 @@ void SDDrivePage::init() { md_exploit.off(); // !note match only supports 3-char suffix strcpy(match, c_snapshot_suffix); - show_dirs = true; strcpy(title, "SD-Drive"); + + show_save = true; + show_dirs = true; + show_filemenu = true; + show_new_folder = true; + show_overwrite = true; FileBrowserPage::init(); } @@ -143,6 +148,10 @@ void SDDrivePage::on_new() { init(); } +void SDDrivePage::on_delete(const char* file) { + gfx.alert("SDDrivePage::on_delete", file); +} + void SDDrivePage::on_select(const char *__) { load_snapshot(); } MCLEncoder sddrive_param1(1, 10, ENCODER_RES_SYS); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.h b/avr/cores/megacommand/MCL/SDDrivePage.h index 0c17bc008..4e3736c59 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.h +++ b/avr/cores/megacommand/MCL/SDDrivePage.h @@ -18,6 +18,7 @@ class SDDrivePage : public FileBrowserPage { void load_snapshot(); virtual void on_select(const char*); virtual void on_new(); + virtual void on_delete(const char*); }; extern SDDrivePage sddrive_page; diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp index a78cc4ba3..3eddc91f8 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp @@ -16,9 +16,14 @@ void SoundBrowserPage::init() { md_exploit.off(); char *snd = ".snd"; strcpy(match, snd); - show_dirs = true; char *files = "Sounds"; strcpy(title, files); + + show_dirs = true; + show_save = true; + show_filemenu = true; + show_new_folder = true; + show_overwrite = true; FileBrowserPage::init(); } From 616374a69989dafe04864e63afbd1555ccfc01ef Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 02:27:23 +0800 Subject: [PATCH 069/469] ... --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 1 + avr/cores/megacommand/MCL/FileBrowserPage.h | 1 - avr/cores/megacommand/MCL/Menu.cpp | 1 + avr/cores/megacommand/MCL/Menu.h | 19 ++++++++++--------- avr/cores/megacommand/MCL/MenuPage.cpp | 3 +-- avr/cores/megacommand/MCL/SDDrivePage.cpp | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index b69004990..1a9c7e2c4 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -1,3 +1,4 @@ +#include "MCL.h" #include "FileBrowserPage.h" const menu_t file_menu PROGMEM = { diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index df798c223..68a73b3b5 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -3,7 +3,6 @@ #ifndef FILEBROWSERPAGE_H__ #define FILEBROWSERPAGE_H__ -#include "MCL.h" #include "GUI.h" #include "MCLEncoder.h" #include "SdFat.h" diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index ad6c13553..8d0b636dc 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -1,3 +1,4 @@ +#include "MCL.h" #include "Menu.h" Menu::Menu() { memset(entry_mask, 0xFF, sizeof(entry_mask)); } diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 72292eca0..78cdb4570 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -1,18 +1,19 @@ #ifndef MENU_H__ #define MENU_H__ + #include "MCL.h" #define MAX_MENU_ITEMS 16 #define MAX_MENU_OPTIONS 16 -typedef void(*FP)(); +typedef void (*FP)(); -typedef struct menu_option_s { +struct menu_option_t { uint8_t pos; char name[17]; -} menu_option_t; +}; -typedef struct menu_item_s { +struct menu_item_t { char name[17]; uint8_t min; uint8_t range; @@ -21,22 +22,22 @@ typedef struct menu_item_s { Page *page_callback; void (*row_function)(); menu_option_t options[MAX_MENU_OPTIONS]; -} menu_item_t; +}; -typedef struct menu_s { +struct menu_t { char name[11]; uint8_t number_of_items; menu_item_t items[MAX_MENU_ITEMS]; void (*exit_function)(); Page *exit_page_callback; -} menu_t; +}; class Menu { public: menu_t *layout; uint8_t values[MAX_MENU_ITEMS]; - uint8_t entry_mask[(MAX_MENU_ITEMS + 7)/8]; + uint8_t entry_mask[(MAX_MENU_ITEMS + 7) / 8]; Menu(); @@ -51,7 +52,7 @@ class Menu { uint8_t get_item_index(uint8_t item_n); PGM_P get_item_name(uint8_t item_n); Page *get_page_callback(uint8_t item_n); - //Page *get_exit_page_callback(); + // Page *get_exit_page_callback(); FP get_exit_function(); FP get_row_function(uint8_t item_n); uint8_t *get_dest_variable(uint8_t item_n); diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index e930faa25..3d3465558 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -1,6 +1,5 @@ - -#include "MenuPage.h" #include "MCL.h" +#include "MenuPage.h" void MenuPage::init() { ((MCLEncoder *)encoders[1])->max = menu.get_number_of_items() - 1; diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 2b154ecbf..4f6325662 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -1,5 +1,5 @@ -#include "SDDrivePage.h" #include "MCL.h" +#include "SDDrivePage.h" const char *c_snapshot_suffix = ".snp"; const char *c_snapshot_root = "/SDDrive/MD"; From 4a87401e1a9233fc9f446cdbd73ffeababcb71ac Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 03:09:15 +0800 Subject: [PATCH 070/469] work in progress --- avr/cores/megacommand/GUI/Pages.hh | 4 +-- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 31 +++++++++++-------- avr/cores/megacommand/MCL/Menu.cpp | 2 -- avr/cores/megacommand/MCL/Menu.h | 4 +-- avr/cores/megacommand/MCL/MenuPage.cpp | 2 +- avr/cores/megacommand/WProgram.h | 2 +- 6 files changed, 23 insertions(+), 22 deletions(-) diff --git a/avr/cores/megacommand/GUI/Pages.hh b/avr/cores/megacommand/GUI/Pages.hh index df03afb0e..536c52b38 100644 --- a/avr/cores/megacommand/GUI/Pages.hh +++ b/avr/cores/megacommand/GUI/Pages.hh @@ -184,11 +184,11 @@ public: virtual void clear(); /** Display the page using the display routine as specfied by function * pointer*/ - virtual void display(); + virtual void display() {} /** Executes the encoder actions by calling checkHandle() on each encoder. **/ virtual void finalize(); /** Set the display routine to supplied fuction pointer **/ - virtual void setup(); + virtual void setup() {} /** Call this to lock all encoders in the page. **/ void lockEncoders(); /** Call this to unlock all encoders in the page. If their value diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 1a9c7e2c4..355601f69 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "FileBrowserPage.h" +#include "MCL.h" const menu_t file_menu PROGMEM = { "File", @@ -15,8 +15,8 @@ const menu_t file_menu PROGMEM = { (Page *)NULL, }; -MCLEncoder file_menu_encoder(0, 4, ENCODER_RES_PAT); -MCLEncoder file_menu_encoder2(0, 1, ENCODER_RES_PAT); +MCLEncoder file_menu_encoder(0, 1, ENCODER_RES_PAT); +MCLEncoder file_menu_encoder2(0, 4, ENCODER_RES_PAT); MenuPage file_menu_page((menu_t *)&file_menu, &file_menu_encoder, &file_menu_encoder2); @@ -48,8 +48,8 @@ void FileBrowserPage::init() { file_menu_page.menu.enable_entry(2, true); // rename file_menu_page.menu.enable_entry(3, show_overwrite); file_menu_page.menu.enable_entry(4, true); // cancel - file_menu_encoder.cur = file_menu_encoder.old = 0; - file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; + file_menu_encoder2.cur = file_menu_encoder2.old = 0; + file_menu_encoder2.max = file_menu_page.menu.get_number_of_items() - 1; filemenu_active = false; int index = 0; @@ -118,6 +118,13 @@ void FileBrowserPage::init() { void FileBrowserPage::display() { #ifdef OLED_DISPLAY + if (filemenu_active) { + oled_display.fillRect(0, 8, 38, 24, BLACK); + file_menu_page.draw_menu(0, 14, 38); + oled_display.display(); + return; + } + constexpr uint8_t x_offset = 43, y_offset = 8, width = MENU_WIDTH; oled_display.clearDisplay(); oled_display.setFont(&TomThumb); @@ -157,6 +164,7 @@ void FileBrowserPage::display() { if (numEntries > MAX_VISIBLE_ROWS) { draw_scrollbar(120); } + oled_display.display(); #else GUI.setLine(GUI.LINE1); @@ -272,9 +280,9 @@ void FileBrowserPage::_handle_filemenu() { volatile uint8_t *ptr = (uint8_t *)pos; memcpy_bank1(&buf1[0], ptr, 16); - char buf2[32] = { '\0' }; + char buf2[32] = {'\0'}; - switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { + switch (file_menu_page.menu.get_item_index(file_menu_encoder2.cur)) { case 0: // new folder create_folder(); break; @@ -282,8 +290,7 @@ void FileBrowserPage::_handle_filemenu() { strcat(buf2, "Delete "); strcat(buf2, buf1); strcat(buf2, "?"); - if(mcl_gui.wait_for_confirm("CONFIRM", buf2)) - { + if (mcl_gui.wait_for_confirm("CONFIRM", buf2)) { on_delete(buf1); } break; @@ -291,16 +298,14 @@ void FileBrowserPage::_handle_filemenu() { strcat(buf2, "Overwrite "); strcat(buf2, buf1); strcat(buf2, "?"); - if(mcl_gui.wait_for_confirm("CONFIRM", buf2)) - { + if (mcl_gui.wait_for_confirm("CONFIRM", buf2)) { file.open(buf1, O_READ); on_select(buf1); } break; case 3: strcat(buf2, buf1); - if(mcl_gui.wait_for_input(buf2, "RENAME TO:", 16)) - { + if (mcl_gui.wait_for_input(buf2, "RENAME TO:", 16)) { on_rename(buf1, buf2); } break; diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index 8d0b636dc..076fa7894 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -1,8 +1,6 @@ #include "MCL.h" #include "Menu.h" -Menu::Menu() { memset(entry_mask, 0xFF, sizeof(entry_mask)); } - void Menu::set_layout(menu_t *menu_layout) { layout = menu_layout; } void Menu::enable_entry(uint8_t entry_index, bool en) { diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 78cdb4570..ad725b76f 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -1,8 +1,6 @@ #ifndef MENU_H__ #define MENU_H__ -#include "MCL.h" - #define MAX_MENU_ITEMS 16 #define MAX_MENU_OPTIONS 16 @@ -39,7 +37,7 @@ class Menu { uint8_t values[MAX_MENU_ITEMS]; uint8_t entry_mask[(MAX_MENU_ITEMS + 7) / 8]; - Menu(); + Menu(){ memset(entry_mask, 0xFF, sizeof(entry_mask)); } void set_layout(menu_t *menu_layout); void enable_entry(uint8_t entry_index, bool en); diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 3d3465558..a4b5ef691 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "MenuPage.h" +#include "MCL.h" void MenuPage::init() { ((MCLEncoder *)encoders[1])->max = menu.get_number_of_items() - 1; diff --git a/avr/cores/megacommand/WProgram.h b/avr/cores/megacommand/WProgram.h index fe5a2e29a..885c19a11 100644 --- a/avr/cores/megacommand/WProgram.h +++ b/avr/cores/megacommand/WProgram.h @@ -11,7 +11,7 @@ #include "wiring_private.h" -//#define DEBUGMODE +#define DEBUGMODE #ifdef MEGACOMMAND #define SD_CS 53 //PB0 From dcdcd1ed81a1926afa7c671e74d16f8c6cd502e6 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 19 Oct 2019 13:04:02 +1100 Subject: [PATCH 071/469] Add MD style light font. --- art/fonts/md_light.ttf | Bin 0 -> 6904 bytes art/fonts/md_light.txt | 3 + .../Adafruit-GFX-Library/Fonts/md_light.h | 132 ++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 art/fonts/md_light.ttf create mode 100644 art/fonts/md_light.txt create mode 100644 avr/cores/megacommand/Adafruit-GFX-Library/Fonts/md_light.h diff --git a/art/fonts/md_light.ttf b/art/fonts/md_light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..bc0f355cb4cd8252c1cf4e46c14d307339d90655 GIT binary patch literal 6904 zcmb_hTWlOx8UD}A?9RHmI`%d|I%%2?o9%Q52#)K#>p+Ab7|_A5atm2_UKf(jcKyL>?ljq80@jL4|~9`%nRDxpevd|D2gU z<4gL&v1ZPH&iQZWzn!yVQbe?rLUQT(!NJ4(zA*UyW}-EBG2621i6;l?Jgvw0M;LEB zaA-@20wPPIN z**kIa_|Q-9MPCQ&pJDyGlSAiD(YNUk)^=muIyE#o^4H&7{T|V(e#~7wb@I&g@(;Se zfAuf1{ukDSpKa?tc=5Sq{r{kK9wvz1xZSeI?p5Gi!8k__&!T}wW{zrn6$mH2rr(ET z<`8tv$^=s?nG2{(W|V4Hf2RyMeapFpnXlmhFT789V`i5Ir7>>W|sBUc)rw$sG6hdl)zT2prIhOvVK)PiVpMT-=kj z#(4JF#);>-x1M-k$y{iy2ZpzWpX}AfhhTi&>$ zsX6~35LePFTD@j%OW~n)t!?ea^$&08*tqGD&drZ@Z7Fs4FsCwotg^Lt+hg1N`l~x0 z4+eIA9F<_#?x*%Vy?382>Hh%lzaNhS_gR!WLywrh9dwLN(^ufHm+4h{jowy^)p|9k zM%8)sl6pn`$;mn!oxnNfTy(BEcbxa#Rc?>_tb5MAy&;Mtx zIakUZ%uVIKnY)?0lY1}sUPE)kj)sYb33v!Os(O&QE^@o;x}q)pH++xY>A9ZG-0i>N zIsoaq%hMU2zTs!pfBJ90z3=pt8`~N?%8kW#wfx;%x11~gJ<{%s#$FE)$1b6Ibyv#y zj;d2GI03g%-T~|<(V-Y`HLZhdJDsZ6=jJ<|Jl@5E>L^z_RX)=A$vaFS0|=jpLXqba zRaH69PY^_c0WG-c^+mJ{l31f6tE4od%`yxEV9%l9T~u9cqN7+T+xRRd4*Li-06Q*M zfCKz;z6dSq4SYF00$_!(HuaXV*-AMS47|-cW3cVSa#+K((I>(lGsqk4p&OcO^<^Kz zA~9|tHi0nUH`3a~wgs=FTy;8C!HH%On4#_xJy>2NQ>zN-ij_W9aLf5Hh0Fu9f00+v z`*N#JpHsp1`rLv8mzJYBRH=zrrtr`jSD1lh2fxM z!}!{k&few>>P_X5p-S_@F+%QIE9QR(2{> zCB$#O$b9>hWOZ1lT>t{V3@ayoagno2;8VnPFY1Diyhla>0&@=0UZWth4Y_PFkn{{= z#?r4rIGI#VG%Ps9d2&;z&C8B;)&&Zb2UobJd2GPGOv1F@DYWLh#PZe;^aYD z$ajdTxk9AsW~f=9Gmswh+t^CtmhU%J7m4NI4mBrcS$^Fv8PsUlmc{T1&whr7&^atZ ze8cS1&7xld?KuY}`*1VhWD)W-g*}3Nhn0|;pHJ?6kgPMuK@$>kJbR{OWe`O|6Dq#3$mAIBbLwbI`OAgGmbkv2JxuLvP5YHOp@EnDq{54HUDpM~NhGWCVtH{)v1A|d-h_LIoTe~AzE`T4 zFK@Oe+PK-)g2PkO(t|q3azh#Qz<)Y^1WzBWFa{C|#Ph2Mfyutu z|4S_I^9?7|(#$)K*8s7?_ryHBjvQ%|GeW=hp~=zetG>CK91rR67TpCEM-~CuRdpcc}I5JR{xQleu&&MW?OXjgi@)|Pb zm@(H0XgY@uvUN@57}K)loMnBXb-q_DrQK_(5HhDZ%pd@b_Y`n|3ZK3ps3w&o+@&PwZ0=NbcTW?fn%dowZs&nz=GcV|8?4Bz-R z!Y8;%KS_d^Gc3oWUOLCVP9O@`O7)y57%m5=f}K%_2CcR' + { 107, 5, 5, 6, 0, -4 }, // 0x3F '?' + { 111, 4, 11, 6, 1, -10 }, // 0x40 '@' + { 117, 4, 5, 5, 0, -4 }, // 0x41 'A' + { 120, 4, 5, 5, 0, -4 }, // 0x42 'B' + { 123, 4, 5, 5, 0, -4 }, // 0x43 'C' + { 126, 4, 5, 5, 0, -4 }, // 0x44 'D' + { 129, 4, 5, 5, 0, -4 }, // 0x45 'E' + { 132, 4, 5, 5, 0, -4 }, // 0x46 'F' + { 135, 4, 5, 5, 0, -4 }, // 0x47 'G' + { 138, 4, 5, 5, 0, -4 }, // 0x48 'H' + { 141, 1, 5, 2, 0, -4 }, // 0x49 'I' + { 142, 3, 5, 4, 0, -4 }, // 0x4A 'J' + { 144, 4, 5, 5, 0, -4 }, // 0x4B 'K' + { 147, 4, 5, 5, 0, -4 }, // 0x4C 'L' + { 150, 5, 5, 6, 0, -4 }, // 0x4D 'M' + { 154, 4, 5, 5, 0, -4 }, // 0x4E 'N' + { 157, 4, 5, 5, 0, -4 }, // 0x4F 'O' + { 160, 4, 5, 5, 0, -4 }, // 0x50 'P' + { 163, 4, 5, 5, 0, -4 }, // 0x51 'Q' + { 166, 4, 5, 5, 0, -4 }, // 0x52 'R' + { 169, 4, 5, 5, 0, -4 }, // 0x53 'S' + { 172, 3, 5, 4, 0, -4 }, // 0x54 'T' + { 174, 4, 5, 5, 0, -4 }, // 0x55 'U' + { 177, 4, 5, 5, 0, -4 }, // 0x56 'V' + { 180, 5, 5, 6, 0, -4 }, // 0x57 'W' + { 184, 3, 5, 4, 0, -4 }, // 0x58 'X' + { 186, 4, 5, 5, 0, -4 }, // 0x59 'Y' + { 189, 4, 5, 5, 0, -4 }, // 0x5A 'Z' + { 192, 4, 11, 6, 1, -10 }, // 0x5B '[' + { 198, 4, 11, 6, 1, -10 }, // 0x5C '\' + { 204, 4, 11, 6, 1, -10 }, // 0x5D ']' + { 210, 4, 11, 6, 1, -10 }, // 0x5E '^' + { 216, 4, 1, 5, 0, 0 }, // 0x5F '_' + { 217, 4, 11, 6, 1, -10 }, // 0x60 '`' + { 223, 4, 4, 5, 0, -3 }, // 0x61 'a' + { 225, 3, 5, 4, 0, -4 }, // 0x62 'b' + { 227, 3, 4, 4, 0, -3 }, // 0x63 'c' + { 229, 4, 5, 5, 0, -4 }, // 0x64 'd' + { 232, 3, 4, 4, 0, -3 }, // 0x65 'e' + { 234, 3, 4, 4, 0, -3 }, // 0x66 'f' + { 236, 3, 4, 4, 0, -3 }, // 0x67 'g' + { 238, 3, 5, 4, 0, -4 }, // 0x68 'h' + { 240, 2, 5, 3, 0, -4 }, // 0x69 'i' + { 242, 2, 5, 3, 0, -4 }, // 0x6A 'j' + { 244, 3, 5, 4, 0, -4 }, // 0x6B 'k' + { 246, 3, 4, 4, 0, -3 }, // 0x6C 'l' + { 248, 5, 4, 6, 0, -3 }, // 0x6D 'm' + { 251, 3, 4, 4, 0, -3 }, // 0x6E 'n' + { 253, 3, 4, 4, 0, -3 }, // 0x6F 'o' + { 255, 4, 4, 5, 0, -3 }, // 0x70 'p' + { 257, 4, 4, 5, 0, -3 }, // 0x71 'q' + { 259, 3, 4, 4, 0, -3 }, // 0x72 'r' + { 261, 3, 4, 4, 0, -3 }, // 0x73 's' + { 263, 3, 5, 4, 0, -4 }, // 0x74 't' + { 265, 4, 4, 5, 0, -3 }, // 0x75 'u' + { 267, 3, 4, 4, 0, -3 }, // 0x76 'v' + { 269, 5, 4, 6, 0, -3 }, // 0x77 'w' + { 272, 3, 4, 4, 0, -3 }, // 0x78 'x' + { 274, 3, 4, 4, 0, -3 }, // 0x79 'y' + { 276, 3, 4, 4, 0, -3 }, // 0x7A 'z' + { 278, 4, 11, 6, 1, -10 }, // 0x7B '{' + { 284, 4, 11, 6, 1, -10 }, // 0x7C '|' + { 290, 4, 11, 6, 1, -10 }, // 0x7D '}' + { 296, 4, 11, 6, 1, -10 } }; // 0x7E '~' + +const GFXfont mdlight8pt7b PROGMEM = { + (uint8_t *)mdlight8pt7bBitmaps, + (GFXglyph *)mdlight8pt7bGlyphs, + 0x20, 0x7E, 12 }; + +// Approx. 974 bytes + From f5596b728b66726744e499328f653d06b6d64298 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 19 Oct 2019 13:06:35 +1100 Subject: [PATCH 072/469] Organise directories. Design folder now in top level dir --- art/{ => sprites}/mcl_logo.png | Bin art/{ => sprites}/wheel1.png | Bin art/{ => sprites}/wheel2.png | Bin art/{ => sprites}/wheel3.png | Bin .../Design => design}/Menu_scratchpad.png | Bin {avr/cores/megacommand/Design => design}/icons.xcf | Bin .../cores/megacommand/Design => design}/infobox.png | Bin .../megacommand/Design => design}/menu_scratch.xcf | Bin .../megacommand/Design => design}/popup_menu.png | Bin 9 files changed, 0 insertions(+), 0 deletions(-) rename art/{ => sprites}/mcl_logo.png (100%) rename art/{ => sprites}/wheel1.png (100%) rename art/{ => sprites}/wheel2.png (100%) rename art/{ => sprites}/wheel3.png (100%) rename {avr/cores/megacommand/Design => design}/Menu_scratchpad.png (100%) rename {avr/cores/megacommand/Design => design}/icons.xcf (100%) rename {avr/cores/megacommand/Design => design}/infobox.png (100%) rename {avr/cores/megacommand/Design => design}/menu_scratch.xcf (100%) rename {avr/cores/megacommand/Design => design}/popup_menu.png (100%) diff --git a/art/mcl_logo.png b/art/sprites/mcl_logo.png similarity index 100% rename from art/mcl_logo.png rename to art/sprites/mcl_logo.png diff --git a/art/wheel1.png b/art/sprites/wheel1.png similarity index 100% rename from art/wheel1.png rename to art/sprites/wheel1.png diff --git a/art/wheel2.png b/art/sprites/wheel2.png similarity index 100% rename from art/wheel2.png rename to art/sprites/wheel2.png diff --git a/art/wheel3.png b/art/sprites/wheel3.png similarity index 100% rename from art/wheel3.png rename to art/sprites/wheel3.png diff --git a/avr/cores/megacommand/Design/Menu_scratchpad.png b/design/Menu_scratchpad.png similarity index 100% rename from avr/cores/megacommand/Design/Menu_scratchpad.png rename to design/Menu_scratchpad.png diff --git a/avr/cores/megacommand/Design/icons.xcf b/design/icons.xcf similarity index 100% rename from avr/cores/megacommand/Design/icons.xcf rename to design/icons.xcf diff --git a/avr/cores/megacommand/Design/infobox.png b/design/infobox.png similarity index 100% rename from avr/cores/megacommand/Design/infobox.png rename to design/infobox.png diff --git a/avr/cores/megacommand/Design/menu_scratch.xcf b/design/menu_scratch.xcf similarity index 100% rename from avr/cores/megacommand/Design/menu_scratch.xcf rename to design/menu_scratch.xcf diff --git a/avr/cores/megacommand/Design/popup_menu.png b/design/popup_menu.png similarity index 100% rename from avr/cores/megacommand/Design/popup_menu.png rename to design/popup_menu.png From ba35567419432d215e3e432f93552b88554ccfcb Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 19 Oct 2019 14:44:51 +1100 Subject: [PATCH 073/469] Add larger version of md_light font --- art/fonts/md_light_larger.txt | 3 + art/fonts/mdlightlarger.ttf | Bin 0 -> 8028 bytes .../Fonts/md_light_larger.h | 141 ++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 art/fonts/md_light_larger.txt create mode 100644 art/fonts/mdlightlarger.ttf create mode 100644 avr/cores/megacommand/Adafruit-GFX-Library/Fonts/md_light_larger.h diff --git a/art/fonts/md_light_larger.txt b/art/fonts/md_light_larger.txt new file mode 100644 index 000000000..b777d7967 --- /dev/null +++ b/art/fonts/md_light_larger.txt @@ -0,0 +1,3 @@ +https://www.pentacom.jp/pentacom/bitfontmaker2/ + +{"33":[0,0,0,0,0,4,4,4,4,4,0,4,0,0,0,0],"38":[0,0,0,0,0,124,4,4,252,68,68,124,0,0,0,0],"39":[0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0],"40":[0,0,0,0,0,8,4,4,4,4,4,8,0,0,0,0],"41":[0,0,0,0,0,4,8,8,8,8,8,4,0,0,0,0],"43":[0,0,0,0,0,0,0,16,16,124,16,16,0,0,0,0],"45":[0,0,0,0,0,0,0,0,0,124,0,0,0,0,0,0],"46":[0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0],"47":[0,0,0,0,0,0,128,64,32,16,8,4,0,0,0,0],"48":[0,0,0,0,0,124,68,68,68,68,68,124,0,0,0,0],"49":[0,0,0,0,0,28,16,16,16,16,16,124,0,0,0,0],"50":[0,0,0,0,0,124,64,64,124,4,4,124,0,0,0,0],"51":[0,0,0,0,0,124,64,64,120,64,64,124,0,0,0,0],"52":[0,0,0,0,0,68,68,68,124,64,64,64,0,0,0,0],"53":[0,0,0,0,0,124,4,4,124,64,64,124,0,0,0,0],"54":[0,0,0,0,0,124,4,4,124,68,68,124,0,0,0,0],"55":[0,0,0,0,0,124,64,64,32,16,8,8,0,0,0,0],"56":[0,0,0,0,0,124,68,68,124,68,68,124,0,0,0,0],"57":[0,0,0,0,0,124,68,68,124,64,64,124,0,0,0,0],"61":[0,0,0,0,0,0,0,0,124,0,124,0,0,0,0,0],"63":[0,0,0,0,0,124,68,64,32,16,0,16,0,0,0,0],"65":[0,0,0,0,0,56,68,68,124,68,68,68,0,0,0,0],"66":[0,0,0,0,0,60,68,68,60,68,68,60,0,0,0,0],"67":[0,0,0,0,0,56,68,4,4,4,68,56,0,0,0,0],"68":[0,0,0,0,0,60,68,68,68,68,68,60,0,0,0,0],"69":[0,0,0,0,0,124,4,4,60,4,4,124,0,0,0,0],"70":[0,0,0,0,0,124,4,4,60,4,4,4,0,0,0,0],"71":[0,0,0,0,0,56,4,4,100,68,68,56,0,0,0,0],"72":[0,0,0,0,0,68,68,68,124,68,68,68,0,0,0,0],"73":[0,0,0,0,0,4,4,4,4,4,4,4,0,0,0,0],"74":[0,0,0,0,0,32,32,32,32,32,36,28,0,0,0,0],"75":[0,0,0,0,0,68,68,68,60,68,68,68,0,0,0,0],"76":[0,0,0,0,0,4,4,4,4,4,4,124,0,0,0,0],"77":[0,0,0,0,0,40,84,84,84,68,68,68,0,0,0,0],"78":[0,0,0,0,0,56,68,68,68,68,68,68,0,0,0,0],"79":[0,0,0,0,0,56,68,68,68,68,68,56,0,0,0,0],"80":[0,0,0,0,0,60,68,68,68,60,4,4,0,0,0,0],"81":[0,0,0,0,0,56,68,68,68,68,100,120,0,0,0,0],"82":[0,0,0,0,0,56,68,68,60,68,68,68,0,0,0,0],"83":[0,0,0,0,0,120,4,4,56,64,64,60,0,0,0,0],"84":[0,0,0,0,0,124,16,16,16,16,16,16,0,0,0,0],"85":[0,0,0,0,0,68,68,68,68,68,68,56,0,0,0,0],"86":[0,0,0,0,0,68,68,68,68,68,36,28,0,0,0,0],"87":[0,0,0,0,0,68,68,68,84,84,84,120,0,0,0,0],"88":[0,0,0,0,0,68,68,68,56,68,68,68,0,0,0,0],"89":[0,0,0,0,0,68,68,68,56,64,64,60,0,0,0,0],"90":[0,0,0,0,0,124,64,32,16,8,4,124,0,0,0,0],"95":[0,0,0,0,0,0,0,0,0,0,0,252,0,0,0,0],"97":[0,0,0,0,0,0,24,36,36,36,36,88,0,0,0,0],"98":[0,0,0,0,0,4,28,36,36,36,36,28,0,0,0,0],"99":[0,0,0,0,0,0,24,36,4,4,36,24,0,0,0,0],"100":[0,0,0,0,0,32,56,36,36,36,36,88,0,0,0,0],"101":[0,0,0,0,0,0,24,36,36,28,4,56,0,0,0,0],"102":[0,0,0,0,0,16,8,28,8,8,8,4,0,0,0,0],"103":[0,0,0,0,0,0,24,36,36,56,32,28,0,0,0,0],"104":[0,0,0,0,0,4,28,36,36,36,36,36,0,0,0,0],"105":[0,0,0,0,0,4,0,4,4,4,4,24,0,0,0,0],"106":[0,0,0,0,0,8,0,8,8,8,8,4,0,0,0,0],"107":[0,0,0,0,0,4,36,20,28,36,36,36,0,0,0,0],"108":[0,0,0,0,0,0,4,4,4,4,4,24,0,0,0,0],"109":[0,0,0,0,0,0,40,84,84,68,68,68,0,0,0,0],"110":[0,0,0,0,0,0,24,36,36,36,36,36,0,0,0,0],"111":[0,0,0,0,0,0,24,36,36,36,36,24,0,0,0,0],"112":[0,0,0,0,0,0,24,36,36,28,4,4,0,0,0,0],"113":[0,0,0,0,0,0,24,36,36,56,32,32,0,0,0,0],"114":[0,0,0,0,0,0,24,4,4,4,4,4,0,0,0,0],"115":[0,0,0,0,0,0,56,4,28,32,32,28,0,0,0,0],"116":[0,0,0,0,0,8,28,8,8,8,8,16,0,0,0,0],"117":[0,0,0,0,0,0,36,36,36,36,36,88,0,0,0,0],"118":[0,0,0,0,0,0,36,36,36,36,36,24,0,0,0,0],"119":[0,0,0,0,0,0,68,84,84,84,84,40,0,0,0,0],"120":[0,0,0,0,0,0,20,8,8,8,8,20,0,0,0,0],"121":[0,0,0,0,0,0,36,36,56,32,32,24,0,0,0,0],"122":[0,0,0,0,0,0,60,32,16,8,4,60,0,0,0,0],"193":[0,0,0,0,0,56,124,68,68,124,68,68,0,0,0,0],"258":[0,0,0,0,0,68,56,68,124,68,68,68,0,0,0,0],"334":[0,0,0,0,0,68,56,68,68,68,68,56,0,0,0,0],"name":"mdlightlarger","copy":"JustinM","letterspace":"128","basefont_size":"512","basefont_left":"62","basefont_top":"0","basefont":"Arial","basefont2":""} diff --git a/art/fonts/mdlightlarger.ttf b/art/fonts/mdlightlarger.ttf new file mode 100644 index 0000000000000000000000000000000000000000..17d0bc1f16f85b7986162e050be8199602bb0078 GIT binary patch literal 8028 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 literal 0 HcmV?d00001 diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/md_light_larger.h b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/md_light_larger.h new file mode 100644 index 000000000..6bb6b9f38 --- /dev/null +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/md_light_larger.h @@ -0,0 +1,141 @@ +const uint8_t mdlightlarger8pt7bBitmaps[] PROGMEM = { + 0xFA, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, + 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, + 0xF0, 0xFA, 0x08, 0x3F, 0x8A, 0x2F, 0x80, 0x60, 0x6A, 0xA4, 0x95, 0x58, + 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0x21, 0x3E, 0x42, 0x00, 0xF9, 0x99, + 0x99, 0x99, 0x99, 0xF0, 0xF8, 0xF0, 0x04, 0x21, 0x08, 0x42, 0x00, 0xFC, + 0x63, 0x18, 0xC7, 0xE0, 0xE1, 0x08, 0x42, 0x13, 0xE0, 0xF8, 0x43, 0xF8, + 0x43, 0xE0, 0xF8, 0x42, 0xF0, 0x87, 0xE0, 0x8C, 0x63, 0xF0, 0x84, 0x20, + 0xFC, 0x21, 0xF0, 0x87, 0xE0, 0xFC, 0x21, 0xF8, 0xC7, 0xE0, 0xF8, 0x42, + 0x22, 0x21, 0x00, 0xFC, 0x63, 0xF8, 0xC7, 0xE0, 0xFC, 0x63, 0xF0, 0x87, + 0xE0, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, + 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xF8, 0x3E, 0xF9, 0x99, 0x99, + 0x99, 0x99, 0xF0, 0xFC, 0x42, 0x22, 0x00, 0x80, 0xF9, 0x99, 0x99, 0x99, + 0x99, 0xF0, 0x74, 0x63, 0xF8, 0xC6, 0x20, 0xF4, 0x63, 0xE8, 0xC7, 0xC0, + 0x74, 0x61, 0x08, 0x45, 0xC0, 0xF4, 0x63, 0x18, 0xC7, 0xC0, 0xFC, 0x21, + 0xE8, 0x43, 0xE0, 0xFC, 0x21, 0xE8, 0x42, 0x00, 0x74, 0x21, 0x38, 0xC5, + 0xC0, 0x8C, 0x63, 0xF8, 0xC6, 0x20, 0xFE, 0x11, 0x11, 0x19, 0xE0, 0x8C, + 0x63, 0xE8, 0xC6, 0x20, 0x84, 0x21, 0x08, 0x43, 0xE0, 0x55, 0x6B, 0x58, + 0xC6, 0x20, 0x74, 0x63, 0x18, 0xC6, 0x20, 0x74, 0x63, 0x18, 0xC5, 0xC0, + 0xF4, 0x63, 0x1F, 0x42, 0x00, 0x74, 0x63, 0x18, 0xCD, 0xE0, 0x74, 0x63, + 0xE8, 0xC6, 0x20, 0x7C, 0x20, 0xE0, 0x87, 0xC0, 0xF9, 0x08, 0x42, 0x10, + 0x80, 0x8C, 0x63, 0x18, 0xC5, 0xC0, 0x8C, 0x63, 0x18, 0xCB, 0x80, 0x8C, + 0x63, 0x5A, 0xD5, 0xE0, 0x8C, 0x62, 0xE8, 0xC6, 0x20, 0x8C, 0x62, 0xE0, + 0x87, 0xC0, 0xF8, 0x44, 0x44, 0x43, 0xE0, 0xF9, 0x99, 0x99, 0x99, 0x99, + 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, + 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xFC, 0xF9, 0x99, 0x99, 0x99, + 0x99, 0xF0, 0x64, 0xA5, 0x29, 0x34, 0x8E, 0x99, 0x99, 0xE0, 0x69, 0x88, + 0x96, 0x13, 0xA5, 0x29, 0x49, 0xA0, 0x69, 0x9E, 0x87, 0x2B, 0xA4, 0xA0, + 0x69, 0x97, 0x1E, 0x8E, 0x99, 0x99, 0x90, 0x82, 0x49, 0x18, 0x45, 0x58, + 0x89, 0xAE, 0x99, 0x90, 0x92, 0x48, 0xC0, 0x55, 0x6B, 0x18, 0xC4, 0x69, + 0x99, 0x99, 0x69, 0x99, 0x96, 0x69, 0x9E, 0x88, 0x69, 0x97, 0x11, 0x72, + 0x49, 0x00, 0x78, 0xE1, 0x1E, 0x5D, 0x24, 0x88, 0x94, 0xA5, 0x29, 0x34, + 0x99, 0x99, 0x96, 0x8D, 0x6B, 0x5A, 0xA8, 0xA9, 0x25, 0x40, 0x99, 0x71, + 0x16, 0xF1, 0x24, 0x8F, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xF9, 0x99, + 0x99, 0x99, 0x99, 0xF0, 0xF9, 0x99, 0x99, 0x99, 0x99, 0xF0, 0xF9, 0x99, + 0x99, 0x99, 0x99, 0xF0 }; + +const GFXglyph mdlightlarger8pt7bGlyphs[] PROGMEM = { + { 0, 0, 0, 5, 0, 1 }, // 0x20 ' ' + { 0, 1, 7, 3, 0, -6 }, // 0x21 '!' + { 1, 4, 11, 6, 1, -10 }, // 0x22 '"' + { 7, 4, 11, 6, 1, -10 }, // 0x23 '#' + { 13, 4, 11, 6, 1, -10 }, // 0x24 '$' + { 19, 4, 11, 6, 1, -10 }, // 0x25 '%' + { 25, 6, 7, 8, 0, -6 }, // 0x26 '&' + { 31, 2, 2, 4, 0, -5 }, // 0x27 ''' + { 32, 2, 7, 4, 0, -6 }, // 0x28 '(' + { 34, 2, 7, 4, 0, -6 }, // 0x29 ')' + { 36, 4, 11, 6, 1, -10 }, // 0x2A '*' + { 42, 5, 5, 7, 0, -4 }, // 0x2B '+' + { 46, 4, 11, 6, 1, -10 }, // 0x2C ',' + { 52, 5, 1, 7, 0, -2 }, // 0x2D '-' + { 53, 2, 2, 4, 0, -1 }, // 0x2E '.' + { 54, 6, 6, 8, 0, -5 }, // 0x2F '/' + { 59, 5, 7, 7, 0, -6 }, // 0x30 '0' + { 64, 5, 7, 7, 0, -6 }, // 0x31 '1' + { 69, 5, 7, 7, 0, -6 }, // 0x32 '2' + { 74, 5, 7, 7, 0, -6 }, // 0x33 '3' + { 79, 5, 7, 7, 0, -6 }, // 0x34 '4' + { 84, 5, 7, 7, 0, -6 }, // 0x35 '5' + { 89, 5, 7, 7, 0, -6 }, // 0x36 '6' + { 94, 5, 7, 7, 0, -6 }, // 0x37 '7' + { 99, 5, 7, 7, 0, -6 }, // 0x38 '8' + { 104, 5, 7, 7, 0, -6 }, // 0x39 '9' + { 109, 4, 11, 6, 1, -10 }, // 0x3A ':' + { 115, 4, 11, 6, 1, -10 }, // 0x3B ';' + { 121, 4, 11, 6, 1, -10 }, // 0x3C '<' + { 127, 5, 3, 7, 0, -3 }, // 0x3D '=' + { 129, 4, 11, 6, 1, -10 }, // 0x3E '>' + { 135, 5, 7, 7, 0, -6 }, // 0x3F '?' + { 140, 4, 11, 6, 1, -10 }, // 0x40 '@' + { 146, 5, 7, 7, 0, -6 }, // 0x41 'A' + { 151, 5, 7, 7, 0, -6 }, // 0x42 'B' + { 156, 5, 7, 7, 0, -6 }, // 0x43 'C' + { 161, 5, 7, 7, 0, -6 }, // 0x44 'D' + { 166, 5, 7, 7, 0, -6 }, // 0x45 'E' + { 171, 5, 7, 7, 0, -6 }, // 0x46 'F' + { 176, 5, 7, 7, 0, -6 }, // 0x47 'G' + { 181, 5, 7, 7, 0, -6 }, // 0x48 'H' + { 186, 1, 7, 3, 0, -6 }, // 0x49 'I' + { 187, 4, 7, 6, 0, -6 }, // 0x4A 'J' + { 191, 5, 7, 7, 0, -6 }, // 0x4B 'K' + { 196, 5, 7, 7, 0, -6 }, // 0x4C 'L' + { 201, 5, 7, 7, 0, -6 }, // 0x4D 'M' + { 206, 5, 7, 7, 0, -6 }, // 0x4E 'N' + { 211, 5, 7, 7, 0, -6 }, // 0x4F 'O' + { 216, 5, 7, 7, 0, -6 }, // 0x50 'P' + { 221, 5, 7, 7, 0, -6 }, // 0x51 'Q' + { 226, 5, 7, 7, 0, -6 }, // 0x52 'R' + { 231, 5, 7, 7, 0, -6 }, // 0x53 'S' + { 236, 5, 7, 7, 0, -6 }, // 0x54 'T' + { 241, 5, 7, 7, 0, -6 }, // 0x55 'U' + { 246, 5, 7, 7, 0, -6 }, // 0x56 'V' + { 251, 5, 7, 7, 0, -6 }, // 0x57 'W' + { 256, 5, 7, 7, 0, -6 }, // 0x58 'X' + { 261, 5, 7, 7, 0, -6 }, // 0x59 'Y' + { 266, 5, 7, 7, 0, -6 }, // 0x5A 'Z' + { 271, 4, 11, 6, 1, -10 }, // 0x5B '[' + { 277, 4, 11, 6, 1, -10 }, // 0x5C '\' + { 283, 4, 11, 6, 1, -10 }, // 0x5D ']' + { 289, 4, 11, 6, 1, -10 }, // 0x5E '^' + { 295, 6, 1, 8, 0, 0 }, // 0x5F '_' + { 296, 4, 11, 6, 1, -10 }, // 0x60 '`' + { 302, 5, 6, 7, 0, -5 }, // 0x61 'a' + { 306, 4, 7, 6, 0, -6 }, // 0x62 'b' + { 310, 4, 6, 6, 0, -5 }, // 0x63 'c' + { 313, 5, 7, 7, 0, -6 }, // 0x64 'd' + { 318, 4, 6, 6, 0, -5 }, // 0x65 'e' + { 321, 3, 7, 5, 0, -6 }, // 0x66 'f' + { 324, 4, 6, 6, 0, -5 }, // 0x67 'g' + { 327, 4, 7, 6, 0, -6 }, // 0x68 'h' + { 331, 3, 7, 5, 0, -6 }, // 0x69 'i' + { 334, 2, 7, 4, 0, -6 }, // 0x6A 'j' + { 336, 4, 7, 6, 0, -6 }, // 0x6B 'k' + { 340, 3, 6, 5, 0, -5 }, // 0x6C 'l' + { 343, 5, 6, 7, 0, -5 }, // 0x6D 'm' + { 347, 4, 6, 6, 0, -5 }, // 0x6E 'n' + { 350, 4, 6, 6, 0, -5 }, // 0x6F 'o' + { 353, 4, 6, 6, 0, -5 }, // 0x70 'p' + { 356, 4, 6, 6, 0, -5 }, // 0x71 'q' + { 359, 3, 6, 5, 0, -5 }, // 0x72 'r' + { 362, 4, 6, 6, 0, -5 }, // 0x73 's' + { 365, 3, 7, 5, 0, -6 }, // 0x74 't' + { 368, 5, 6, 7, 0, -5 }, // 0x75 'u' + { 372, 4, 6, 6, 0, -5 }, // 0x76 'v' + { 375, 5, 6, 7, 0, -5 }, // 0x77 'w' + { 379, 3, 6, 5, 0, -5 }, // 0x78 'x' + { 382, 4, 6, 6, 0, -5 }, // 0x79 'y' + { 385, 4, 6, 6, 0, -5 }, // 0x7A 'z' + { 388, 4, 11, 6, 1, -10 }, // 0x7B '{' + { 394, 4, 11, 6, 1, -10 }, // 0x7C '|' + { 400, 4, 11, 6, 1, -10 }, // 0x7D '}' + { 406, 4, 11, 6, 1, -10 } }; // 0x7E '~' + +const GFXfont mdlightlarger8pt7b PROGMEM = { + (uint8_t *)mdlightlarger8pt7bBitmaps, + (GFXglyph *)mdlightlarger8pt7bGlyphs, + 0x20, 0x7E, 12 }; + +// Approx. 1084 bytes +// From d5e550f5b24c16d93a13dfd61060355c7cc1f63d Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 14:23:23 +0800 Subject: [PATCH 074/469] disable hard-coded DEBUG_MODE --- .gitignore | 1 + avr/cores/megacommand/WProgram.h | 2 +- build.ps1 | 31 +++++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 97cee5c93..ec136793e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vscode bin +obj .ionide .ccls-cache sketch/*.elf diff --git a/avr/cores/megacommand/WProgram.h b/avr/cores/megacommand/WProgram.h index 885c19a11..fe5a2e29a 100644 --- a/avr/cores/megacommand/WProgram.h +++ b/avr/cores/megacommand/WProgram.h @@ -11,7 +11,7 @@ #include "wiring_private.h" -#define DEBUGMODE +//#define DEBUGMODE #ifdef MEGACOMMAND #define SD_CS 53 //PB0 diff --git a/build.ps1 b/build.ps1 index 9411a9ee4..b91c0500d 100644 --- a/build.ps1 +++ b/build.ps1 @@ -4,15 +4,42 @@ param( [Switch] $Quiet, [Switch] - $Upload + $Upload, + [Switch] + $Clean, + [Switch] + $Debug ) $pattern = "#pragma message:" Write-Host "============> Build started." +$DEBUG_FLAG = "" +$DEBUG_OPT = "" + +if ($Debug) { + $DEBUG_FLAG = "--build-properties" + $DEBUG_OPT = "compiler.cpp.extra_flags=-DDEBUGMODE" +} + +if ($Clean) { + Remove-Item -ErrorAction SilentlyContinue -Recurse -Force bin + Remove-Item -ErrorAction SilentlyContinue -Recurse -Force obj +} + +$BIN_PATH = [System.IO.Path]::GetFullPath("$PSScriptRoot\bin") +$OBJ_PATH = [System.IO.Path]::GetFullPath("$PSScriptRoot\obj") +$SKETCH_PATH = [System.IO.Path]::GetFullPath("$PSScriptRoot\sketch") + $buildOutput = & { - arduino compile --warnings default -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ + arduino compile ` + --warnings default ` + --build-path $BIN_PATH ` + --build-cache-path $OBJ_PATH ` + -b MIDICtrl20_MegaCommand:avr:mega ` + $DEBUG_FLAG $DEBUG_OPT ` + $SKETCH_PATH $Script:compileStatus = $LASTEXITCODE } 2>&1 | ForEach-Object { $content = $_.ToString() From e3abd0c59504ac8c1b65b1dedd5aa146f46ec513 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 14:31:20 +0800 Subject: [PATCH 075/469] revert changes to Pages.hh --- avr/cores/megacommand/GUI/Pages.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/GUI/Pages.hh b/avr/cores/megacommand/GUI/Pages.hh index 536c52b38..df03afb0e 100644 --- a/avr/cores/megacommand/GUI/Pages.hh +++ b/avr/cores/megacommand/GUI/Pages.hh @@ -184,11 +184,11 @@ public: virtual void clear(); /** Display the page using the display routine as specfied by function * pointer*/ - virtual void display() {} + virtual void display(); /** Executes the encoder actions by calling checkHandle() on each encoder. **/ virtual void finalize(); /** Set the display routine to supplied fuction pointer **/ - virtual void setup() {} + virtual void setup(); /** Call this to lock all encoders in the page. **/ void lockEncoders(); /** Call this to unlock all encoders in the page. If their value From daaf4d045e4ed371c19cee296287793a756fa85c Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 14:36:23 +0800 Subject: [PATCH 076/469] revert struct definitions in Menu.h --- avr/cores/megacommand/MCL/Menu.h | 14 +++++++------- avr/cores/megacommand/MCL/SDDrivePage.cpp | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index ad725b76f..f81118721 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -4,14 +4,14 @@ #define MAX_MENU_ITEMS 16 #define MAX_MENU_OPTIONS 16 -typedef void (*FP)(); +typedef void(*FP)(); -struct menu_option_t { +typedef struct menu_option_s { uint8_t pos; char name[17]; -}; +} menu_option_t; -struct menu_item_t { +typedef struct menu_item_s { char name[17]; uint8_t min; uint8_t range; @@ -20,15 +20,15 @@ struct menu_item_t { Page *page_callback; void (*row_function)(); menu_option_t options[MAX_MENU_OPTIONS]; -}; +} menu_item_t; -struct menu_t { +typedef struct menu_s { char name[11]; uint8_t number_of_items; menu_item_t items[MAX_MENU_ITEMS]; void (*exit_function)(); Page *exit_page_callback; -}; +} menu_t; class Menu { diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 4f6325662..d55e083ff 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "SDDrivePage.h" +#include "MCL.h" const char *c_snapshot_suffix = ".snp"; const char *c_snapshot_root = "/SDDrive/MD"; @@ -148,7 +148,7 @@ void SDDrivePage::on_new() { init(); } -void SDDrivePage::on_delete(const char* file) { +void SDDrivePage::on_delete(const char *file) { gfx.alert("SDDrivePage::on_delete", file); } From deb30a9ac9f81bc37598cd67535017663de12b0f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 14:50:44 +0800 Subject: [PATCH 077/469] remove undefine methods in LightPage --- avr/cores/megacommand/GUI/Pages.hh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/avr/cores/megacommand/GUI/Pages.hh b/avr/cores/megacommand/GUI/Pages.hh index df03afb0e..a0e3a55b5 100644 --- a/avr/cores/megacommand/GUI/Pages.hh +++ b/avr/cores/megacommand/GUI/Pages.hh @@ -182,13 +182,8 @@ public: virtual void update(); /** This will clear the encoder movements. **/ virtual void clear(); - /** Display the page using the display routine as specfied by function - * pointer*/ - virtual void display(); /** Executes the encoder actions by calling checkHandle() on each encoder. **/ virtual void finalize(); - /** Set the display routine to supplied fuction pointer **/ - virtual void setup(); /** Call this to lock all encoders in the page. **/ void lockEncoders(); /** Call this to unlock all encoders in the page. If their value From b9fc37c61c14c5634eff0f0cc2c70e850762c6fb Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 15:19:30 +0800 Subject: [PATCH 078/469] fix undefined base methods --- avr/cores/megacommand/GUI/Pages.hh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/GUI/Pages.hh b/avr/cores/megacommand/GUI/Pages.hh index a0e3a55b5..377b59f7a 100644 --- a/avr/cores/megacommand/GUI/Pages.hh +++ b/avr/cores/megacommand/GUI/Pages.hh @@ -92,7 +92,7 @@ public: * } * \endcode **/ - virtual void update(); + virtual void update() {} /** * The loop() method is basically the same as the update() method. **/ @@ -185,12 +185,11 @@ public: /** Executes the encoder actions by calling checkHandle() on each encoder. **/ virtual void finalize(); /** Call this to lock all encoders in the page. **/ - void lockEncoders(); + void lockEncoders() {} // TODO /** Call this to unlock all encoders in the page. If their value changed while locked, they will send out their new value. **/ - virtual void config() {} - void unlockEncoders(); + void unlockEncoders() {} // TODO }; class Page : public PageParent { From 4d1185d12293c0026b673add954a507149770c72 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 16:15:13 +0800 Subject: [PATCH 079/469] debugging --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 51 +++++-------------- avr/cores/megacommand/MCL/FileBrowserPage.h | 16 +++--- avr/cores/megacommand/MCL/MCLMenus.cpp | 4 ++ avr/cores/megacommand/MCL/MCLMenus.h | 16 ++++++ avr/cores/megacommand/MCL/Menu.h | 6 ++- 5 files changed, 45 insertions(+), 48 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 355601f69..81a00e2d2 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -1,24 +1,6 @@ #include "FileBrowserPage.h" #include "MCL.h" - -const menu_t file_menu PROGMEM = { - "File", - 5, - { - {"NEW FOLDER", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"DELETE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"RENAME", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"OVERWRITE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"CANCEL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - }, - NULL, - (Page *)NULL, -}; - -MCLEncoder file_menu_encoder(0, 1, ENCODER_RES_PAT); -MCLEncoder file_menu_encoder2(0, 4, ENCODER_RES_PAT); -MenuPage file_menu_page((menu_t *)&file_menu, &file_menu_encoder, - &file_menu_encoder2); +#include "MCLMenus.h" void FileBrowserPage::setup() { #ifdef OLED_DISPLAY @@ -43,13 +25,13 @@ void FileBrowserPage::init() { char temp_entry[16]; // config menu - file_menu_page.menu.enable_entry(0, show_new_folder); - file_menu_page.menu.enable_entry(1, true); // delete - file_menu_page.menu.enable_entry(2, true); // rename - file_menu_page.menu.enable_entry(3, show_overwrite); - file_menu_page.menu.enable_entry(4, true); // cancel - file_menu_encoder2.cur = file_menu_encoder2.old = 0; - file_menu_encoder2.max = file_menu_page.menu.get_number_of_items() - 1; + //file_menu_page.menu.enable_entry(0, show_new_folder); + //file_menu_page.menu.enable_entry(1, true); // delete + //file_menu_page.menu.enable_entry(2, true); // rename + //file_menu_page.menu.enable_entry(3, show_overwrite); + //file_menu_page.menu.enable_entry(4, true); // cancel + //file_menu_encoder.cur = file_menu_encoder.old = 0; + //file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; filemenu_active = false; int index = 0; @@ -282,7 +264,7 @@ void FileBrowserPage::_handle_filemenu() { char buf2[32] = {'\0'}; - switch (file_menu_page.menu.get_item_index(file_menu_encoder2.cur)) { + switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { case 0: // new folder create_folder(); break; @@ -322,16 +304,16 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON3)) { filemenu_active = true; - encoders[0] = &file_menu_encoder; - encoders[1] = &file_menu_encoder2; + encoders[0] = &config_param1; + encoders[1] = &file_menu_encoder; file_menu_page.init(); return false; } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { filemenu_active = false; - encoders[0] = param1; - encoders[1] = param2; + //encoders[0] = param1; + //encoders[1] = param2; _handle_filemenu(); return false; } @@ -374,13 +356,6 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { return true; } - if (EVENT_RELEASED(event, Buttons.BUTTON2)) { - // TODO shift menu - // TODO delete - // TODO rename - // TODO copy/paste - } - // cancel if (EVENT_PRESSED(event, Buttons.BUTTON1) || EVENT_RELEASED(event, Buttons.BUTTON3) || diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 68a73b3b5..641517dec 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -7,6 +7,8 @@ #include "MCLEncoder.h" #include "SdFat.h" #include "SeqPage.h" +#include "Menu.h" +#include "MenuPage.h" #define MAX_ENTRIES 1024 @@ -30,25 +32,23 @@ class FileBrowserPage : public LightPage { uint8_t cur_col = 0; uint8_t cur_row = 0; uint8_t cur_file = 0; - char title[12]; - File file; // configuration, should be set before calling base init() bool show_dirs = false; bool show_save = true; bool show_parent = true; - bool show_filemenu; bool show_new_folder = true; - bool show_overwrite; + bool show_filemenu = true; + bool show_overwrite = false; - bool filemenu_active; + bool filemenu_active = false; - Encoder* param1; - Encoder* param2; + char title[12]; + File file; FileBrowserPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4), param1(e1), param2(e2) {} + : LightPage(e1, e2, e3, e4) {} virtual bool handleEvent(gui_event_t *event); virtual void display(); void add_entry(char *entry); diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index dddb03da6..c90a46c0d 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -28,3 +28,7 @@ MCLEncoder input_encoder1(0, 127, ENCODER_RES_SYS); MCLEncoder input_encoder2(0, 127, ENCODER_RES_SYS); TextInputPage text_input_page(&input_encoder1, &input_encoder2); + +MCLEncoder file_menu_encoder(0, 4, ENCODER_RES_PAT); +MenuPage file_menu_page(&file_menu_layout, &config_param1, &file_menu_encoder); + diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 2cca12ce4..be441bc6e 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -38,6 +38,9 @@ extern MCLEncoder input_encoder2; extern TextInputPage text_input_page; +extern MCLEncoder file_menu_encoder; +extern MenuPage file_menu_page; + const menu_t system_menu_layout PROGMEM = { "GLOBAL", 7, @@ -126,6 +129,19 @@ const menu_t mclconfig_menu_layout PROGMEM = { (void*)(&mclsys_apply_config), }; +const menu_t file_menu_layout PROGMEM = { + "FILE", + 5, + { + {"NEW FOLDER", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, + {"DELETE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, + {"RENAME", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, + {"OVERWRITE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, + {"CANCEL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, + }, + NULL, + (Page *)NULL, +}; #endif /* MCLMENUS_H__ */ diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index f81118721..b6f09e3ed 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -35,9 +35,11 @@ class Menu { public: menu_t *layout; uint8_t values[MAX_MENU_ITEMS]; - uint8_t entry_mask[(MAX_MENU_ITEMS + 7) / 8]; + uint8_t entry_mask[2]; - Menu(){ memset(entry_mask, 0xFF, sizeof(entry_mask)); } + Menu(){ + entry_mask[0] = entry_mask[1] = 0xFF; + } void set_layout(menu_t *menu_layout); void enable_entry(uint8_t entry_index, bool en); From 85773c439d79d0c2983060f7c545bc6273df3451 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 16:48:13 +0800 Subject: [PATCH 080/469] debugging --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 18 +++++++++--------- avr/cores/megacommand/MCL/FileBrowserPage.h | 7 ++++++- avr/cores/megacommand/MCL/Menu.cpp | 11 +++++------ avr/cores/megacommand/MCL/Menu.h | 1 - 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 81a00e2d2..5deba4b9e 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -25,13 +25,13 @@ void FileBrowserPage::init() { char temp_entry[16]; // config menu - //file_menu_page.menu.enable_entry(0, show_new_folder); - //file_menu_page.menu.enable_entry(1, true); // delete - //file_menu_page.menu.enable_entry(2, true); // rename - //file_menu_page.menu.enable_entry(3, show_overwrite); - //file_menu_page.menu.enable_entry(4, true); // cancel - //file_menu_encoder.cur = file_menu_encoder.old = 0; - //file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; + file_menu_page.menu.enable_entry(0, show_new_folder); + file_menu_page.menu.enable_entry(1, true); // delete + file_menu_page.menu.enable_entry(2, true); // rename + file_menu_page.menu.enable_entry(3, show_overwrite); + file_menu_page.menu.enable_entry(4, true); // cancel + file_menu_encoder.cur = file_menu_encoder.old = 0; + file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; filemenu_active = false; int index = 0; @@ -312,8 +312,8 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { if (EVENT_RELEASED(event, Buttons.BUTTON3)) { filemenu_active = false; - //encoders[0] = param1; - //encoders[1] = param2; + encoders[0] = param1; + encoders[1] = param2; _handle_filemenu(); return false; } diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 641517dec..489a69c0a 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -44,11 +44,16 @@ class FileBrowserPage : public LightPage { bool filemenu_active = false; char title[12]; + Encoder* param1; + Encoder* param2; File file; FileBrowserPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4) {} + : LightPage(e1, e2, e3, e4) { + param1 = e1; + param2 = e2; + } virtual bool handleEvent(gui_event_t *event); virtual void display(); void add_entry(char *entry); diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index 076fa7894..da7957a8d 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -1,9 +1,9 @@ #include "MCL.h" -#include "Menu.h" void Menu::set_layout(menu_t *menu_layout) { layout = menu_layout; } void Menu::enable_entry(uint8_t entry_index, bool en) { + auto midx = entry_index / 8; auto bit = entry_index % 8; @@ -46,11 +46,11 @@ uint8_t Menu::get_number_of_items() { menu_item_t *Menu::get_item(uint8_t item_n) { uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); - for(uint8_t idx = 0; idx < entry_cnt; ++idx) { - if(is_entry_enable(idx)) { + for (uint8_t idx = 0; idx < entry_cnt; ++idx) { + if (is_entry_enable(idx)) { if (item_n == 0) { return &layout->items[idx]; - }else { + } else { --item_n; } } @@ -58,8 +58,7 @@ menu_item_t *Menu::get_item(uint8_t item_n) { return nullptr; } -uint8_t Menu::get_item_index(uint8_t item_n) -{ +uint8_t Menu::get_item_index(uint8_t item_n) { auto pentry = get_item(item_n); return pentry - &layout->items[0]; } diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index b6f09e3ed..89354bc77 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -34,7 +34,6 @@ class Menu { public: menu_t *layout; - uint8_t values[MAX_MENU_ITEMS]; uint8_t entry_mask[2]; Menu(){ From 3365a25be545336923b8dfbe9381f31c9f31ffcf Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 19:46:59 +0800 Subject: [PATCH 081/469] maximally bootable firmware... --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 21 ++++---- avr/cores/megacommand/MCL/FileBrowserPage.h | 4 +- avr/cores/megacommand/MCL/MCLMenus.cpp | 2 +- avr/cores/megacommand/MCL/MCLMenus.h | 1 - avr/cores/megacommand/MCL/Menu.cpp | 52 ++++--------------- avr/cores/megacommand/MCL/Menu.h | 11 ++-- 6 files changed, 28 insertions(+), 63 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 5deba4b9e..375ca49ed 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -25,13 +25,13 @@ void FileBrowserPage::init() { char temp_entry[16]; // config menu - file_menu_page.menu.enable_entry(0, show_new_folder); - file_menu_page.menu.enable_entry(1, true); // delete - file_menu_page.menu.enable_entry(2, true); // rename - file_menu_page.menu.enable_entry(3, show_overwrite); - file_menu_page.menu.enable_entry(4, true); // cancel + //file_menu_page.menu.enable_entry(0, show_new_folder); + //file_menu_page.menu.enable_entry(1, true); // delete + //file_menu_page.menu.enable_entry(2, true); // rename + //file_menu_page.menu.enable_entry(3, show_overwrite); + //file_menu_page.menu.enable_entry(4, true); // cancel file_menu_encoder.cur = file_menu_encoder.old = 0; - file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; + //file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; filemenu_active = false; int index = 0; @@ -102,7 +102,7 @@ void FileBrowserPage::display() { #ifdef OLED_DISPLAY if (filemenu_active) { oled_display.fillRect(0, 8, 38, 24, BLACK); - file_menu_page.draw_menu(0, 14, 38); + //file_menu_page.draw_menu(0, 14, 38); oled_display.display(); return; } @@ -179,7 +179,7 @@ void FileBrowserPage::draw_scrollbar(uint8_t x_offset) { void FileBrowserPage::loop() { if (filemenu_active) { - file_menu_page.loop(); + //file_menu_page.loop(); return; } @@ -264,7 +264,8 @@ void FileBrowserPage::_handle_filemenu() { char buf2[32] = {'\0'}; - switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { + //switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { + switch (file_menu_encoder.cur) { case 0: // new folder create_folder(); break; @@ -306,7 +307,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { filemenu_active = true; encoders[0] = &config_param1; encoders[1] = &file_menu_encoder; - file_menu_page.init(); + //file_menu_page.init(); return false; } diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 489a69c0a..7b14e0124 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -24,11 +24,13 @@ class FileBrowserPage : public LightPage { public: + File file; // char file_entries[NUM_FILE_ENTRIES][16]; int numEntries; char match[5]; char lwd[128]; + char title[12]; uint8_t cur_col = 0; uint8_t cur_row = 0; uint8_t cur_file = 0; @@ -43,10 +45,8 @@ class FileBrowserPage : public LightPage { bool filemenu_active = false; - char title[12]; Encoder* param1; Encoder* param2; - File file; FileBrowserPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index c90a46c0d..5dff15a36 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -30,5 +30,5 @@ MCLEncoder input_encoder2(0, 127, ENCODER_RES_SYS); TextInputPage text_input_page(&input_encoder1, &input_encoder2); MCLEncoder file_menu_encoder(0, 4, ENCODER_RES_PAT); -MenuPage file_menu_page(&file_menu_layout, &config_param1, &file_menu_encoder); +//MenuPage file_menu_page(&file_menu_layout, &config_param1, &file_menu_encoder); diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index be441bc6e..26d3575ab 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -140,7 +140,6 @@ const menu_t file_menu_layout PROGMEM = { {"CANCEL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, }, NULL, - (Page *)NULL, }; diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index da7957a8d..2c8ea5bbd 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -1,25 +1,8 @@ #include "MCL.h" +#include "Menu.h" void Menu::set_layout(menu_t *menu_layout) { layout = menu_layout; } -void Menu::enable_entry(uint8_t entry_index, bool en) { - - auto midx = entry_index / 8; - auto bit = entry_index % 8; - - if (en) { - entry_mask[midx] |= _BV(bit); - } else { - entry_mask[midx] &= ~_BV(bit); - } -} - -bool Menu::is_entry_enable(uint8_t entry_index) { - auto midx = entry_index / 8; - auto bit = entry_index % 8; - return bit_is_set(entry_mask[midx], bit); -} - PGM_P Menu::get_name() { return layout->name; } /* Page *Menu::get_exit_page_callback() { @@ -32,35 +15,21 @@ FP Menu::get_row_function(uint8_t item_n) { return pgm_read_word(&(item->row_function)); } -FP Menu::get_exit_function() { return pgm_read_word(&(layout->exit_function)); } + +FP Menu::get_exit_function() { + return pgm_read_word(&(layout->exit_function)); +} + uint8_t Menu::get_number_of_items() { - uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); - uint8_t item_cnt = 0; - for (auto i = 0; i < entry_cnt; ++i) { - if (is_entry_enable(i)) - ++item_cnt; - } - return item_cnt; + return pgm_read_byte(&(layout->number_of_items)); } menu_item_t *Menu::get_item(uint8_t item_n) { - uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); - for (uint8_t idx = 0; idx < entry_cnt; ++idx) { - if (is_entry_enable(idx)) { - if (item_n == 0) { - return &layout->items[idx]; - } else { - --item_n; - } - } + if (item_n > get_number_of_items()) { + return &(layout->items[get_number_of_items() - 1]); } - return nullptr; -} - -uint8_t Menu::get_item_index(uint8_t item_n) { - auto pentry = get_item(item_n); - return pentry - &layout->items[0]; + return &(layout->items[item_n]); } PGM_P Menu::get_item_name(uint8_t item_n) { @@ -85,6 +54,7 @@ uint8_t Menu::get_option_min(uint8_t item_n) { return pgm_read_byte(&(item->min)); } + uint8_t Menu::get_number_of_options(uint8_t item_n) { menu_item_t *item = get_item(item_n); return pgm_read_byte(&(item->number_of_options)); diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 89354bc77..4ffa9ca9d 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -34,24 +34,19 @@ class Menu { public: menu_t *layout; - uint8_t entry_mask[2]; + uint8_t values[MAX_MENU_ITEMS]; - Menu(){ - entry_mask[0] = entry_mask[1] = 0xFF; - } + Menu() {} void set_layout(menu_t *menu_layout); - void enable_entry(uint8_t entry_index, bool en); - bool is_entry_enable(uint8_t entry_index); PGM_P get_name(); uint8_t get_number_of_items(); menu_item_t *get_item(uint8_t item_n); - uint8_t get_item_index(uint8_t item_n); PGM_P get_item_name(uint8_t item_n); Page *get_page_callback(uint8_t item_n); - // Page *get_exit_page_callback(); + //Page *get_exit_page_callback(); FP get_exit_function(); FP get_row_function(uint8_t item_n); uint8_t *get_dest_variable(uint8_t item_n); From 6589ed3f4a9d0651967e63ca871ad690c1ec70f1 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 19:50:17 +0800 Subject: [PATCH 082/469] menu mask implementation ok... --- avr/cores/megacommand/MCL/Menu.cpp | 51 ++++++++++++++++++++++++------ avr/cores/megacommand/MCL/Menu.h | 10 ++++-- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index 2c8ea5bbd..076fa7894 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -3,6 +3,23 @@ void Menu::set_layout(menu_t *menu_layout) { layout = menu_layout; } +void Menu::enable_entry(uint8_t entry_index, bool en) { + auto midx = entry_index / 8; + auto bit = entry_index % 8; + + if (en) { + entry_mask[midx] |= _BV(bit); + } else { + entry_mask[midx] &= ~_BV(bit); + } +} + +bool Menu::is_entry_enable(uint8_t entry_index) { + auto midx = entry_index / 8; + auto bit = entry_index % 8; + return bit_is_set(entry_mask[midx], bit); +} + PGM_P Menu::get_name() { return layout->name; } /* Page *Menu::get_exit_page_callback() { @@ -15,21 +32,36 @@ FP Menu::get_row_function(uint8_t item_n) { return pgm_read_word(&(item->row_function)); } - -FP Menu::get_exit_function() { - return pgm_read_word(&(layout->exit_function)); -} - +FP Menu::get_exit_function() { return pgm_read_word(&(layout->exit_function)); } uint8_t Menu::get_number_of_items() { - return pgm_read_byte(&(layout->number_of_items)); + uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); + uint8_t item_cnt = 0; + for (auto i = 0; i < entry_cnt; ++i) { + if (is_entry_enable(i)) + ++item_cnt; + } + return item_cnt; } menu_item_t *Menu::get_item(uint8_t item_n) { - if (item_n > get_number_of_items()) { - return &(layout->items[get_number_of_items() - 1]); + uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); + for(uint8_t idx = 0; idx < entry_cnt; ++idx) { + if(is_entry_enable(idx)) { + if (item_n == 0) { + return &layout->items[idx]; + }else { + --item_n; + } + } } - return &(layout->items[item_n]); + return nullptr; +} + +uint8_t Menu::get_item_index(uint8_t item_n) +{ + auto pentry = get_item(item_n); + return pentry - &layout->items[0]; } PGM_P Menu::get_item_name(uint8_t item_n) { @@ -54,7 +86,6 @@ uint8_t Menu::get_option_min(uint8_t item_n) { return pgm_read_byte(&(item->min)); } - uint8_t Menu::get_number_of_options(uint8_t item_n) { menu_item_t *item = get_item(item_n); return pgm_read_byte(&(item->number_of_options)); diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 4ffa9ca9d..b6f09e3ed 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -35,18 +35,24 @@ class Menu { public: menu_t *layout; uint8_t values[MAX_MENU_ITEMS]; + uint8_t entry_mask[2]; - Menu() {} + Menu(){ + entry_mask[0] = entry_mask[1] = 0xFF; + } void set_layout(menu_t *menu_layout); + void enable_entry(uint8_t entry_index, bool en); + bool is_entry_enable(uint8_t entry_index); PGM_P get_name(); uint8_t get_number_of_items(); menu_item_t *get_item(uint8_t item_n); + uint8_t get_item_index(uint8_t item_n); PGM_P get_item_name(uint8_t item_n); Page *get_page_callback(uint8_t item_n); - //Page *get_exit_page_callback(); + // Page *get_exit_page_callback(); FP get_exit_function(); FP get_row_function(uint8_t item_n); uint8_t *get_dest_variable(uint8_t item_n); From 7021e6f26b7eb4e7ec157452b0000b00da5cd45a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 19:58:05 +0800 Subject: [PATCH 083/469] move menu layout definitions to cpp --- avr/cores/megacommand/MCL/MCLMenus.cpp | 103 ++++++++++++++++++++++++- avr/cores/megacommand/MCL/MCLMenus.h | 101 ------------------------ avr/cores/megacommand/MCL/Menu.cpp | 2 +- avr/cores/megacommand/MCL/Menu.h | 4 +- avr/cores/megacommand/MCL/MenuPage.h | 2 +- 5 files changed, 106 insertions(+), 106 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index 5dff15a36..8c7767f27 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -12,6 +12,107 @@ MCLEncoder config_param5(0, 17, ENCODER_RES_SYS); MCLEncoder config_param6(0, 17, ENCODER_RES_SYS); MCLEncoder config_param7(0, 17, ENCODER_RES_SYS); +const menu_t system_menu_layout PROGMEM = { + "GLOBAL", + 7, + { + {"LOAD PROJECT" ,0, 0, 0, (uint8_t *) NULL, (Page*) &load_proj_page, NULL, {}}, + {"NEW PROJECT",0, 0, 0, (uint8_t *) NULL, (Page*) &new_proj_page, NULL, {}}, + {"MIDI",0, 0, 0, (uint8_t *) NULL, (Page*) &midi_config_page, NULL, {}}, + {"MACHINEDRUM", 0, 0, 0, (uint8_t *) NULL, (Page*) &md_config_page, NULL, {}}, + {"CHAIN MODE", 0, 0, 0, (uint8_t *) NULL, (Page*) &chain_config_page, NULL, {}}, + {"AUX PAGES", 0, 0, 0, (uint8_t *) NULL, (Page*) &aux_config_page, NULL, {}}, + {"SYSTEM", 0, 0, 0, (uint8_t *) NULL, (Page*) &mcl_config_page, NULL, {}}, + }, + NULL, +}; + +const menu_t auxconfig_menu_layout PROGMEM = { + "AUX PAGES", + 1, + { + {"RAM Page" ,0, 0, 0, (uint8_t *) NULL, (Page*) &ram_config_page, NULL, {}}, + }, + NULL, +}; + +const menu_t rampage1_menu_layout PROGMEM = { + "RAM PAGE", + 1, + { + {"LINK:", 0, 2, 2, (uint8_t *) &mcl_cfg.ram_page_mode, (Page*) NULL, NULL, {{0, "MONO"},{1, "STEREO"}}}, + }, + + NULL, + +}; + + +const menu_t midiconfig_menu_layout PROGMEM = { + "MIDI", + 5, + { + {"TURBO 1:", 0, 4, 4, (uint8_t *) &mcl_cfg.uart1_turbo, (Page*) NULL, NULL, {{0, "1x"},{1, "2x"},{2,"4x"},{3,"8x"}}}, + {"TURBO 2:", 0, 4, 4, (uint8_t *) &mcl_cfg.uart2_turbo, (Page*) NULL, NULL, {{0, "1x"},{1, "2x"},{2,"4x"},{3,"8x"}}}, + + {"CLK REC:", 0, 2, 2, (uint8_t *) &mcl_cfg.clock_rec, (Page*) NULL, NULL, {{0, "MIDI 1"},{1, "MIDI 2"}}}, + {"CLK SEND:", 0, 2, 2, (uint8_t *) &mcl_cfg.clock_send, (Page*) NULL, NULL, {{0, "OFF"},{1, "MIDI 2"}}}, + + {"MIDI FWD:", 0, 3, 3, (uint8_t *) &mcl_cfg.midi_forward, (Page*) NULL, NULL, {{0, "OFF"}, {1, "1->2"},{2, "2->1"}}}, + }, + + (&mclsys_apply_config), + +}; + +const menu_t mdconfig_menu_layout PROGMEM = { + "MD", + 5, + { + {"KIT SAVE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_save, (Page*) NULL, NULL, {{0, "OFF"},{1, "AUTO"}}}, + {"NORMALIZE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_normalize, (Page*) NULL, NULL, {{0, "OFF"},{1, "AUTO"}}}, + {"CTRL CHAN:",0, 18, 2, (uint8_t *) &mcl_cfg.uart2_ctrl_mode, (Page*) NULL, NULL, {{0, "INT"},{17, "OMNI"}}}, + {"POLY CONFIG", 0, 0, 0, (uint8_t *) NULL, (Page*) &poly_page, NULL, {}}, + {"SD DRIVE", 0, 0, 0, (uint8_t *) NULL, (Page*) &sddrive_page, NULL, {}}, + }, + (&mclsys_apply_config), +}; + +const menu_t chain_menu_layout PROGMEM = { + "CHAIN", + 3, + { + {"CHAIN:", 1, 4, 3, (uint8_t *) &mcl_cfg.chain_mode, (Page*) NULL, NULL, {{1, "AUT"},{2,"MAN"},{3,"RND"}}}, + {"RAND MIN:", 0, 128, 0, (uint8_t *) &mcl_cfg.chain_rand_min, (Page*) NULL, NULL, {}}, + {"RAND MAX:", 0, 128, 0, (uint8_t *) &mcl_cfg.chain_rand_max, (Page*) NULL, NULL, {}}, + }, + (&mclsys_apply_config), +}; + + +const menu_t mclconfig_menu_layout PROGMEM = { + "SYSTEM", + 2, + { + {"DISPLAY:", 0, 2, 2, (uint8_t *) &mcl_cfg.display_mirror, (Page*) NULL, NULL, {{0, "INT"}, {1, "INT+EXT"}}}, + {"SCREENSAVER:", 0, 2, 2, (uint8_t *) &mcl_cfg.screen_saver, (Page*) NULL, NULL, {{0, "OFF"}, {1, "ON"}}}, + }, + (&mclsys_apply_config), +}; + +const menu_t file_menu_layout PROGMEM = { + "FILE", + 5, + { + {"NEW FOLDER", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"DELETE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"RENAME", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"OVERWRITE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"CANCEL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + }, + NULL, +}; + MenuPage aux_config_page(&auxconfig_menu_layout, &config_param1, &config_param6); MenuPage system_page(&system_menu_layout, &options_param1, &options_param2); MenuPage midi_config_page(&midiconfig_menu_layout, &config_param1, @@ -30,5 +131,5 @@ MCLEncoder input_encoder2(0, 127, ENCODER_RES_SYS); TextInputPage text_input_page(&input_encoder1, &input_encoder2); MCLEncoder file_menu_encoder(0, 4, ENCODER_RES_PAT); -//MenuPage file_menu_page(&file_menu_layout, &config_param1, &file_menu_encoder); +MenuPage file_menu_page(&file_menu_layout, &config_param1, &file_menu_encoder); diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 26d3575ab..90b057c8b 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -41,106 +41,5 @@ extern TextInputPage text_input_page; extern MCLEncoder file_menu_encoder; extern MenuPage file_menu_page; -const menu_t system_menu_layout PROGMEM = { - "GLOBAL", - 7, - { - {"LOAD PROJECT" ,0, 0, 0, (uint8_t *) NULL, (Page*) &load_proj_page, (void*)NULL, {}}, - {"NEW PROJECT",0, 0, 0, (uint8_t *) NULL, (Page*) &new_proj_page, (void*)NULL, {}}, - {"MIDI",0, 0, 0, (uint8_t *) NULL, (Page*) &midi_config_page, (void*)NULL, {}}, - {"MACHINEDRUM", 0, 0, 0, (uint8_t *) NULL, (Page*) &md_config_page, (void*)NULL, {}}, - {"CHAIN MODE", 0, 0, 0, (uint8_t *) NULL, (Page*) &chain_config_page, (void*)NULL, {}}, - {"AUX PAGES", 0, 0, 0, (uint8_t *) NULL, (Page*) &aux_config_page, (void*)NULL, {}}, - {"SYSTEM", 0, 0, 0, (uint8_t *) NULL, (Page*) &mcl_config_page, (void*)NULL, {}}, - }, - (void*) NULL, -}; - -const menu_t auxconfig_menu_layout PROGMEM = { - "AUX PAGES", - 1, - { - {"RAM Page" ,0, 0, 0, (uint8_t *) NULL, (Page*) &ram_config_page, (void*)NULL, {}}, - }, - (void*) NULL, -}; - -const menu_t rampage1_menu_layout PROGMEM = { - "RAM PAGE", - 1, - { - {"LINK:", 0, 2, 2, (uint8_t *) &mcl_cfg.ram_page_mode, (Page*) NULL, (void*)NULL, {{0, "MONO"},{1, "STEREO"}}}, - }, - - (void*) NULL, - -}; - - -const menu_t midiconfig_menu_layout PROGMEM = { - "MIDI", - 5, - { - {"TURBO 1:", 0, 4, 4, (uint8_t *) &mcl_cfg.uart1_turbo, (Page*) NULL, (void*)NULL, {{0, "1x"},{1, "2x"},{2,"4x"},{3,"8x"}}}, - {"TURBO 2:", 0, 4, 4, (uint8_t *) &mcl_cfg.uart2_turbo, (Page*) NULL, (void*)NULL, {{0, "1x"},{1, "2x"},{2,"4x"},{3,"8x"}}}, - - {"CLK REC:", 0, 2, 2, (uint8_t *) &mcl_cfg.clock_rec, (Page*) NULL, (void*)NULL, {{0, "MIDI 1"},{1, "MIDI 2"}}}, - {"CLK SEND:", 0, 2, 2, (uint8_t *) &mcl_cfg.clock_send, (Page*) NULL, (void*)NULL, {{0, "OFF"},{1, "MIDI 2"}}}, - - {"MIDI FWD:", 0, 3, 3, (uint8_t *) &mcl_cfg.midi_forward, (Page*) NULL, (void*)NULL, {{0, "OFF"}, {1, "1->2"},{2, "2->1"}}}, - }, - - (void*)(&mclsys_apply_config), - -}; - -const menu_t mdconfig_menu_layout PROGMEM = { - "MD", - 5, - { - {"KIT SAVE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_save, (Page*) NULL, (void*)NULL, {{0, "OFF"},{1, "AUTO"}}}, - {"NORMALIZE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_normalize, (Page*) NULL, (void*)NULL, {{0, "OFF"},{1, "AUTO"}}}, - {"CTRL CHAN:",0, 18, 2, (uint8_t *) &mcl_cfg.uart2_ctrl_mode, (Page*) NULL, (void*)NULL, {{0, "INT"},{17, "OMNI"}}}, - {"POLY CONFIG", 0, 0, 0, (uint8_t *) NULL, (Page*) &poly_page, (void*)NULL, {}}, - {"SD DRIVE", 0, 0, 0, (uint8_t *) NULL, (Page*) &sddrive_page, (void*)NULL, {}}, - }, - (void*)(&mclsys_apply_config), -}; - -const menu_t chain_menu_layout PROGMEM = { - "CHAIN", - 3, - { - {"CHAIN:", 1, 4, 3, (uint8_t *) &mcl_cfg.chain_mode, (Page*) NULL, (void*)NULL, {{1, "AUT"},{2,"MAN"},{3,"RND"}}}, - {"RAND MIN:", 0, 128, 0, (uint8_t *) &mcl_cfg.chain_rand_min, (Page*) NULL, (void*)NULL, {}}, - {"RAND MAX:", 0, 128, 0, (uint8_t *) &mcl_cfg.chain_rand_max, (Page*) NULL, (void*)NULL, {}}, - }, - (void*)(&mclsys_apply_config), -}; - - -const menu_t mclconfig_menu_layout PROGMEM = { - "SYSTEM", - 2, - { - {"DISPLAY:", 0, 2, 2, (uint8_t *) &mcl_cfg.display_mirror, (Page*) NULL, (void*)NULL, {{0, "INT"}, {1, "INT+EXT"}}}, - {"SCREENSAVER:", 0, 2, 2, (uint8_t *) &mcl_cfg.screen_saver, (Page*) NULL, (void*)NULL, {{0, "OFF"}, {1, "ON"}}}, - }, - (void*)(&mclsys_apply_config), -}; - -const menu_t file_menu_layout PROGMEM = { - "FILE", - 5, - { - {"NEW FOLDER", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, - {"DELETE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, - {"RENAME", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, - {"OVERWRITE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, - {"CANCEL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, (void*)NULL, {}}, - }, - NULL, -}; - #endif /* MCLMENUS_H__ */ diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index 076fa7894..8900675fd 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -1,7 +1,7 @@ #include "MCL.h" #include "Menu.h" -void Menu::set_layout(menu_t *menu_layout) { layout = menu_layout; } +void Menu::set_layout(const menu_t *menu_layout) { layout = menu_layout; } void Menu::enable_entry(uint8_t entry_index, bool en) { auto midx = entry_index / 8; diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index b6f09e3ed..51477930f 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -33,7 +33,7 @@ typedef struct menu_s { class Menu { public: - menu_t *layout; + const menu_t *layout; uint8_t values[MAX_MENU_ITEMS]; uint8_t entry_mask[2]; @@ -41,7 +41,7 @@ class Menu { entry_mask[0] = entry_mask[1] = 0xFF; } - void set_layout(menu_t *menu_layout); + void set_layout(const menu_t *menu_layout); void enable_entry(uint8_t entry_index, bool en); bool is_entry_enable(uint8_t entry_index); diff --git a/avr/cores/megacommand/MCL/MenuPage.h b/avr/cores/megacommand/MCL/MenuPage.h index f22193c79..d29426720 100644 --- a/avr/cores/megacommand/MCL/MenuPage.h +++ b/avr/cores/megacommand/MCL/MenuPage.h @@ -20,7 +20,7 @@ class MenuPage : public LightPage { Menu menu; - MenuPage(menu_t *layout, Encoder *e1 = NULL, Encoder *e2 = NULL, + MenuPage(const menu_t *layout, Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { menu.set_layout(layout); From ea9fd77d987a8ff2e48f297b24e5df874b185ae8 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 20:03:09 +0800 Subject: [PATCH 084/469] critical point: after these changes it is not bootable --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 375ca49ed..fd1183a11 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -25,13 +25,13 @@ void FileBrowserPage::init() { char temp_entry[16]; // config menu - //file_menu_page.menu.enable_entry(0, show_new_folder); - //file_menu_page.menu.enable_entry(1, true); // delete - //file_menu_page.menu.enable_entry(2, true); // rename - //file_menu_page.menu.enable_entry(3, show_overwrite); - //file_menu_page.menu.enable_entry(4, true); // cancel + file_menu_page.menu.enable_entry(0, show_new_folder); + file_menu_page.menu.enable_entry(1, true); // delete + file_menu_page.menu.enable_entry(2, true); // rename + file_menu_page.menu.enable_entry(3, show_overwrite); + file_menu_page.menu.enable_entry(4, true); // cancel file_menu_encoder.cur = file_menu_encoder.old = 0; - //file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; + file_menu_encoder.max = file_menu_page.menu.get_number_of_items() - 1; filemenu_active = false; int index = 0; @@ -102,7 +102,7 @@ void FileBrowserPage::display() { #ifdef OLED_DISPLAY if (filemenu_active) { oled_display.fillRect(0, 8, 38, 24, BLACK); - //file_menu_page.draw_menu(0, 14, 38); + file_menu_page.draw_menu(0, 14, 38); oled_display.display(); return; } @@ -179,7 +179,7 @@ void FileBrowserPage::draw_scrollbar(uint8_t x_offset) { void FileBrowserPage::loop() { if (filemenu_active) { - //file_menu_page.loop(); + file_menu_page.loop(); return; } @@ -264,8 +264,8 @@ void FileBrowserPage::_handle_filemenu() { char buf2[32] = {'\0'}; - //switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { - switch (file_menu_encoder.cur) { + switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { + //switch (file_menu_encoder.cur) { case 0: // new folder create_folder(); break; @@ -307,7 +307,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { filemenu_active = true; encoders[0] = &config_param1; encoders[1] = &file_menu_encoder; - //file_menu_page.init(); + file_menu_page.init(); return false; } From b4985e0b9eeaa17dd4b502876a825f4b4b0b3f85 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 19 Oct 2019 23:06:46 +1100 Subject: [PATCH 085/469] Add MD style encoder sprites --- art/sprites/encoder1.png | Bin 0 -> 150 bytes art/sprites/encoder2.png | Bin 0 -> 146 bytes art/sprites/encoder3.png | Bin 0 -> 150 bytes art/sprites/encoder4.png | Bin 0 -> 152 bytes art/sprites/encoder5.png | Bin 0 -> 153 bytes art/sprites/encoder6.png | Bin 0 -> 150 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/encoder1.png create mode 100644 art/sprites/encoder2.png create mode 100644 art/sprites/encoder3.png create mode 100644 art/sprites/encoder4.png create mode 100644 art/sprites/encoder5.png create mode 100644 art/sprites/encoder6.png diff --git a/art/sprites/encoder1.png b/art/sprites/encoder1.png new file mode 100644 index 0000000000000000000000000000000000000000..18da7b188e85cfcb981216a4aba6252116504b29 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|0z6$DLnI`p zPVnSoP~c!b`v1S=;Y1-WiN)eCk{oltKJMIf#ant=4o~5uS)M^0H>cJ$bc*PC6}pS)iGRBUQULCC{pOZGIqVE-vA^xIh{T^VRLgQu&X%Q~loCIIkPH9Y_T literal 0 HcmV?d00001 diff --git a/art/sprites/encoder2.png b/art/sprites/encoder2.png new file mode 100644 index 0000000000000000000000000000000000000000..2d177478c3f1b772172bf911660e67592e459990 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|d^}woLnI`p zPH^O7P~bRx(w6mUV3V^-(Bt~+5j{SGN&-3c_8!PC{xWt~$(699W*Gb#W8 literal 0 HcmV?d00001 diff --git a/art/sprites/encoder3.png b/art/sprites/encoder3.png new file mode 100644 index 0000000000000000000000000000000000000000..e4acaa517bc05d04e3cfdfcd345a2d4cc10d27c5 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|0z6$DLnI`p z1~_stC~&NP@ju>-hs9hyQ2q9^W#H$P literal 0 HcmV?d00001 diff --git a/art/sprites/encoder4.png b/art/sprites/encoder4.png new file mode 100644 index 0000000000000000000000000000000000000000..03d44ff70e53ed69a4055bc8e23c8c1999391e55 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|f;?RuLnI_y zCj@daC~!Dy@A^NLmpSoVWbMs4=O4;^mGlsP6|#%7_g+Ypp{kdI*y{cE>)-upOPR^9 zezE7*<~|MCzu(LiZ|$Dsf6pX0Xi3MD4Oi@Ao(lg7nexO7XgY(ZtDnm{r-UW|AqF-f literal 0 HcmV?d00001 diff --git a/art/sprites/encoder5.png b/art/sprites/encoder5.png new file mode 100644 index 0000000000000000000000000000000000000000..92005f72da745760cdb4a37f14e347b0705efe8f GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|f<0XvLnI_y zCj{~_C~!D!{(b-ECg1iYSN%Kui%#z7Z&F<_*@VL}JM13oQW5@{Z~kS~?RqRa`Sjlc zp~NrqZ-sxH{qS*d(eL$FP1K`h xToZU0t@SeZk=prx+BdV#$Vz*xuH)d{67QNJ_?xprKnG|xgQu&X%Q~loCIFiYHNgM? literal 0 HcmV?d00001 From e9320283c8922510fcf8bfaa251a98bedfc9e937 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Oct 2019 22:23:18 +0800 Subject: [PATCH 086/469] moving TomThumb definition to cpp --- .../Adafruit-GFX-Library/Fonts/TomThumb.cpp | 429 +++++++++++++++++ .../Adafruit-GFX-Library/Fonts/TomThumb.h | 430 +----------------- 2 files changed, 438 insertions(+), 421 deletions(-) create mode 100644 avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp new file mode 100644 index 000000000..9c0bbacad --- /dev/null +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp @@ -0,0 +1,429 @@ +#include "TomThumb.h" +#include +#include "gfxfont.h" + +/* {offset, width, height, advance cursor, x offset, y offset} */ +const uint8_t TomThumbBitmaps[] PROGMEM = { + 0x00, /* 0x20 space */ + 0x80, 0x80, 0x80, 0x00, 0x80, /* 0x21 exclam */ + 0xA0, 0xA0, /* 0x22 quotedbl */ + 0xA0, 0xE0, 0xA0, 0xE0, 0xA0, /* 0x23 numbersign */ + 0x60, 0xC0, 0x60, 0xC0, 0x40, /* 0x24 dollar */ + 0x80, 0x20, 0x40, 0x80, 0x20, /* 0x25 percent */ + 0xC0, 0xC0, 0xE0, 0xA0, 0x60, /* 0x26 ampersand */ + 0x80, 0x80, /* 0x27 quotesingle */ + 0x40, 0x80, 0x80, 0x80, 0x40, /* 0x28 parenleft */ + 0x80, 0x40, 0x40, 0x40, 0x80, /* 0x29 parenright */ + 0xA0, 0x40, 0xA0, /* 0x2A asterisk */ + 0x40, 0xE0, 0x40, /* 0x2B plus */ + 0x40, 0x80, /* 0x2C comma */ + 0xE0, /* 0x2D hyphen */ + 0x80, /* 0x2E period */ + 0x20, 0x20, 0x40, 0x80, 0x80, /* 0x2F slash */ + 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0x30 zero */ //Edited for aesthetics + 0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x31 one */ //Edited for aesthetics + 0xC0, 0x20, 0x40, 0x80, 0xE0, /* 0x32 two */ + 0xC0, 0x20, 0x40, 0x20, 0xC0, /* 0x33 three */ + 0xA0, 0xA0, 0xE0, 0x20, 0x20, /* 0x34 four */ + 0xE0, 0x80, 0xC0, 0x20, 0xC0, /* 0x35 five */ + 0x60, 0x80, 0xE0, 0xA0, 0xE0, /* 0x36 six */ + 0xE0, 0x20, 0x40, 0x80, 0x80, /* 0x37 seven */ + 0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /* 0x38 eight */ + 0xE0, 0xA0, 0xE0, 0x20, 0xC0, /* 0x39 nine */ + 0x80, 0x00, 0x80, /* 0x3A colon */ + 0x40, 0x00, 0x40, 0x80, /* 0x3B semicolon */ //edited + 0x00, 0x40, 0x80, 0x40, 0x00, /* 0x3C less */ + 0xE0, 0x00, 0xE0, /* 0x3D equal */ + 0x00, 0x40, 0x20, 0x40, 0x00, /* 0x3E greater */ //edited + 0xE0, 0x20, 0x40, 0x00, 0x40, /* 0x3F question */ + 0x40, 0xA0, 0xE0, 0x80, 0x60, /* 0x40 at */ + 0x40, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x41 A */ + 0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /* 0x42 B */ + 0x60, 0x80, 0x80, 0x80, 0x60, /* 0x43 C */ + 0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x44 D */ + 0xE0, 0x80, 0xE0, 0x80, 0xE0, /* 0x45 E */ + 0xE0, 0x80, 0xE0, 0x80, 0x80, /* 0x46 F */ + 0x60, 0x80, 0xA0, 0xA0, 0x60, /* 0x47 G */ //Edited for aesthetics + 0xA0, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x48 H */ + 0xE0, 0x40, 0x40, 0x40, 0xE0, /* 0x49 I */ + 0x20, 0x20, 0x20, 0xA0, 0x40, /* 0x4A J */ + 0xA0, 0xA0, 0xC0, 0xA0, 0xA0, /* 0x4B K */ + 0x80, 0x80, 0x80, 0x80, 0xE0, /* 0x4C L */ + 0xA0, 0xE0, 0xE0, 0xA0, 0xA0, /* 0x4D M */ + 0xC0, 0xA0, 0xA0, 0xA0, 0xA0, /* 0x4E N */ //Edited for improved legibility + 0x40, 0xA0, 0xA0, 0xA0, 0x40, /* 0x4F O */ + 0xC0, 0xA0, 0xC0, 0x80, 0x80, /* 0x50 P */ + 0x40, 0xA0, 0xA0, 0xE0, 0x60, /* 0x51 Q */ + 0xC0, 0xA0, 0xE0, 0xC0, 0xA0, /* 0x52 R */ + 0x60, 0x80, 0x40, 0x20, 0xC0, /* 0x53 S */ + 0xE0, 0x40, 0x40, 0x40, 0x40, /* 0x54 T */ + 0xA0, 0xA0, 0xA0, 0xA0, 0x60, /* 0x55 U */ + 0xA0, 0xA0, 0xA0, 0x40, 0x40, /* 0x56 V */ + 0xA0, 0xA0, 0xE0, 0xE0, 0xA0, /* 0x57 W */ + 0xA0, 0xA0, 0x40, 0xA0, 0xA0, /* 0x58 X */ + 0xA0, 0xA0, 0x40, 0x40, 0x40, /* 0x59 Y */ + 0xE0, 0x20, 0x40, 0x80, 0xE0, /* 0x5A Z */ + 0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x5B bracketleft */ + 0x80, 0x40, 0x20, /* 0x5C backslash */ + 0xE0, 0x20, 0x20, 0x20, 0xE0, /* 0x5D bracketright */ + 0x40, 0xA0, /* 0x5E asciicircum */ + 0xE0, /* 0x5F underscore */ + 0x00, 0x00, /* 0x60 space white */ + 0xC0, 0x60, 0xA0, 0xE0, /* 0x61 a */ + 0x80, 0xC0, 0xA0, 0xA0, 0xC0, /* 0x62 b */ + 0x60, 0x80, 0x80, 0x60, /* 0x63 c */ + 0x20, 0x60, 0xA0, 0xA0, 0x60, /* 0x64 d */ + 0x60, 0xA0, 0xC0, 0x60, /* 0x65 e */ + 0x20, 0x40, 0xE0, 0x40, 0x40, /* 0x66 f */ + 0x60, 0xA0, 0xE0, 0x20, 0x40, /* 0x67 g */ + 0x80, 0xC0, 0xA0, 0xA0, 0xA0, /* 0x68 h */ + 0x80, 0x00, 0x80, 0x80, 0x80, /* 0x69 i */ + 0x20, 0x00, 0x20, 0x20, 0xA0, 0x40, /* 0x6A j */ + 0x80, 0xA0, 0xC0, 0xC0, 0xA0, /* 0x6B k */ + 0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x6C l */ + 0xE0, 0xE0, 0xE0, 0xA0, /* 0x6D m */ + 0xC0, 0xA0, 0xA0, 0xA0, /* 0x6E n */ + 0x40, 0xA0, 0xA0, 0x40, /* 0x6F o */ + 0xC0, 0xA0, 0xA0, 0xC0, 0x80, /* 0x70 p */ + 0x60, 0xA0, 0xA0, 0x60, 0x20, /* 0x71 q */ + 0x60, 0x80, 0x80, 0x80, /* 0x72 r */ + 0x60, 0xC0, 0x60, 0xC0, /* 0x73 s */ + 0x40, 0xE0, 0x40, 0x40, 0x60, /* 0x74 t */ + 0xA0, 0xA0, 0xA0, 0x60, /* 0x75 u */ + 0xA0, 0xA0, 0xE0, 0x40, /* 0x76 v */ + 0xA0, 0xE0, 0xE0, 0xE0, /* 0x77 w */ + 0xA0, 0x40, 0x40, 0xA0, /* 0x78 x */ + 0xA0, 0xA0, 0x60, 0x20, 0x40, /* 0x79 y */ + 0xE0, 0x60, 0xC0, 0xE0, /* 0x7A z */ + 0x60, 0x40, 0x80, 0x40, 0x60, /* 0x7B braceleft */ + 0x80, 0x80, 0x00, 0x80, 0x80, /* 0x7C bar */ + 0xC0, 0x40, 0x20, 0x40, 0xC0, /* 0x7D braceright */ + 0x60, 0xC0, /* 0x7E asciitilde */ +#if (TOMTHUMB_USE_EXTENDED) + 0x80, 0x00, 0x80, 0x80, 0x80, /* 0xA1 exclamdown */ + 0x40, 0xE0, 0x80, 0xE0, 0x40, /* 0xA2 cent */ + 0x60, 0x40, 0xE0, 0x40, 0xE0, /* 0xA3 sterling */ + 0xA0, 0x40, 0xE0, 0x40, 0xA0, /* 0xA4 currency */ + 0xA0, 0xA0, 0x40, 0xE0, 0x40, /* 0xA5 yen */ + 0x80, 0x80, 0x00, 0x80, 0x80, /* 0xA6 brokenbar */ + 0x60, 0x40, 0xA0, 0x40, 0xC0, /* 0xA7 section */ + 0xA0, /* 0xA8 dieresis */ + 0x60, 0x80, 0x60, /* 0xA9 copyright */ + 0x60, 0xA0, 0xE0, 0x00, 0xE0, /* 0xAA ordfeminine */ + 0x40, 0x80, 0x40, /* 0xAB guillemotleft */ + 0xE0, 0x20, /* 0xAC logicalnot */ + 0xC0, /* 0xAD softhyphen */ + 0xC0, 0xC0, 0xA0, /* 0xAE registered */ + 0xE0, /* 0xAF macron */ + 0x40, 0xA0, 0x40, /* 0xB0 degree */ + 0x40, 0xE0, 0x40, 0x00, 0xE0, /* 0xB1 plusminus */ + 0xC0, 0x40, 0x60, /* 0xB2 twosuperior */ + 0xE0, 0x60, 0xE0, /* 0xB3 threesuperior */ + 0x40, 0x80, /* 0xB4 acute */ + 0xA0, 0xA0, 0xA0, 0xC0, 0x80, /* 0xB5 mu */ + 0x60, 0xA0, 0x60, 0x60, 0x60, /* 0xB6 paragraph */ + 0xE0, 0xE0, 0xE0, /* 0xB7 periodcentered */ + 0x40, 0x20, 0xC0, /* 0xB8 cedilla */ + 0x80, 0x80, 0x80, /* 0xB9 onesuperior */ + 0x40, 0xA0, 0x40, 0x00, 0xE0, /* 0xBA ordmasculine */ + 0x80, 0x40, 0x80, /* 0xBB guillemotright */ + 0x80, 0x80, 0x00, 0x60, 0x20, /* 0xBC onequarter */ + 0x80, 0x80, 0x00, 0xC0, 0x60, /* 0xBD onehalf */ + 0xC0, 0xC0, 0x00, 0x60, 0x20, /* 0xBE threequarters */ + 0x40, 0x00, 0x40, 0x80, 0xE0, /* 0xBF questiondown */ + 0x40, 0x20, 0x40, 0xE0, 0xA0, /* 0xC0 Agrave */ + 0x40, 0x80, 0x40, 0xE0, 0xA0, /* 0xC1 Aacute */ + 0xE0, 0x00, 0x40, 0xE0, 0xA0, /* 0xC2 Acircumflex */ + 0x60, 0xC0, 0x40, 0xE0, 0xA0, /* 0xC3 Atilde */ + 0xA0, 0x40, 0xA0, 0xE0, 0xA0, /* 0xC4 Adieresis */ + 0xC0, 0xC0, 0xA0, 0xE0, 0xA0, /* 0xC5 Aring */ + 0x60, 0xC0, 0xE0, 0xC0, 0xE0, /* 0xC6 AE */ + 0x60, 0x80, 0x80, 0x60, 0x20, 0x40, /* 0xC7 Ccedilla */ + 0x40, 0x20, 0xE0, 0xC0, 0xE0, /* 0xC8 Egrave */ + 0x40, 0x80, 0xE0, 0xC0, 0xE0, /* 0xC9 Eacute */ + 0xE0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCA Ecircumflex */ + 0xA0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCB Edieresis */ + 0x40, 0x20, 0xE0, 0x40, 0xE0, /* 0xCC Igrave */ + 0x40, 0x80, 0xE0, 0x40, 0xE0, /* 0xCD Iacute */ + 0xE0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCE Icircumflex */ + 0xA0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCF Idieresis */ + 0xC0, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD0 Eth */ + 0xC0, 0x60, 0xA0, 0xE0, 0xA0, /* 0xD1 Ntilde */ + 0x40, 0x20, 0xE0, 0xA0, 0xE0, /* 0xD2 Ograve */ + 0x40, 0x80, 0xE0, 0xA0, 0xE0, /* 0xD3 Oacute */ + 0xE0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD4 Ocircumflex */ + 0xC0, 0x60, 0xE0, 0xA0, 0xE0, /* 0xD5 Otilde */ + 0xA0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD6 Odieresis */ + 0xA0, 0x40, 0xA0, /* 0xD7 multiply */ + 0x60, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD8 Oslash */ + 0x80, 0x40, 0xA0, 0xA0, 0xE0, /* 0xD9 Ugrave */ + 0x20, 0x40, 0xA0, 0xA0, 0xE0, /* 0xDA Uacute */ + 0xE0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDB Ucircumflex */ + 0xA0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDC Udieresis */ + 0x20, 0x40, 0xA0, 0xE0, 0x40, /* 0xDD Yacute */ + 0x80, 0xE0, 0xA0, 0xE0, 0x80, /* 0xDE Thorn */ + 0x60, 0xA0, 0xC0, 0xA0, 0xC0, 0x80, /* 0xDF germandbls */ + 0x40, 0x20, 0x60, 0xA0, 0xE0, /* 0xE0 agrave */ + 0x40, 0x80, 0x60, 0xA0, 0xE0, /* 0xE1 aacute */ + 0xE0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE2 acircumflex */ + 0x60, 0xC0, 0x60, 0xA0, 0xE0, /* 0xE3 atilde */ + 0xA0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE4 adieresis */ + 0x60, 0x60, 0x60, 0xA0, 0xE0, /* 0xE5 aring */ + 0x60, 0xE0, 0xE0, 0xC0, /* 0xE6 ae */ + 0x60, 0x80, 0x60, 0x20, 0x40, /* 0xE7 ccedilla */ + 0x40, 0x20, 0x60, 0xE0, 0x60, /* 0xE8 egrave */ + 0x40, 0x80, 0x60, 0xE0, 0x60, /* 0xE9 eacute */ + 0xE0, 0x00, 0x60, 0xE0, 0x60, /* 0xEA ecircumflex */ + 0xA0, 0x00, 0x60, 0xE0, 0x60, /* 0xEB edieresis */ + 0x80, 0x40, 0x80, 0x80, 0x80, /* 0xEC igrave */ + 0x40, 0x80, 0x40, 0x40, 0x40, /* 0xED iacute */ + 0xE0, 0x00, 0x40, 0x40, 0x40, /* 0xEE icircumflex */ + 0xA0, 0x00, 0x40, 0x40, 0x40, /* 0xEF idieresis */ + 0x60, 0xC0, 0x60, 0xA0, 0x60, /* 0xF0 eth */ + 0xC0, 0x60, 0xC0, 0xA0, 0xA0, /* 0xF1 ntilde */ + 0x40, 0x20, 0x40, 0xA0, 0x40, /* 0xF2 ograve */ + 0x40, 0x80, 0x40, 0xA0, 0x40, /* 0xF3 oacute */ + 0xE0, 0x00, 0x40, 0xA0, 0x40, /* 0xF4 ocircumflex */ + 0xC0, 0x60, 0x40, 0xA0, 0x40, /* 0xF5 otilde */ + 0xA0, 0x00, 0x40, 0xA0, 0x40, /* 0xF6 odieresis */ + 0x40, 0x00, 0xE0, 0x00, 0x40, /* 0xF7 divide */ + 0x60, 0xE0, 0xA0, 0xC0, /* 0xF8 oslash */ + 0x80, 0x40, 0xA0, 0xA0, 0x60, /* 0xF9 ugrave */ + 0x20, 0x40, 0xA0, 0xA0, 0x60, /* 0xFA uacute */ + 0xE0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFB ucircumflex */ + 0xA0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFC udieresis */ + 0x20, 0x40, 0xA0, 0x60, 0x20, 0x40, /* 0xFD yacute */ + 0x80, 0xC0, 0xA0, 0xC0, 0x80, /* 0xFE thorn */ + 0xA0, 0x00, 0xA0, 0x60, 0x20, 0x40, /* 0xFF ydieresis */ + 0x00, /* 0x11D gcircumflex */ + 0x60, 0xC0, 0xE0, 0xC0, 0x60, /* 0x152 OE */ + 0x60, 0xE0, 0xC0, 0xE0, /* 0x153 oe */ + 0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x160 Scaron */ + 0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x161 scaron */ + 0xA0, 0x00, 0xA0, 0x40, 0x40, /* 0x178 Ydieresis */ + 0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17D Zcaron */ + 0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17E zcaron */ + 0x00, /* 0xEA4 uni0EA4 */ + 0x00, /* 0x13A0 uni13A0 */ + 0x80, /* 0x2022 bullet */ + 0xA0, /* 0x2026 ellipsis */ + 0x60, 0xE0, 0xE0, 0xC0, 0x60, /* 0x20AC Euro */ + 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0xFFFD uniFFFD */ +#endif /* (TOMTHUMB_USE_EXTENDED) */ + }; + + +/* {offset, width, height, advance cursor, x offset, y offset} */ +const GFXglyph TomThumbGlyphs[] PROGMEM = { + { 0, 8, 1, 2, 0, -5 }, /* 0x20 space */ + { 1, 8, 5, 2, 0, -5 }, /* 0x21 exclam */ + { 6, 8, 2, 4, 0, -5 }, /* 0x22 quotedbl */ + { 8, 8, 5, 4, 0, -5 }, /* 0x23 numbersign */ + { 13, 8, 5, 4, 0, -5 }, /* 0x24 dollar */ + { 18, 8, 5, 4, 0, -5 }, /* 0x25 percent */ + { 23, 8, 5, 4, 0, -5 }, /* 0x26 ampersand */ + { 28, 8, 2, 2, 0, -5 }, /* 0x27 quotesingle */ + { 30, 8, 5, 3, 0, -5 }, /* 0x28 parenleft */ + { 35, 8, 5, 3, 0, -5 }, /* 0x29 parenright */ + { 40, 8, 3, 4, 0, -5 }, /* 0x2A asterisk */ + { 43, 8, 3, 4, 0, -4 }, /* 0x2B plus */ + { 46, 8, 2, 3, 0, -2 }, /* 0x2C comma */ + { 48, 8, 1, 4, 0, -3 }, /* 0x2D hyphen */ + { 49, 8, 1, 2, 0, -1 }, /* 0x2E period */ + { 50, 8, 5, 4, 0, -5 }, /* 0x2F slash */ + { 55, 8, 5, 4, 0, -5 }, /* 0x30 zero */ + { 60, 8, 5, 4, 0, -5 }, /* 0x31 one */ //Spacing improved + { 65, 8, 5, 4, 0, -5 }, /* 0x32 two */ + { 70, 8, 5, 4, 0, -5 }, /* 0x33 three */ + { 75, 8, 5, 4, 0, -5 }, /* 0x34 four */ + { 80, 8, 5, 4, 0, -5 }, /* 0x35 five */ + { 85, 8, 5, 4, 0, -5 }, /* 0x36 six */ + { 90, 8, 5, 4, 0, -5 }, /* 0x37 seven */ + { 95, 8, 5, 4, 0, -5 }, /* 0x38 eight */ + { 100, 8, 5, 4, 0, -5 }, /* 0x39 nine */ + { 105, 8, 3, 2, 0, -4 }, /* 0x3A colon */ + { 108, 8, 4, 3, 0, -4 }, /* 0x3B semicolon */ + { 112, 8, 5, 4, 0, -5 }, /* 0x3C less */ + { 117, 8, 3, 4, 0, -4 }, /* 0x3D equal */ + { 120, 8, 5, 4, 0, -5 }, /* 0x3E greater */ + { 125, 8, 5, 4, 0, -5 }, /* 0x3F question */ + { 130, 8, 5, 4, 0, -5 }, /* 0x40 at */ + { 135, 8, 5, 4, 0, -5 }, /* 0x41 A */ + { 140, 8, 5, 4, 0, -5 }, /* 0x42 B */ + { 145, 8, 5, 4, 0, -5 }, /* 0x43 C */ + { 150, 8, 5, 4, 0, -5 }, /* 0x44 D */ + { 155, 8, 5, 4, 0, -5 }, /* 0x45 E */ + { 160, 8, 5, 4, 0, -5 }, /* 0x46 F */ + { 165, 8, 5, 4, 0, -5 }, /* 0x47 G */ + { 170, 8, 5, 4, 0, -5 }, /* 0x48 H */ + { 175, 8, 5, 4, 0, -5 }, /* 0x49 I */ + { 180, 8, 5, 4, 0, -5 }, /* 0x4A J */ + { 185, 8, 5, 4, 0, -5 }, /* 0x4B K */ + { 190, 8, 5, 4, 0, -5 }, /* 0x4C L */ + { 195, 8, 5, 4, 0, -5 }, /* 0x4D M */ + { 200, 8, 5, 4, 0, -5 }, /* 0x4E N */ + { 205, 8, 5, 4, 0, -5 }, /* 0x4F O */ + { 210, 8, 5, 4, 0, -5 }, /* 0x50 P */ + { 215, 8, 5, 4, 0, -5 }, /* 0x51 Q */ + { 220, 8, 5, 4, 0, -5 }, /* 0x52 R */ + { 225, 8, 5, 4, 0, -5 }, /* 0x53 S */ + { 230, 8, 5, 4, 0, -5 }, /* 0x54 T */ + { 235, 8, 5, 4, 0, -5 }, /* 0x55 U */ + { 240, 8, 5, 4, 0, -5 }, /* 0x56 V */ + { 245, 8, 5, 4, 0, -5 }, /* 0x57 W */ + { 250, 8, 5, 4, 0, -5 }, /* 0x58 X */ + { 255, 8, 5, 4, 0, -5 }, /* 0x59 Y */ + { 260, 8, 5, 4, 0, -5 }, /* 0x5A Z */ + { 265, 8, 5, 4, 0, -5 }, /* 0x5B bracketleft */ + { 270, 8, 3, 4, 0, -4 }, /* 0x5C backslash */ + { 273, 8, 5, 4, 0, -5 }, /* 0x5D bracketright */ + { 278, 8, 2, 4, 0, -5 }, /* 0x5E asciicircum */ + { 280, 8, 1, 4, 0, -1 }, /* 0x5F underscore */ + { 281, 8, 1, 4, 0, -5 }, /* 0x60 space wide */ + { 283, 8, 4, 4, 0, -4 }, /* 0x61 a */ + { 287, 8, 5, 4, 0, -5 }, /* 0x62 b */ + { 292, 8, 4, 4, 0, -4 }, /* 0x63 c */ + { 296, 8, 5, 4, 0, -5 }, /* 0x64 d */ + { 301, 8, 4, 4, 0, -4 }, /* 0x65 e */ + { 305, 8, 5, 4, 0, -5 }, /* 0x66 f */ + { 310, 8, 5, 4, 0, -4 }, /* 0x67 g */ + { 315, 8, 5, 4, 0, -5 }, /* 0x68 h */ + { 320, 8, 5, 2, 0, -5 }, /* 0x69 i */ + { 325, 8, 6, 4, 0, -5 }, /* 0x6A j */ + { 331, 8, 5, 4, 0, -5 }, /* 0x6B k */ + { 336, 8, 5, 4, 0, -5 }, /* 0x6C l */ + { 341, 8, 4, 4, 0, -4 }, /* 0x6D m */ + { 345, 8, 4, 4, 0, -4 }, /* 0x6E n */ + { 349, 8, 4, 4, 0, -4 }, /* 0x6F o */ + { 353, 8, 5, 4, 0, -4 }, /* 0x70 p */ + { 358, 8, 5, 4, 0, -4 }, /* 0x71 q */ + { 363, 8, 4, 4, 0, -4 }, /* 0x72 r */ + { 367, 8, 4, 4, 0, -4 }, /* 0x73 s */ + { 371, 8, 5, 4, 0, -5 }, /* 0x74 t */ + { 376, 8, 4, 4, 0, -4 }, /* 0x75 u */ + { 380, 8, 4, 4, 0, -4 }, /* 0x76 v */ + { 384, 8, 4, 4, 0, -4 }, /* 0x77 w */ + { 388, 8, 4, 4, 0, -4 }, /* 0x78 x */ + { 392, 8, 5, 4, 0, -4 }, /* 0x79 y */ + { 397, 8, 4, 4, 0, -4 }, /* 0x7A z */ + { 401, 8, 5, 4, 0, -5 }, /* 0x7B braceleft */ + { 406, 8, 5, 2, 0, -5 }, /* 0x7C bar */ + { 411, 8, 5, 4, 0, -5 }, /* 0x7D braceright */ + { 416, 8, 2, 4, 0, -5 }, /* 0x7E asciitilde */ +#if (TOMTHUMB_USE_EXTENDED) + { 418, 8, 5, 2, 0, -5 }, /* 0xA1 exclamdown */ + { 423, 8, 5, 4, 0, -5 }, /* 0xA2 cent */ + { 428, 8, 5, 4, 0, -5 }, /* 0xA3 sterling */ + { 433, 8, 5, 4, 0, -5 }, /* 0xA4 currency */ + { 438, 8, 5, 4, 0, -5 }, /* 0xA5 yen */ + { 443, 8, 5, 2, 0, -5 }, /* 0xA6 brokenbar */ + { 448, 8, 5, 4, 0, -5 }, /* 0xA7 section */ + { 453, 8, 1, 4, 0, -5 }, /* 0xA8 dieresis */ + { 454, 8, 3, 4, 0, -5 }, /* 0xA9 copyright */ + { 457, 8, 5, 4, 0, -5 }, /* 0xAA ordfeminine */ + { 462, 8, 3, 3, 0, -5 }, /* 0xAB guillemotleft */ + { 465, 8, 2, 4, 0, -4 }, /* 0xAC logicalnot */ + { 467, 8, 1, 3, 0, -3 }, /* 0xAD softhyphen */ + { 468, 8, 3, 4, 0, -5 }, /* 0xAE registered */ + { 471, 8, 1, 4, 0, -5 }, /* 0xAF macron */ + { 472, 8, 3, 4, 0, -5 }, /* 0xB0 degree */ + { 475, 8, 5, 4, 0, -5 }, /* 0xB1 plusminus */ + { 480, 8, 3, 4, 0, -5 }, /* 0xB2 twosuperior */ + { 483, 8, 3, 4, 0, -5 }, /* 0xB3 threesuperior */ + { 486, 8, 2, 3, 0, -5 }, /* 0xB4 acute */ + { 488, 8, 5, 4, 0, -5 }, /* 0xB5 mu */ + { 493, 8, 5, 4, 0, -5 }, /* 0xB6 paragraph */ + { 498, 8, 3, 4, 0, -4 }, /* 0xB7 periodcentered */ + { 501, 8, 3, 4, 0, -3 }, /* 0xB8 cedilla */ + { 504, 8, 3, 2, 0, -5 }, /* 0xB9 onesuperior */ + { 507, 8, 5, 4, 0, -5 }, /* 0xBA ordmasculine */ + { 512, 8, 3, 3, 0, -5 }, /* 0xBB guillemotright */ + { 515, 8, 5, 4, 0, -5 }, /* 0xBC onequarter */ + { 520, 8, 5, 4, 0, -5 }, /* 0xBD onehalf */ + { 525, 8, 5, 4, 0, -5 }, /* 0xBE threequarters */ + { 530, 8, 5, 4, 0, -5 }, /* 0xBF questiondown */ + { 535, 8, 5, 4, 0, -5 }, /* 0xC0 Agrave */ + { 540, 8, 5, 4, 0, -5 }, /* 0xC1 Aacute */ + { 545, 8, 5, 4, 0, -5 }, /* 0xC2 Acircumflex */ + { 550, 8, 5, 4, 0, -5 }, /* 0xC3 Atilde */ + { 555, 8, 5, 4, 0, -5 }, /* 0xC4 Adieresis */ + { 560, 8, 5, 4, 0, -5 }, /* 0xC5 Aring */ + { 565, 8, 5, 4, 0, -5 }, /* 0xC6 AE */ + { 570, 8, 6, 4, 0, -5 }, /* 0xC7 Ccedilla */ + { 576, 8, 5, 4, 0, -5 }, /* 0xC8 Egrave */ + { 581, 8, 5, 4, 0, -5 }, /* 0xC9 Eacute */ + { 586, 8, 5, 4, 0, -5 }, /* 0xCA Ecircumflex */ + { 591, 8, 5, 4, 0, -5 }, /* 0xCB Edieresis */ + { 596, 8, 5, 4, 0, -5 }, /* 0xCC Igrave */ + { 601, 8, 5, 4, 0, -5 }, /* 0xCD Iacute */ + { 606, 8, 5, 4, 0, -5 }, /* 0xCE Icircumflex */ + { 611, 8, 5, 4, 0, -5 }, /* 0xCF Idieresis */ + { 616, 8, 5, 4, 0, -5 }, /* 0xD0 Eth */ + { 621, 8, 5, 4, 0, -5 }, /* 0xD1 Ntilde */ + { 626, 8, 5, 4, 0, -5 }, /* 0xD2 Ograve */ + { 631, 8, 5, 4, 0, -5 }, /* 0xD3 Oacute */ + { 636, 8, 5, 4, 0, -5 }, /* 0xD4 Ocircumflex */ + { 641, 8, 5, 4, 0, -5 }, /* 0xD5 Otilde */ + { 646, 8, 5, 4, 0, -5 }, /* 0xD6 Odieresis */ + { 651, 8, 3, 4, 0, -4 }, /* 0xD7 multiply */ + { 654, 8, 5, 4, 0, -5 }, /* 0xD8 Oslash */ + { 659, 8, 5, 4, 0, -5 }, /* 0xD9 Ugrave */ + { 664, 8, 5, 4, 0, -5 }, /* 0xDA Uacute */ + { 669, 8, 5, 4, 0, -5 }, /* 0xDB Ucircumflex */ + { 674, 8, 5, 4, 0, -5 }, /* 0xDC Udieresis */ + { 679, 8, 5, 4, 0, -5 }, /* 0xDD Yacute */ + { 684, 8, 5, 4, 0, -5 }, /* 0xDE Thorn */ + { 689, 8, 6, 4, 0, -5 }, /* 0xDF germandbls */ + { 695, 8, 5, 4, 0, -5 }, /* 0xE0 agrave */ + { 700, 8, 5, 4, 0, -5 }, /* 0xE1 aacute */ + { 705, 8, 5, 4, 0, -5 }, /* 0xE2 acircumflex */ + { 710, 8, 5, 4, 0, -5 }, /* 0xE3 atilde */ + { 715, 8, 5, 4, 0, -5 }, /* 0xE4 adieresis */ + { 720, 8, 5, 4, 0, -5 }, /* 0xE5 aring */ + { 725, 8, 4, 4, 0, -4 }, /* 0xE6 ae */ + { 729, 8, 5, 4, 0, -4 }, /* 0xE7 ccedilla */ + { 734, 8, 5, 4, 0, -5 }, /* 0xE8 egrave */ + { 739, 8, 5, 4, 0, -5 }, /* 0xE9 eacute */ + { 744, 8, 5, 4, 0, -5 }, /* 0xEA ecircumflex */ + { 749, 8, 5, 4, 0, -5 }, /* 0xEB edieresis */ + { 754, 8, 5, 3, 0, -5 }, /* 0xEC igrave */ + { 759, 8, 5, 3, 0, -5 }, /* 0xED iacute */ + { 764, 8, 5, 4, 0, -5 }, /* 0xEE icircumflex */ + { 769, 8, 5, 4, 0, -5 }, /* 0xEF idieresis */ + { 774, 8, 5, 4, 0, -5 }, /* 0xF0 eth */ + { 779, 8, 5, 4, 0, -5 }, /* 0xF1 ntilde */ + { 784, 8, 5, 4, 0, -5 }, /* 0xF2 ograve */ + { 789, 8, 5, 4, 0, -5 }, /* 0xF3 oacute */ + { 794, 8, 5, 4, 0, -5 }, /* 0xF4 ocircumflex */ + { 799, 8, 5, 4, 0, -5 }, /* 0xF5 otilde */ + { 804, 8, 5, 4, 0, -5 }, /* 0xF6 odieresis */ + { 809, 8, 5, 4, 0, -5 }, /* 0xF7 divide */ + { 814, 8, 4, 4, 0, -4 }, /* 0xF8 oslash */ + { 818, 8, 5, 4, 0, -5 }, /* 0xF9 ugrave */ + { 823, 8, 5, 4, 0, -5 }, /* 0xFA uacute */ + { 828, 8, 5, 4, 0, -5 }, /* 0xFB ucircumflex */ + { 833, 8, 5, 4, 0, -5 }, /* 0xFC udieresis */ + { 838, 8, 6, 4, 0, -5 }, /* 0xFD yacute */ + { 844, 8, 5, 4, 0, -4 }, /* 0xFE thorn */ + { 849, 8, 6, 4, 0, -5 }, /* 0xFF ydieresis */ + { 855, 8, 1, 2, 0, -1 }, /* 0x11D gcircumflex */ + { 856, 8, 5, 4, 0, -5 }, /* 0x152 OE */ + { 861, 8, 4, 4, 0, -4 }, /* 0x153 oe */ + { 865, 8, 5, 4, 0, -5 }, /* 0x160 Scaron */ + { 870, 8, 5, 4, 0, -5 }, /* 0x161 scaron */ + { 875, 8, 5, 4, 0, -5 }, /* 0x178 Ydieresis */ + { 880, 8, 5, 4, 0, -5 }, /* 0x17D Zcaron */ + { 885, 8, 5, 4, 0, -5 }, /* 0x17E zcaron */ + { 890, 8, 1, 2, 0, -1 }, /* 0xEA4 uni0EA4 */ + { 891, 8, 1, 2, 0, -1 }, /* 0x13A0 uni13A0 */ + { 892, 8, 1, 2, 0, -3 }, /* 0x2022 bullet */ + { 893, 8, 1, 4, 0, -1 }, /* 0x2026 ellipsis */ + { 894, 8, 5, 4, 0, -5 }, /* 0x20AC Euro */ + { 899, 8, 5, 4, 0, -5 }, /* 0xFFFD uniFFFD */ +#endif /* (TOMTHUMB_USE_EXTENDED) */ +}; + +const GFXfont TomThumb PROGMEM = { + (uint8_t *)TomThumbBitmaps, + (GFXglyph *)TomThumbGlyphs, + 0x20, 0x7E, 6 }; diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.h b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.h index e5eaa2a78..8612a7f7e 100755 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.h +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.h @@ -47,429 +47,17 @@ ** Sept 7, 2018: Glyph changes for MCL (Justin Mammarella) */ +#ifndef _TOMTHUMB_H__ +#define _TOMTHUMB_H__ + +#include +#include "gfxfont.h" #define TOMTHUMB_USE_EXTENDED 0 -const uint8_t TomThumbBitmaps[] PROGMEM = { - 0x00, /* 0x20 space */ - 0x80, 0x80, 0x80, 0x00, 0x80, /* 0x21 exclam */ - 0xA0, 0xA0, /* 0x22 quotedbl */ - 0xA0, 0xE0, 0xA0, 0xE0, 0xA0, /* 0x23 numbersign */ - 0x60, 0xC0, 0x60, 0xC0, 0x40, /* 0x24 dollar */ - 0x80, 0x20, 0x40, 0x80, 0x20, /* 0x25 percent */ - 0xC0, 0xC0, 0xE0, 0xA0, 0x60, /* 0x26 ampersand */ - 0x80, 0x80, /* 0x27 quotesingle */ - 0x40, 0x80, 0x80, 0x80, 0x40, /* 0x28 parenleft */ - 0x80, 0x40, 0x40, 0x40, 0x80, /* 0x29 parenright */ - 0xA0, 0x40, 0xA0, /* 0x2A asterisk */ - 0x40, 0xE0, 0x40, /* 0x2B plus */ - 0x40, 0x80, /* 0x2C comma */ - 0xE0, /* 0x2D hyphen */ - 0x80, /* 0x2E period */ - 0x20, 0x20, 0x40, 0x80, 0x80, /* 0x2F slash */ - 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0x30 zero */ //Edited for aesthetics - 0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x31 one */ //Edited for aesthetics - 0xC0, 0x20, 0x40, 0x80, 0xE0, /* 0x32 two */ - 0xC0, 0x20, 0x40, 0x20, 0xC0, /* 0x33 three */ - 0xA0, 0xA0, 0xE0, 0x20, 0x20, /* 0x34 four */ - 0xE0, 0x80, 0xC0, 0x20, 0xC0, /* 0x35 five */ - 0x60, 0x80, 0xE0, 0xA0, 0xE0, /* 0x36 six */ - 0xE0, 0x20, 0x40, 0x80, 0x80, /* 0x37 seven */ - 0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /* 0x38 eight */ - 0xE0, 0xA0, 0xE0, 0x20, 0xC0, /* 0x39 nine */ - 0x80, 0x00, 0x80, /* 0x3A colon */ - 0x40, 0x00, 0x40, 0x80, /* 0x3B semicolon */ //edited - 0x00, 0x40, 0x80, 0x40, 0x00, /* 0x3C less */ - 0xE0, 0x00, 0xE0, /* 0x3D equal */ - 0x00, 0x40, 0x20, 0x40, 0x00, /* 0x3E greater */ //edited - 0xE0, 0x20, 0x40, 0x00, 0x40, /* 0x3F question */ - 0x40, 0xA0, 0xE0, 0x80, 0x60, /* 0x40 at */ - 0x40, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x41 A */ - 0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /* 0x42 B */ - 0x60, 0x80, 0x80, 0x80, 0x60, /* 0x43 C */ - 0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x44 D */ - 0xE0, 0x80, 0xE0, 0x80, 0xE0, /* 0x45 E */ - 0xE0, 0x80, 0xE0, 0x80, 0x80, /* 0x46 F */ - 0x60, 0x80, 0xA0, 0xA0, 0x60, /* 0x47 G */ //Edited for aesthetics - 0xA0, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x48 H */ - 0xE0, 0x40, 0x40, 0x40, 0xE0, /* 0x49 I */ - 0x20, 0x20, 0x20, 0xA0, 0x40, /* 0x4A J */ - 0xA0, 0xA0, 0xC0, 0xA0, 0xA0, /* 0x4B K */ - 0x80, 0x80, 0x80, 0x80, 0xE0, /* 0x4C L */ - 0xA0, 0xE0, 0xE0, 0xA0, 0xA0, /* 0x4D M */ - 0xC0, 0xA0, 0xA0, 0xA0, 0xA0, /* 0x4E N */ //Edited for improved legibility - 0x40, 0xA0, 0xA0, 0xA0, 0x40, /* 0x4F O */ - 0xC0, 0xA0, 0xC0, 0x80, 0x80, /* 0x50 P */ - 0x40, 0xA0, 0xA0, 0xE0, 0x60, /* 0x51 Q */ - 0xC0, 0xA0, 0xE0, 0xC0, 0xA0, /* 0x52 R */ - 0x60, 0x80, 0x40, 0x20, 0xC0, /* 0x53 S */ - 0xE0, 0x40, 0x40, 0x40, 0x40, /* 0x54 T */ - 0xA0, 0xA0, 0xA0, 0xA0, 0x60, /* 0x55 U */ - 0xA0, 0xA0, 0xA0, 0x40, 0x40, /* 0x56 V */ - 0xA0, 0xA0, 0xE0, 0xE0, 0xA0, /* 0x57 W */ - 0xA0, 0xA0, 0x40, 0xA0, 0xA0, /* 0x58 X */ - 0xA0, 0xA0, 0x40, 0x40, 0x40, /* 0x59 Y */ - 0xE0, 0x20, 0x40, 0x80, 0xE0, /* 0x5A Z */ - 0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x5B bracketleft */ - 0x80, 0x40, 0x20, /* 0x5C backslash */ - 0xE0, 0x20, 0x20, 0x20, 0xE0, /* 0x5D bracketright */ - 0x40, 0xA0, /* 0x5E asciicircum */ - 0xE0, /* 0x5F underscore */ - 0x00, 0x00, /* 0x60 space white */ - 0xC0, 0x60, 0xA0, 0xE0, /* 0x61 a */ - 0x80, 0xC0, 0xA0, 0xA0, 0xC0, /* 0x62 b */ - 0x60, 0x80, 0x80, 0x60, /* 0x63 c */ - 0x20, 0x60, 0xA0, 0xA0, 0x60, /* 0x64 d */ - 0x60, 0xA0, 0xC0, 0x60, /* 0x65 e */ - 0x20, 0x40, 0xE0, 0x40, 0x40, /* 0x66 f */ - 0x60, 0xA0, 0xE0, 0x20, 0x40, /* 0x67 g */ - 0x80, 0xC0, 0xA0, 0xA0, 0xA0, /* 0x68 h */ - 0x80, 0x00, 0x80, 0x80, 0x80, /* 0x69 i */ - 0x20, 0x00, 0x20, 0x20, 0xA0, 0x40, /* 0x6A j */ - 0x80, 0xA0, 0xC0, 0xC0, 0xA0, /* 0x6B k */ - 0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x6C l */ - 0xE0, 0xE0, 0xE0, 0xA0, /* 0x6D m */ - 0xC0, 0xA0, 0xA0, 0xA0, /* 0x6E n */ - 0x40, 0xA0, 0xA0, 0x40, /* 0x6F o */ - 0xC0, 0xA0, 0xA0, 0xC0, 0x80, /* 0x70 p */ - 0x60, 0xA0, 0xA0, 0x60, 0x20, /* 0x71 q */ - 0x60, 0x80, 0x80, 0x80, /* 0x72 r */ - 0x60, 0xC0, 0x60, 0xC0, /* 0x73 s */ - 0x40, 0xE0, 0x40, 0x40, 0x60, /* 0x74 t */ - 0xA0, 0xA0, 0xA0, 0x60, /* 0x75 u */ - 0xA0, 0xA0, 0xE0, 0x40, /* 0x76 v */ - 0xA0, 0xE0, 0xE0, 0xE0, /* 0x77 w */ - 0xA0, 0x40, 0x40, 0xA0, /* 0x78 x */ - 0xA0, 0xA0, 0x60, 0x20, 0x40, /* 0x79 y */ - 0xE0, 0x60, 0xC0, 0xE0, /* 0x7A z */ - 0x60, 0x40, 0x80, 0x40, 0x60, /* 0x7B braceleft */ - 0x80, 0x80, 0x00, 0x80, 0x80, /* 0x7C bar */ - 0xC0, 0x40, 0x20, 0x40, 0xC0, /* 0x7D braceright */ - 0x60, 0xC0, /* 0x7E asciitilde */ -#if (TOMTHUMB_USE_EXTENDED) - 0x80, 0x00, 0x80, 0x80, 0x80, /* 0xA1 exclamdown */ - 0x40, 0xE0, 0x80, 0xE0, 0x40, /* 0xA2 cent */ - 0x60, 0x40, 0xE0, 0x40, 0xE0, /* 0xA3 sterling */ - 0xA0, 0x40, 0xE0, 0x40, 0xA0, /* 0xA4 currency */ - 0xA0, 0xA0, 0x40, 0xE0, 0x40, /* 0xA5 yen */ - 0x80, 0x80, 0x00, 0x80, 0x80, /* 0xA6 brokenbar */ - 0x60, 0x40, 0xA0, 0x40, 0xC0, /* 0xA7 section */ - 0xA0, /* 0xA8 dieresis */ - 0x60, 0x80, 0x60, /* 0xA9 copyright */ - 0x60, 0xA0, 0xE0, 0x00, 0xE0, /* 0xAA ordfeminine */ - 0x40, 0x80, 0x40, /* 0xAB guillemotleft */ - 0xE0, 0x20, /* 0xAC logicalnot */ - 0xC0, /* 0xAD softhyphen */ - 0xC0, 0xC0, 0xA0, /* 0xAE registered */ - 0xE0, /* 0xAF macron */ - 0x40, 0xA0, 0x40, /* 0xB0 degree */ - 0x40, 0xE0, 0x40, 0x00, 0xE0, /* 0xB1 plusminus */ - 0xC0, 0x40, 0x60, /* 0xB2 twosuperior */ - 0xE0, 0x60, 0xE0, /* 0xB3 threesuperior */ - 0x40, 0x80, /* 0xB4 acute */ - 0xA0, 0xA0, 0xA0, 0xC0, 0x80, /* 0xB5 mu */ - 0x60, 0xA0, 0x60, 0x60, 0x60, /* 0xB6 paragraph */ - 0xE0, 0xE0, 0xE0, /* 0xB7 periodcentered */ - 0x40, 0x20, 0xC0, /* 0xB8 cedilla */ - 0x80, 0x80, 0x80, /* 0xB9 onesuperior */ - 0x40, 0xA0, 0x40, 0x00, 0xE0, /* 0xBA ordmasculine */ - 0x80, 0x40, 0x80, /* 0xBB guillemotright */ - 0x80, 0x80, 0x00, 0x60, 0x20, /* 0xBC onequarter */ - 0x80, 0x80, 0x00, 0xC0, 0x60, /* 0xBD onehalf */ - 0xC0, 0xC0, 0x00, 0x60, 0x20, /* 0xBE threequarters */ - 0x40, 0x00, 0x40, 0x80, 0xE0, /* 0xBF questiondown */ - 0x40, 0x20, 0x40, 0xE0, 0xA0, /* 0xC0 Agrave */ - 0x40, 0x80, 0x40, 0xE0, 0xA0, /* 0xC1 Aacute */ - 0xE0, 0x00, 0x40, 0xE0, 0xA0, /* 0xC2 Acircumflex */ - 0x60, 0xC0, 0x40, 0xE0, 0xA0, /* 0xC3 Atilde */ - 0xA0, 0x40, 0xA0, 0xE0, 0xA0, /* 0xC4 Adieresis */ - 0xC0, 0xC0, 0xA0, 0xE0, 0xA0, /* 0xC5 Aring */ - 0x60, 0xC0, 0xE0, 0xC0, 0xE0, /* 0xC6 AE */ - 0x60, 0x80, 0x80, 0x60, 0x20, 0x40, /* 0xC7 Ccedilla */ - 0x40, 0x20, 0xE0, 0xC0, 0xE0, /* 0xC8 Egrave */ - 0x40, 0x80, 0xE0, 0xC0, 0xE0, /* 0xC9 Eacute */ - 0xE0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCA Ecircumflex */ - 0xA0, 0x00, 0xE0, 0xC0, 0xE0, /* 0xCB Edieresis */ - 0x40, 0x20, 0xE0, 0x40, 0xE0, /* 0xCC Igrave */ - 0x40, 0x80, 0xE0, 0x40, 0xE0, /* 0xCD Iacute */ - 0xE0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCE Icircumflex */ - 0xA0, 0x00, 0xE0, 0x40, 0xE0, /* 0xCF Idieresis */ - 0xC0, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD0 Eth */ - 0xC0, 0x60, 0xA0, 0xE0, 0xA0, /* 0xD1 Ntilde */ - 0x40, 0x20, 0xE0, 0xA0, 0xE0, /* 0xD2 Ograve */ - 0x40, 0x80, 0xE0, 0xA0, 0xE0, /* 0xD3 Oacute */ - 0xE0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD4 Ocircumflex */ - 0xC0, 0x60, 0xE0, 0xA0, 0xE0, /* 0xD5 Otilde */ - 0xA0, 0x00, 0xE0, 0xA0, 0xE0, /* 0xD6 Odieresis */ - 0xA0, 0x40, 0xA0, /* 0xD7 multiply */ - 0x60, 0xA0, 0xE0, 0xA0, 0xC0, /* 0xD8 Oslash */ - 0x80, 0x40, 0xA0, 0xA0, 0xE0, /* 0xD9 Ugrave */ - 0x20, 0x40, 0xA0, 0xA0, 0xE0, /* 0xDA Uacute */ - 0xE0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDB Ucircumflex */ - 0xA0, 0x00, 0xA0, 0xA0, 0xE0, /* 0xDC Udieresis */ - 0x20, 0x40, 0xA0, 0xE0, 0x40, /* 0xDD Yacute */ - 0x80, 0xE0, 0xA0, 0xE0, 0x80, /* 0xDE Thorn */ - 0x60, 0xA0, 0xC0, 0xA0, 0xC0, 0x80, /* 0xDF germandbls */ - 0x40, 0x20, 0x60, 0xA0, 0xE0, /* 0xE0 agrave */ - 0x40, 0x80, 0x60, 0xA0, 0xE0, /* 0xE1 aacute */ - 0xE0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE2 acircumflex */ - 0x60, 0xC0, 0x60, 0xA0, 0xE0, /* 0xE3 atilde */ - 0xA0, 0x00, 0x60, 0xA0, 0xE0, /* 0xE4 adieresis */ - 0x60, 0x60, 0x60, 0xA0, 0xE0, /* 0xE5 aring */ - 0x60, 0xE0, 0xE0, 0xC0, /* 0xE6 ae */ - 0x60, 0x80, 0x60, 0x20, 0x40, /* 0xE7 ccedilla */ - 0x40, 0x20, 0x60, 0xE0, 0x60, /* 0xE8 egrave */ - 0x40, 0x80, 0x60, 0xE0, 0x60, /* 0xE9 eacute */ - 0xE0, 0x00, 0x60, 0xE0, 0x60, /* 0xEA ecircumflex */ - 0xA0, 0x00, 0x60, 0xE0, 0x60, /* 0xEB edieresis */ - 0x80, 0x40, 0x80, 0x80, 0x80, /* 0xEC igrave */ - 0x40, 0x80, 0x40, 0x40, 0x40, /* 0xED iacute */ - 0xE0, 0x00, 0x40, 0x40, 0x40, /* 0xEE icircumflex */ - 0xA0, 0x00, 0x40, 0x40, 0x40, /* 0xEF idieresis */ - 0x60, 0xC0, 0x60, 0xA0, 0x60, /* 0xF0 eth */ - 0xC0, 0x60, 0xC0, 0xA0, 0xA0, /* 0xF1 ntilde */ - 0x40, 0x20, 0x40, 0xA0, 0x40, /* 0xF2 ograve */ - 0x40, 0x80, 0x40, 0xA0, 0x40, /* 0xF3 oacute */ - 0xE0, 0x00, 0x40, 0xA0, 0x40, /* 0xF4 ocircumflex */ - 0xC0, 0x60, 0x40, 0xA0, 0x40, /* 0xF5 otilde */ - 0xA0, 0x00, 0x40, 0xA0, 0x40, /* 0xF6 odieresis */ - 0x40, 0x00, 0xE0, 0x00, 0x40, /* 0xF7 divide */ - 0x60, 0xE0, 0xA0, 0xC0, /* 0xF8 oslash */ - 0x80, 0x40, 0xA0, 0xA0, 0x60, /* 0xF9 ugrave */ - 0x20, 0x40, 0xA0, 0xA0, 0x60, /* 0xFA uacute */ - 0xE0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFB ucircumflex */ - 0xA0, 0x00, 0xA0, 0xA0, 0x60, /* 0xFC udieresis */ - 0x20, 0x40, 0xA0, 0x60, 0x20, 0x40, /* 0xFD yacute */ - 0x80, 0xC0, 0xA0, 0xC0, 0x80, /* 0xFE thorn */ - 0xA0, 0x00, 0xA0, 0x60, 0x20, 0x40, /* 0xFF ydieresis */ - 0x00, /* 0x11D gcircumflex */ - 0x60, 0xC0, 0xE0, 0xC0, 0x60, /* 0x152 OE */ - 0x60, 0xE0, 0xC0, 0xE0, /* 0x153 oe */ - 0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x160 Scaron */ - 0xA0, 0x60, 0xC0, 0x60, 0xC0, /* 0x161 scaron */ - 0xA0, 0x00, 0xA0, 0x40, 0x40, /* 0x178 Ydieresis */ - 0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17D Zcaron */ - 0xA0, 0xE0, 0x60, 0xC0, 0xE0, /* 0x17E zcaron */ - 0x00, /* 0xEA4 uni0EA4 */ - 0x00, /* 0x13A0 uni13A0 */ - 0x80, /* 0x2022 bullet */ - 0xA0, /* 0x2026 ellipsis */ - 0x60, 0xE0, 0xE0, 0xC0, 0x60, /* 0x20AC Euro */ - 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0xFFFD uniFFFD */ -#endif /* (TOMTHUMB_USE_EXTENDED) */ - }; +extern const uint8_t TomThumbBitmaps[]; +extern const GFXglyph TomThumbGlyphs[]; -/* {offset, width, height, advance cursor, x offset, y offset} */ -const GFXglyph TomThumbGlyphs[] PROGMEM = { - { 0, 8, 1, 2, 0, -5 }, /* 0x20 space */ - { 1, 8, 5, 2, 0, -5 }, /* 0x21 exclam */ - { 6, 8, 2, 4, 0, -5 }, /* 0x22 quotedbl */ - { 8, 8, 5, 4, 0, -5 }, /* 0x23 numbersign */ - { 13, 8, 5, 4, 0, -5 }, /* 0x24 dollar */ - { 18, 8, 5, 4, 0, -5 }, /* 0x25 percent */ - { 23, 8, 5, 4, 0, -5 }, /* 0x26 ampersand */ - { 28, 8, 2, 2, 0, -5 }, /* 0x27 quotesingle */ - { 30, 8, 5, 3, 0, -5 }, /* 0x28 parenleft */ - { 35, 8, 5, 3, 0, -5 }, /* 0x29 parenright */ - { 40, 8, 3, 4, 0, -5 }, /* 0x2A asterisk */ - { 43, 8, 3, 4, 0, -4 }, /* 0x2B plus */ - { 46, 8, 2, 3, 0, -2 }, /* 0x2C comma */ - { 48, 8, 1, 4, 0, -3 }, /* 0x2D hyphen */ - { 49, 8, 1, 2, 0, -1 }, /* 0x2E period */ - { 50, 8, 5, 4, 0, -5 }, /* 0x2F slash */ - { 55, 8, 5, 4, 0, -5 }, /* 0x30 zero */ - { 60, 8, 5, 4, 0, -5 }, /* 0x31 one */ //Spacing improved - { 65, 8, 5, 4, 0, -5 }, /* 0x32 two */ - { 70, 8, 5, 4, 0, -5 }, /* 0x33 three */ - { 75, 8, 5, 4, 0, -5 }, /* 0x34 four */ - { 80, 8, 5, 4, 0, -5 }, /* 0x35 five */ - { 85, 8, 5, 4, 0, -5 }, /* 0x36 six */ - { 90, 8, 5, 4, 0, -5 }, /* 0x37 seven */ - { 95, 8, 5, 4, 0, -5 }, /* 0x38 eight */ - { 100, 8, 5, 4, 0, -5 }, /* 0x39 nine */ - { 105, 8, 3, 2, 0, -4 }, /* 0x3A colon */ - { 108, 8, 4, 3, 0, -4 }, /* 0x3B semicolon */ - { 112, 8, 5, 4, 0, -5 }, /* 0x3C less */ - { 117, 8, 3, 4, 0, -4 }, /* 0x3D equal */ - { 120, 8, 5, 4, 0, -5 }, /* 0x3E greater */ - { 125, 8, 5, 4, 0, -5 }, /* 0x3F question */ - { 130, 8, 5, 4, 0, -5 }, /* 0x40 at */ - { 135, 8, 5, 4, 0, -5 }, /* 0x41 A */ - { 140, 8, 5, 4, 0, -5 }, /* 0x42 B */ - { 145, 8, 5, 4, 0, -5 }, /* 0x43 C */ - { 150, 8, 5, 4, 0, -5 }, /* 0x44 D */ - { 155, 8, 5, 4, 0, -5 }, /* 0x45 E */ - { 160, 8, 5, 4, 0, -5 }, /* 0x46 F */ - { 165, 8, 5, 4, 0, -5 }, /* 0x47 G */ - { 170, 8, 5, 4, 0, -5 }, /* 0x48 H */ - { 175, 8, 5, 4, 0, -5 }, /* 0x49 I */ - { 180, 8, 5, 4, 0, -5 }, /* 0x4A J */ - { 185, 8, 5, 4, 0, -5 }, /* 0x4B K */ - { 190, 8, 5, 4, 0, -5 }, /* 0x4C L */ - { 195, 8, 5, 4, 0, -5 }, /* 0x4D M */ - { 200, 8, 5, 4, 0, -5 }, /* 0x4E N */ - { 205, 8, 5, 4, 0, -5 }, /* 0x4F O */ - { 210, 8, 5, 4, 0, -5 }, /* 0x50 P */ - { 215, 8, 5, 4, 0, -5 }, /* 0x51 Q */ - { 220, 8, 5, 4, 0, -5 }, /* 0x52 R */ - { 225, 8, 5, 4, 0, -5 }, /* 0x53 S */ - { 230, 8, 5, 4, 0, -5 }, /* 0x54 T */ - { 235, 8, 5, 4, 0, -5 }, /* 0x55 U */ - { 240, 8, 5, 4, 0, -5 }, /* 0x56 V */ - { 245, 8, 5, 4, 0, -5 }, /* 0x57 W */ - { 250, 8, 5, 4, 0, -5 }, /* 0x58 X */ - { 255, 8, 5, 4, 0, -5 }, /* 0x59 Y */ - { 260, 8, 5, 4, 0, -5 }, /* 0x5A Z */ - { 265, 8, 5, 4, 0, -5 }, /* 0x5B bracketleft */ - { 270, 8, 3, 4, 0, -4 }, /* 0x5C backslash */ - { 273, 8, 5, 4, 0, -5 }, /* 0x5D bracketright */ - { 278, 8, 2, 4, 0, -5 }, /* 0x5E asciicircum */ - { 280, 8, 1, 4, 0, -1 }, /* 0x5F underscore */ - { 281, 8, 1, 4, 0, -5 }, /* 0x60 space wide */ - { 283, 8, 4, 4, 0, -4 }, /* 0x61 a */ - { 287, 8, 5, 4, 0, -5 }, /* 0x62 b */ - { 292, 8, 4, 4, 0, -4 }, /* 0x63 c */ - { 296, 8, 5, 4, 0, -5 }, /* 0x64 d */ - { 301, 8, 4, 4, 0, -4 }, /* 0x65 e */ - { 305, 8, 5, 4, 0, -5 }, /* 0x66 f */ - { 310, 8, 5, 4, 0, -4 }, /* 0x67 g */ - { 315, 8, 5, 4, 0, -5 }, /* 0x68 h */ - { 320, 8, 5, 2, 0, -5 }, /* 0x69 i */ - { 325, 8, 6, 4, 0, -5 }, /* 0x6A j */ - { 331, 8, 5, 4, 0, -5 }, /* 0x6B k */ - { 336, 8, 5, 4, 0, -5 }, /* 0x6C l */ - { 341, 8, 4, 4, 0, -4 }, /* 0x6D m */ - { 345, 8, 4, 4, 0, -4 }, /* 0x6E n */ - { 349, 8, 4, 4, 0, -4 }, /* 0x6F o */ - { 353, 8, 5, 4, 0, -4 }, /* 0x70 p */ - { 358, 8, 5, 4, 0, -4 }, /* 0x71 q */ - { 363, 8, 4, 4, 0, -4 }, /* 0x72 r */ - { 367, 8, 4, 4, 0, -4 }, /* 0x73 s */ - { 371, 8, 5, 4, 0, -5 }, /* 0x74 t */ - { 376, 8, 4, 4, 0, -4 }, /* 0x75 u */ - { 380, 8, 4, 4, 0, -4 }, /* 0x76 v */ - { 384, 8, 4, 4, 0, -4 }, /* 0x77 w */ - { 388, 8, 4, 4, 0, -4 }, /* 0x78 x */ - { 392, 8, 5, 4, 0, -4 }, /* 0x79 y */ - { 397, 8, 4, 4, 0, -4 }, /* 0x7A z */ - { 401, 8, 5, 4, 0, -5 }, /* 0x7B braceleft */ - { 406, 8, 5, 2, 0, -5 }, /* 0x7C bar */ - { 411, 8, 5, 4, 0, -5 }, /* 0x7D braceright */ - { 416, 8, 2, 4, 0, -5 }, /* 0x7E asciitilde */ -#if (TOMTHUMB_USE_EXTENDED) - { 418, 8, 5, 2, 0, -5 }, /* 0xA1 exclamdown */ - { 423, 8, 5, 4, 0, -5 }, /* 0xA2 cent */ - { 428, 8, 5, 4, 0, -5 }, /* 0xA3 sterling */ - { 433, 8, 5, 4, 0, -5 }, /* 0xA4 currency */ - { 438, 8, 5, 4, 0, -5 }, /* 0xA5 yen */ - { 443, 8, 5, 2, 0, -5 }, /* 0xA6 brokenbar */ - { 448, 8, 5, 4, 0, -5 }, /* 0xA7 section */ - { 453, 8, 1, 4, 0, -5 }, /* 0xA8 dieresis */ - { 454, 8, 3, 4, 0, -5 }, /* 0xA9 copyright */ - { 457, 8, 5, 4, 0, -5 }, /* 0xAA ordfeminine */ - { 462, 8, 3, 3, 0, -5 }, /* 0xAB guillemotleft */ - { 465, 8, 2, 4, 0, -4 }, /* 0xAC logicalnot */ - { 467, 8, 1, 3, 0, -3 }, /* 0xAD softhyphen */ - { 468, 8, 3, 4, 0, -5 }, /* 0xAE registered */ - { 471, 8, 1, 4, 0, -5 }, /* 0xAF macron */ - { 472, 8, 3, 4, 0, -5 }, /* 0xB0 degree */ - { 475, 8, 5, 4, 0, -5 }, /* 0xB1 plusminus */ - { 480, 8, 3, 4, 0, -5 }, /* 0xB2 twosuperior */ - { 483, 8, 3, 4, 0, -5 }, /* 0xB3 threesuperior */ - { 486, 8, 2, 3, 0, -5 }, /* 0xB4 acute */ - { 488, 8, 5, 4, 0, -5 }, /* 0xB5 mu */ - { 493, 8, 5, 4, 0, -5 }, /* 0xB6 paragraph */ - { 498, 8, 3, 4, 0, -4 }, /* 0xB7 periodcentered */ - { 501, 8, 3, 4, 0, -3 }, /* 0xB8 cedilla */ - { 504, 8, 3, 2, 0, -5 }, /* 0xB9 onesuperior */ - { 507, 8, 5, 4, 0, -5 }, /* 0xBA ordmasculine */ - { 512, 8, 3, 3, 0, -5 }, /* 0xBB guillemotright */ - { 515, 8, 5, 4, 0, -5 }, /* 0xBC onequarter */ - { 520, 8, 5, 4, 0, -5 }, /* 0xBD onehalf */ - { 525, 8, 5, 4, 0, -5 }, /* 0xBE threequarters */ - { 530, 8, 5, 4, 0, -5 }, /* 0xBF questiondown */ - { 535, 8, 5, 4, 0, -5 }, /* 0xC0 Agrave */ - { 540, 8, 5, 4, 0, -5 }, /* 0xC1 Aacute */ - { 545, 8, 5, 4, 0, -5 }, /* 0xC2 Acircumflex */ - { 550, 8, 5, 4, 0, -5 }, /* 0xC3 Atilde */ - { 555, 8, 5, 4, 0, -5 }, /* 0xC4 Adieresis */ - { 560, 8, 5, 4, 0, -5 }, /* 0xC5 Aring */ - { 565, 8, 5, 4, 0, -5 }, /* 0xC6 AE */ - { 570, 8, 6, 4, 0, -5 }, /* 0xC7 Ccedilla */ - { 576, 8, 5, 4, 0, -5 }, /* 0xC8 Egrave */ - { 581, 8, 5, 4, 0, -5 }, /* 0xC9 Eacute */ - { 586, 8, 5, 4, 0, -5 }, /* 0xCA Ecircumflex */ - { 591, 8, 5, 4, 0, -5 }, /* 0xCB Edieresis */ - { 596, 8, 5, 4, 0, -5 }, /* 0xCC Igrave */ - { 601, 8, 5, 4, 0, -5 }, /* 0xCD Iacute */ - { 606, 8, 5, 4, 0, -5 }, /* 0xCE Icircumflex */ - { 611, 8, 5, 4, 0, -5 }, /* 0xCF Idieresis */ - { 616, 8, 5, 4, 0, -5 }, /* 0xD0 Eth */ - { 621, 8, 5, 4, 0, -5 }, /* 0xD1 Ntilde */ - { 626, 8, 5, 4, 0, -5 }, /* 0xD2 Ograve */ - { 631, 8, 5, 4, 0, -5 }, /* 0xD3 Oacute */ - { 636, 8, 5, 4, 0, -5 }, /* 0xD4 Ocircumflex */ - { 641, 8, 5, 4, 0, -5 }, /* 0xD5 Otilde */ - { 646, 8, 5, 4, 0, -5 }, /* 0xD6 Odieresis */ - { 651, 8, 3, 4, 0, -4 }, /* 0xD7 multiply */ - { 654, 8, 5, 4, 0, -5 }, /* 0xD8 Oslash */ - { 659, 8, 5, 4, 0, -5 }, /* 0xD9 Ugrave */ - { 664, 8, 5, 4, 0, -5 }, /* 0xDA Uacute */ - { 669, 8, 5, 4, 0, -5 }, /* 0xDB Ucircumflex */ - { 674, 8, 5, 4, 0, -5 }, /* 0xDC Udieresis */ - { 679, 8, 5, 4, 0, -5 }, /* 0xDD Yacute */ - { 684, 8, 5, 4, 0, -5 }, /* 0xDE Thorn */ - { 689, 8, 6, 4, 0, -5 }, /* 0xDF germandbls */ - { 695, 8, 5, 4, 0, -5 }, /* 0xE0 agrave */ - { 700, 8, 5, 4, 0, -5 }, /* 0xE1 aacute */ - { 705, 8, 5, 4, 0, -5 }, /* 0xE2 acircumflex */ - { 710, 8, 5, 4, 0, -5 }, /* 0xE3 atilde */ - { 715, 8, 5, 4, 0, -5 }, /* 0xE4 adieresis */ - { 720, 8, 5, 4, 0, -5 }, /* 0xE5 aring */ - { 725, 8, 4, 4, 0, -4 }, /* 0xE6 ae */ - { 729, 8, 5, 4, 0, -4 }, /* 0xE7 ccedilla */ - { 734, 8, 5, 4, 0, -5 }, /* 0xE8 egrave */ - { 739, 8, 5, 4, 0, -5 }, /* 0xE9 eacute */ - { 744, 8, 5, 4, 0, -5 }, /* 0xEA ecircumflex */ - { 749, 8, 5, 4, 0, -5 }, /* 0xEB edieresis */ - { 754, 8, 5, 3, 0, -5 }, /* 0xEC igrave */ - { 759, 8, 5, 3, 0, -5 }, /* 0xED iacute */ - { 764, 8, 5, 4, 0, -5 }, /* 0xEE icircumflex */ - { 769, 8, 5, 4, 0, -5 }, /* 0xEF idieresis */ - { 774, 8, 5, 4, 0, -5 }, /* 0xF0 eth */ - { 779, 8, 5, 4, 0, -5 }, /* 0xF1 ntilde */ - { 784, 8, 5, 4, 0, -5 }, /* 0xF2 ograve */ - { 789, 8, 5, 4, 0, -5 }, /* 0xF3 oacute */ - { 794, 8, 5, 4, 0, -5 }, /* 0xF4 ocircumflex */ - { 799, 8, 5, 4, 0, -5 }, /* 0xF5 otilde */ - { 804, 8, 5, 4, 0, -5 }, /* 0xF6 odieresis */ - { 809, 8, 5, 4, 0, -5 }, /* 0xF7 divide */ - { 814, 8, 4, 4, 0, -4 }, /* 0xF8 oslash */ - { 818, 8, 5, 4, 0, -5 }, /* 0xF9 ugrave */ - { 823, 8, 5, 4, 0, -5 }, /* 0xFA uacute */ - { 828, 8, 5, 4, 0, -5 }, /* 0xFB ucircumflex */ - { 833, 8, 5, 4, 0, -5 }, /* 0xFC udieresis */ - { 838, 8, 6, 4, 0, -5 }, /* 0xFD yacute */ - { 844, 8, 5, 4, 0, -4 }, /* 0xFE thorn */ - { 849, 8, 6, 4, 0, -5 }, /* 0xFF ydieresis */ - { 855, 8, 1, 2, 0, -1 }, /* 0x11D gcircumflex */ - { 856, 8, 5, 4, 0, -5 }, /* 0x152 OE */ - { 861, 8, 4, 4, 0, -4 }, /* 0x153 oe */ - { 865, 8, 5, 4, 0, -5 }, /* 0x160 Scaron */ - { 870, 8, 5, 4, 0, -5 }, /* 0x161 scaron */ - { 875, 8, 5, 4, 0, -5 }, /* 0x178 Ydieresis */ - { 880, 8, 5, 4, 0, -5 }, /* 0x17D Zcaron */ - { 885, 8, 5, 4, 0, -5 }, /* 0x17E zcaron */ - { 890, 8, 1, 2, 0, -1 }, /* 0xEA4 uni0EA4 */ - { 891, 8, 1, 2, 0, -1 }, /* 0x13A0 uni13A0 */ - { 892, 8, 1, 2, 0, -3 }, /* 0x2022 bullet */ - { 893, 8, 1, 4, 0, -1 }, /* 0x2026 ellipsis */ - { 894, 8, 5, 4, 0, -5 }, /* 0x20AC Euro */ - { 899, 8, 5, 4, 0, -5 }, /* 0xFFFD uniFFFD */ -#endif /* (TOMTHUMB_USE_EXTENDED) */ -}; +extern const GFXfont TomThumb; -const GFXfont TomThumb PROGMEM = { - (uint8_t *)TomThumbBitmaps, - (GFXglyph *)TomThumbGlyphs, - 0x20, 0x7E, 6 }; +#endif //_TOMTHUMB_H__ From ad2bfeabbe524f16099b381d59d2ea7bfdab840a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 20 Oct 2019 01:11:56 +0800 Subject: [PATCH 087/469] dynamic menu size via templates --- avr/cores/megacommand/MCL/GridPages.cpp | 35 +++++++++++- avr/cores/megacommand/MCL/GridPages.h | 32 +++-------- avr/cores/megacommand/MCL/MCLMenus.cpp | 44 ++++++--------- avr/cores/megacommand/MCL/MCLMenus.h | 16 +++--- avr/cores/megacommand/MCL/Menu.cpp | 66 ++++++++++------------ avr/cores/megacommand/MCL/Menu.h | 71 ++++++++++++++---------- avr/cores/megacommand/MCL/MenuPage.cpp | 74 ++++++++++++------------- avr/cores/megacommand/MCL/MenuPage.h | 32 ++++++++--- avr/cores/megacommand/MCL/SeqPages.cpp | 2 +- avr/cores/megacommand/MCL/SeqPages.h | 5 +- 10 files changed, 201 insertions(+), 176 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPages.cpp b/avr/cores/megacommand/MCL/GridPages.cpp index c4bec5cf0..d255e7365 100644 --- a/avr/cores/megacommand/MCL/GridPages.cpp +++ b/avr/cores/megacommand/MCL/GridPages.cpp @@ -1,6 +1,32 @@ #include "GridPages.h" #include "MCL.h" +const menu_t< + #ifndef OLED_DISPLAY + 8 + #else + 7 + #endif + > +slot_menu_layout PROGMEM = { + "Slot", + { + {"CHAIN:", 1, 4, 3, (uint8_t *) &mcl_cfg.chain_mode, (Page*) NULL, NULL, {{1, "AUT"},{2,"MAN"},{3,"RND"}}}, + {"LOOP: ", 0, 64, 0, (uint8_t *) &slot.chain.loops, (Page*) NULL, NULL, {}}, + {"ROW: ", 0, 128, 0, (uint8_t*) &slot.chain.row, (Page*) NULL, NULL, {}}, + #ifndef OLED_DISPLAY + {"APPLY:", 1, 21, 1, (uint8_t *) &grid_page.slot_apply, (Page*) NULL, NULL, {{0," "}}}, + #endif + {"CLEAR:", 0, 2, 2, (uint8_t *) &grid_page.slot_clear, (Page*) NULL, NULL, {{0,"--"},{1, "YES"}}}, + {"COPY:", 0, 2, 2, (uint8_t *) &grid_page.slot_copy, (Page*) NULL, NULL, {{0,"--"},{1, "YES"}}}, + {"PASTE:", 0, 2, 2, (uint8_t *) &grid_page.slot_paste, (Page*) NULL, NULL,{{0,"--"},{1, "YES"}}}, + {"RENAME", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, &rename_row, {}}, + }, + &apply_slot_changes_cb, + (Page*)NULL, +}; + + #ifdef OLED_DISPLAY MCLEncoder param1(GRID_WIDTH - 1, 0, 1); MCLEncoder param2(GRID_LENGTH - 1, 0 , 1); @@ -28,5 +54,12 @@ GridTrack slot; MCLEncoder grid_slot_param1(0, 7, ENCODER_RES_PAT); MCLEncoder grid_slot_param2(0, 16, ENCODER_RES_PAT); -MenuPage grid_slot_page(&slot_menu_layout, &grid_slot_param1, &grid_slot_param2); +MenuPage< + #ifndef OLED_DISPLAY + 8 + #else + 7 + #endif +> +grid_slot_page(&slot_menu_layout, &grid_slot_param1, &grid_slot_param2); diff --git a/avr/cores/megacommand/MCL/GridPages.h b/avr/cores/megacommand/MCL/GridPages.h index 4a7ec1d3a..cc003247d 100644 --- a/avr/cores/megacommand/MCL/GridPages.h +++ b/avr/cores/megacommand/MCL/GridPages.h @@ -45,31 +45,15 @@ extern GridWritePage grid_write_page; extern GridTrack slot; -const menu_t slot_menu_layout PROGMEM = { - "Slot", - #ifndef OLED_DISPLAY - 8, - #else - 7, - #endif - { - {"CHAIN:", 1, 4, 3, (uint8_t *) &mcl_cfg.chain_mode, (Page*) NULL, (void*)NULL, {{1, "AUT"},{2,"MAN"},{3,"RND"}}}, - {"LOOP: ", 0, 64, 0, (uint8_t *) &slot.chain.loops, (Page*) NULL, (void*)NULL, {}}, - {"ROW: ", 0, 128, 0, (uint8_t*) &slot.chain.row, (Page*) NULL, (void*)NULL, {}}, - #ifndef OLED_DISPLAY - {"APPLY:", 1, 21, 1, (uint8_t *) &grid_page.slot_apply, (Page*) NULL, (void*)NULL, {{0," "}}}, - #endif - {"CLEAR:", 0, 2, 2, (uint8_t *) &grid_page.slot_clear, (Page*) NULL, (void*)NULL, {{0,"--"},{1, "YES"}}}, - {"COPY:", 0, 2, 2, (uint8_t *) &grid_page.slot_copy, (Page*) NULL, (void*)NULL, {{0,"--"},{1, "YES"}}}, - {"PASTE:", 0, 2, 2, (uint8_t *) &grid_page.slot_paste, (Page*) NULL, (void*)NULL,{{0,"--"},{1, "YES"}}}, - {"RENAME", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, (void*)&rename_row, {}}, - }, - (void*)&apply_slot_changes_cb, - (Page*)NULL, -}; - extern MCLEncoder grid_slot_param1; extern MCLEncoder grid_slot_param2; -extern MenuPage grid_slot_page; +extern MenuPage< + #ifndef OLED_DISPLAY + 8 + #else + 7 + #endif +> +grid_slot_page; #endif /* GRIDPAGES_H__ */ diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index 8c7767f27..06bf8d444 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -12,9 +12,8 @@ MCLEncoder config_param5(0, 17, ENCODER_RES_SYS); MCLEncoder config_param6(0, 17, ENCODER_RES_SYS); MCLEncoder config_param7(0, 17, ENCODER_RES_SYS); -const menu_t system_menu_layout PROGMEM = { +const menu_t<7> system_menu_layout PROGMEM = { "GLOBAL", - 7, { {"LOAD PROJECT" ,0, 0, 0, (uint8_t *) NULL, (Page*) &load_proj_page, NULL, {}}, {"NEW PROJECT",0, 0, 0, (uint8_t *) NULL, (Page*) &new_proj_page, NULL, {}}, @@ -27,30 +26,24 @@ const menu_t system_menu_layout PROGMEM = { NULL, }; -const menu_t auxconfig_menu_layout PROGMEM = { +const menu_t<1> auxconfig_menu_layout PROGMEM = { "AUX PAGES", - 1, { {"RAM Page" ,0, 0, 0, (uint8_t *) NULL, (Page*) &ram_config_page, NULL, {}}, }, NULL, }; -const menu_t rampage1_menu_layout PROGMEM = { +const menu_t<1> rampage1_menu_layout PROGMEM = { "RAM PAGE", - 1, { {"LINK:", 0, 2, 2, (uint8_t *) &mcl_cfg.ram_page_mode, (Page*) NULL, NULL, {{0, "MONO"},{1, "STEREO"}}}, }, - NULL, - }; - -const menu_t midiconfig_menu_layout PROGMEM = { +const menu_t<5> midiconfig_menu_layout PROGMEM = { "MIDI", - 5, { {"TURBO 1:", 0, 4, 4, (uint8_t *) &mcl_cfg.uart1_turbo, (Page*) NULL, NULL, {{0, "1x"},{1, "2x"},{2,"4x"},{3,"8x"}}}, {"TURBO 2:", 0, 4, 4, (uint8_t *) &mcl_cfg.uart2_turbo, (Page*) NULL, NULL, {{0, "1x"},{1, "2x"},{2,"4x"},{3,"8x"}}}, @@ -62,12 +55,10 @@ const menu_t midiconfig_menu_layout PROGMEM = { }, (&mclsys_apply_config), - }; -const menu_t mdconfig_menu_layout PROGMEM = { +const menu_t<5> mdconfig_menu_layout PROGMEM = { "MD", - 5, { {"KIT SAVE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_save, (Page*) NULL, NULL, {{0, "OFF"},{1, "AUTO"}}}, {"NORMALIZE:",0, 2, 2, (uint8_t *) &mcl_cfg.auto_normalize, (Page*) NULL, NULL, {{0, "OFF"},{1, "AUTO"}}}, @@ -78,9 +69,8 @@ const menu_t mdconfig_menu_layout PROGMEM = { (&mclsys_apply_config), }; -const menu_t chain_menu_layout PROGMEM = { +const menu_t<3> chain_menu_layout PROGMEM = { "CHAIN", - 3, { {"CHAIN:", 1, 4, 3, (uint8_t *) &mcl_cfg.chain_mode, (Page*) NULL, NULL, {{1, "AUT"},{2,"MAN"},{3,"RND"}}}, {"RAND MIN:", 0, 128, 0, (uint8_t *) &mcl_cfg.chain_rand_min, (Page*) NULL, NULL, {}}, @@ -90,9 +80,8 @@ const menu_t chain_menu_layout PROGMEM = { }; -const menu_t mclconfig_menu_layout PROGMEM = { +const menu_t<2> mclconfig_menu_layout PROGMEM = { "SYSTEM", - 2, { {"DISPLAY:", 0, 2, 2, (uint8_t *) &mcl_cfg.display_mirror, (Page*) NULL, NULL, {{0, "INT"}, {1, "INT+EXT"}}}, {"SCREENSAVER:", 0, 2, 2, (uint8_t *) &mcl_cfg.screen_saver, (Page*) NULL, NULL, {{0, "OFF"}, {1, "ON"}}}, @@ -100,9 +89,8 @@ const menu_t mclconfig_menu_layout PROGMEM = { (&mclsys_apply_config), }; -const menu_t file_menu_layout PROGMEM = { +const menu_t<5> file_menu_layout PROGMEM = { "FILE", - 5, { {"NEW FOLDER", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, {"DELETE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, @@ -113,15 +101,15 @@ const menu_t file_menu_layout PROGMEM = { NULL, }; -MenuPage aux_config_page(&auxconfig_menu_layout, &config_param1, &config_param6); -MenuPage system_page(&system_menu_layout, &options_param1, &options_param2); -MenuPage midi_config_page(&midiconfig_menu_layout, &config_param1, +MenuPage<1> aux_config_page(&auxconfig_menu_layout, &config_param1, &config_param6); +MenuPage<7> system_page(&system_menu_layout, &options_param1, &options_param2); +MenuPage<5> midi_config_page(&midiconfig_menu_layout, &config_param1, &config_param3); -MenuPage md_config_page(&mdconfig_menu_layout, &config_param1, &config_param4); -MenuPage chain_config_page(&chain_menu_layout, &config_param1, &config_param6); -MenuPage mcl_config_page(&mclconfig_menu_layout, &config_param1, +MenuPage<5> md_config_page(&mdconfig_menu_layout, &config_param1, &config_param4); +MenuPage<3> chain_config_page(&chain_menu_layout, &config_param1, &config_param6); +MenuPage<2> mcl_config_page(&mclconfig_menu_layout, &config_param1, &config_param5); -MenuPage ram_config_page(&rampage1_menu_layout, &config_param1, +MenuPage<1> ram_config_page(&rampage1_menu_layout, &config_param1, &config_param7); @@ -131,5 +119,5 @@ MCLEncoder input_encoder2(0, 127, ENCODER_RES_SYS); TextInputPage text_input_page(&input_encoder1, &input_encoder2); MCLEncoder file_menu_encoder(0, 4, ENCODER_RES_PAT); -MenuPage file_menu_page(&file_menu_layout, &config_param1, &file_menu_encoder); +MenuPage<5> file_menu_page(&file_menu_layout, &config_param1, &file_menu_encoder); diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 90b057c8b..e8547ee60 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -25,13 +25,13 @@ extern MCLEncoder config_param5; extern MCLEncoder config_param6; extern MCLEncoder config_param7; -extern MenuPage system_page; -extern MenuPage midi_config_page; -extern MenuPage md_config_page; -extern MenuPage mcl_config_page; -extern MenuPage chain_config_page; -extern MenuPage aux_config_page; -extern MenuPage ram_config_page; +extern MenuPage<7> system_page; +extern MenuPage<5> midi_config_page; +extern MenuPage<5> md_config_page; +extern MenuPage<2> mcl_config_page; +extern MenuPage<3> chain_config_page; +extern MenuPage<1> aux_config_page; +extern MenuPage<1> ram_config_page; extern MCLEncoder input_encoder1; extern MCLEncoder input_encoder2; @@ -39,7 +39,7 @@ extern MCLEncoder input_encoder2; extern TextInputPage text_input_page; extern MCLEncoder file_menu_encoder; -extern MenuPage file_menu_page; +extern MenuPage<5> file_menu_page; #endif /* MCLMENUS_H__ */ diff --git a/avr/cores/megacommand/MCL/Menu.cpp b/avr/cores/megacommand/MCL/Menu.cpp index 8900675fd..d8b7fb558 100644 --- a/avr/cores/megacommand/MCL/Menu.cpp +++ b/avr/cores/megacommand/MCL/Menu.cpp @@ -1,9 +1,7 @@ #include "MCL.h" #include "Menu.h" -void Menu::set_layout(const menu_t *menu_layout) { layout = menu_layout; } - -void Menu::enable_entry(uint8_t entry_index, bool en) { +void MenuBase::enable_entry(uint8_t entry_index, bool en) { auto midx = entry_index / 8; auto bit = entry_index % 8; @@ -14,28 +12,19 @@ void Menu::enable_entry(uint8_t entry_index, bool en) { } } -bool Menu::is_entry_enable(uint8_t entry_index) { +bool MenuBase::is_entry_enable(uint8_t entry_index) { auto midx = entry_index / 8; auto bit = entry_index % 8; return bit_is_set(entry_mask[midx], bit); } -PGM_P Menu::get_name() { return layout->name; } -/* -Page *Menu::get_exit_page_callback() { - return pgm_read_word(&(layout->exit_page_callback)); -} -*/ - -FP Menu::get_row_function(uint8_t item_n) { - menu_item_t *item = get_item(item_n); +FP MenuBase::get_row_function(uint8_t item_n) { + const menu_item_t *item = get_item(item_n); return pgm_read_word(&(item->row_function)); } -FP Menu::get_exit_function() { return pgm_read_word(&(layout->exit_function)); } - -uint8_t Menu::get_number_of_items() { - uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); +uint8_t MenuBase::get_number_of_items() { + uint8_t entry_cnt = get_entry_count(); uint8_t item_cnt = 0; for (auto i = 0; i < entry_cnt; ++i) { if (is_entry_enable(i)) @@ -44,12 +33,12 @@ uint8_t Menu::get_number_of_items() { return item_cnt; } -menu_item_t *Menu::get_item(uint8_t item_n) { - uint8_t entry_cnt = pgm_read_byte(&(layout->number_of_items)); +const menu_item_t *MenuBase::get_item(uint8_t item_n) { + uint8_t entry_cnt = get_entry_count(); for(uint8_t idx = 0; idx < entry_cnt; ++idx) { if(is_entry_enable(idx)) { if (item_n == 0) { - return &layout->items[idx]; + return get_entry_address(idx); }else { --item_n; } @@ -58,42 +47,45 @@ menu_item_t *Menu::get_item(uint8_t item_n) { return nullptr; } -uint8_t Menu::get_item_index(uint8_t item_n) +uint8_t MenuBase::get_item_index(uint8_t item_n) { auto pentry = get_item(item_n); - return pentry - &layout->items[0]; + return pentry - get_entry_address(0); } -PGM_P Menu::get_item_name(uint8_t item_n) { - menu_item_t *item = get_item(item_n); +PGM_P MenuBase::get_item_name(uint8_t item_n) { + auto *item = get_item(item_n); return item->name; } -Page *Menu::get_page_callback(uint8_t item_n) { - menu_item_t *item = get_item(item_n); + +Page *MenuBase::get_page_callback(uint8_t item_n) { + auto *item = get_item(item_n); return pgm_read_word(&(item->page_callback)); } -uint8_t *Menu::get_dest_variable(uint8_t item_n) { - menu_item_t *item = get_item(item_n); +uint8_t *MenuBase::get_dest_variable(uint8_t item_n) { + auto *item = get_item(item_n); return pgm_read_word(&(item->destination_var)); } -uint8_t Menu::get_option_range(uint8_t item_n) { - menu_item_t *item = get_item(item_n); + +uint8_t MenuBase::get_option_range(uint8_t item_n) { + auto *item = get_item(item_n); return pgm_read_byte(&(item->range)); } -uint8_t Menu::get_option_min(uint8_t item_n) { - menu_item_t *item = get_item(item_n); + +uint8_t MenuBase::get_option_min(uint8_t item_n) { + auto *item = get_item(item_n); return pgm_read_byte(&(item->min)); } -uint8_t Menu::get_number_of_options(uint8_t item_n) { - menu_item_t *item = get_item(item_n); +uint8_t MenuBase::get_number_of_options(uint8_t item_n) { + auto *item = get_item(item_n); return pgm_read_byte(&(item->number_of_options)); } -PGM_P Menu::get_option_name(uint8_t item_n, uint8_t option_n) { - menu_item_t *item = get_item(item_n); - menu_option_t *option; +PGM_P MenuBase::get_option_name(uint8_t item_n, uint8_t option_n) { + auto *item = get_item(item_n); + const menu_option_t *option; uint8_t num_of_options = get_number_of_options(item_n); for (uint8_t a = 0; a < num_of_options; a++) { option = &(item->options[a]); diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 51477930f..527e5f88e 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -4,15 +4,15 @@ #define MAX_MENU_ITEMS 16 #define MAX_MENU_OPTIONS 16 -typedef void(*FP)(); +typedef void (*FP)(); -typedef struct menu_option_s { +struct menu_option_t { uint8_t pos; - char name[17]; -} menu_option_t; + char name[12]; +}; -typedef struct menu_item_s { - char name[17]; +struct menu_item_t { + char name[14]; uint8_t min; uint8_t range; uint8_t number_of_options; @@ -20,46 +20,59 @@ typedef struct menu_item_s { Page *page_callback; void (*row_function)(); menu_option_t options[MAX_MENU_OPTIONS]; -} menu_item_t; +}; -typedef struct menu_s { +template struct menu_t { char name[11]; - uint8_t number_of_items; - menu_item_t items[MAX_MENU_ITEMS]; + menu_item_t items[N]; void (*exit_function)(); Page *exit_page_callback; -} menu_t; - -class Menu { +}; +class MenuBase { public: - const menu_t *layout; - uint8_t values[MAX_MENU_ITEMS]; uint8_t entry_mask[2]; - Menu(){ - entry_mask[0] = entry_mask[1] = 0xFF; - } + MenuBase() { entry_mask[0] = entry_mask[1] = 0xFF; } - void set_layout(const menu_t *menu_layout); void enable_entry(uint8_t entry_index, bool en); bool is_entry_enable(uint8_t entry_index); - PGM_P get_name(); - - uint8_t get_number_of_items(); - menu_item_t *get_item(uint8_t item_n); - uint8_t get_item_index(uint8_t item_n); - PGM_P get_item_name(uint8_t item_n); - Page *get_page_callback(uint8_t item_n); - // Page *get_exit_page_callback(); - FP get_exit_function(); - FP get_row_function(uint8_t item_n); uint8_t *get_dest_variable(uint8_t item_n); uint8_t get_option_min(uint8_t item_n); uint8_t get_option_range(uint8_t item_n); uint8_t get_number_of_options(uint8_t item_n); + Page *get_page_callback(uint8_t item_n); + uint8_t get_number_of_items(); + const menu_item_t *get_item(uint8_t item_n); + PGM_P get_item_name(uint8_t item_n); + uint8_t get_item_index(uint8_t item_n); PGM_P get_option_name(uint8_t item_n, uint8_t option_n); + FP get_row_function(uint8_t item_n); + + virtual PGM_P get_name() = 0; + virtual FP get_exit_function() = 0; + +protected: + virtual const menu_item_t *get_entry_address(uint8_t) = 0; + virtual uint8_t get_entry_count() = 0; +}; + +template class Menu : public MenuBase { + +public: + Menu() : MenuBase(){}; + const menu_t *layout; + + void set_layout(const menu_t *menu_layout) { + layout = menu_layout; + } + virtual PGM_P get_name() { return layout->name; } + virtual FP get_exit_function() { + return pgm_read_word(&(layout->exit_function)); + } + virtual const menu_item_t *get_entry_address(uint8_t i) { return layout->items + i; } + virtual uint8_t get_entry_count() { return N; }; }; #endif /* MENU_H__ */ diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index a4b5ef691..9cf2952ef 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -1,34 +1,34 @@ #include "MenuPage.h" #include "MCL.h" -void MenuPage::init() { - ((MCLEncoder *)encoders[1])->max = menu.get_number_of_items() - 1; +void MenuPageBase::init() { + ((MCLEncoder *)encoders[1])->max = get_menu()->get_number_of_items() - 1; if (((MCLEncoder *)encoders[1])->cur > ((MCLEncoder *)encoders[1])->max) { ((MCLEncoder *)encoders[1])->cur = 0; } ((MCLEncoder *)encoders[0])->max = - menu.get_option_range(encoders[1]->cur) - 1; - ((MCLEncoder *)encoders[0])->min = menu.get_option_min(encoders[1]->cur); + get_menu()->get_option_range(encoders[1]->cur) - 1; + ((MCLEncoder *)encoders[0])->min = get_menu()->get_option_min(encoders[1]->cur); - uint8_t *dest_var = menu.get_dest_variable(encoders[1]->cur); + uint8_t *dest_var = get_menu()->get_dest_variable(encoders[1]->cur); if (dest_var != NULL) { encoders[0]->setValue(*dest_var); } encoders[0]->old = encoders[0]->cur; encoders[1]->old = encoders[1]->cur; } -void MenuPage::setup() { +void MenuPageBase::setup() { #ifdef OLED_DISPLAY classic_display = false; #endif } -void MenuPage::loop() { +void MenuPageBase::loop() { if (encoders[1]->hasChanged()) { ((MCLEncoder *)encoders[0])->max = - menu.get_option_range(encoders[1]->cur) - 1; - ((MCLEncoder *)encoders[0])->min = menu.get_option_min(encoders[1]->cur); + get_menu()->get_option_range(encoders[1]->cur) - 1; + ((MCLEncoder *)encoders[0])->min = get_menu()->get_option_min(encoders[1]->cur); uint8_t diff = encoders[1]->cur - encoders[1]->old; @@ -43,7 +43,7 @@ void MenuPage::loop() { #endif // MD.assignMachine(0, encoders[1]->cur); cur_row = new_val; - uint8_t *dest_var = menu.get_dest_variable(encoders[1]->cur); + uint8_t *dest_var = get_menu()->get_dest_variable(encoders[1]->cur); if (dest_var != NULL) { encoders[0]->setValue(*dest_var); } else { @@ -51,41 +51,41 @@ void MenuPage::loop() { } } if (encoders[0]->hasChanged()) { - uint8_t *dest_var = menu.get_dest_variable(encoders[1]->cur); + uint8_t *dest_var = get_menu()->get_dest_variable(encoders[1]->cur); if (dest_var != NULL) { *dest_var = encoders[0]->cur; } } } -void MenuPage::draw_scrollbar(uint8_t x_offset) { +void MenuPageBase::draw_scrollbar(uint8_t x_offset) { #ifdef OLED_DISPLAY - mcl_gui.draw_vertical_scrollbar(x_offset, menu.get_number_of_items(), + mcl_gui.draw_vertical_scrollbar(x_offset, get_menu()->get_number_of_items(), MAX_VISIBLE_ROWS, encoders[1]->cur - cur_row); #endif } -void MenuPage::draw_item(uint8_t item_n, uint8_t row) { +void MenuPageBase::draw_item(uint8_t item_n, uint8_t row) { #ifdef OLED_DISPLAY char str[17]; - PGM_P pgp = menu.get_item_name(item_n); + PGM_P pgp = get_menu()->get_item_name(item_n); if (pgp != NULL) { m_strncpy_p(str, pgp, 16); oled_display.print(str); } - uint8_t number_of_items = menu.get_number_of_items(); + uint8_t number_of_items = get_menu()->get_number_of_items(); if (item_n > number_of_items - 1) { return; } - uint8_t number_of_options = menu.get_number_of_options(item_n); - if (menu.get_option_range(item_n) > 0) { + uint8_t number_of_options = get_menu()->get_number_of_options(item_n); + if (get_menu()->get_option_range(item_n) > 0) { oled_display.print(" "); - pgp = menu.get_option_name(item_n, *(menu.get_dest_variable(item_n))); + pgp = get_menu()->get_option_name(item_n, *(get_menu()->get_dest_variable(item_n))); if (pgp == NULL) { - oled_display.println(*(menu.get_dest_variable(item_n))); + oled_display.println(*(get_menu()->get_dest_variable(item_n))); } else { m_strncpy_p(str, pgp, 11); oled_display.println(str); @@ -94,10 +94,10 @@ void MenuPage::draw_item(uint8_t item_n, uint8_t row) { #endif } -void MenuPage::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width) { +void MenuPageBase::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width) { #ifdef OLED_DISPLAY oled_display.setCursor(x_offset, y_offset); - uint8_t number_of_items = menu.get_number_of_items(); + uint8_t number_of_items = get_menu()->get_number_of_items(); uint8_t max_items; if (number_of_items > MAX_VISIBLE_ROWS) { max_items = MAX_VISIBLE_ROWS; @@ -123,15 +123,15 @@ void MenuPage::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width) { #endif } -void MenuPage::display() { +void MenuPageBase::display() { char str[17]; PGM_P pgp; - pgp = menu.get_name(); + pgp = get_menu()->get_name(); m_strncpy_p(str, pgp, 16); - uint8_t number_of_items = menu.get_number_of_items(); + uint8_t number_of_items = get_menu()->get_number_of_items(); #ifdef OLED_DISPLAY uint8_t x_offset = 43; oled_display.clearDisplay(); @@ -154,7 +154,7 @@ void MenuPage::display() { GUI.put_string_at(1, str); GUI.put_string_at(m_strlen(str), "]"); - pgp = menu.get_item_name(cur_row); + pgp = get_menu()->get_item_name(cur_row); GUI.setLine(GUI.LINE2); if (pgp != NULL) { @@ -166,12 +166,12 @@ void MenuPage::display() { return true; } - uint8_t number_of_options = menu.get_number_of_options(cur_row); - if (menu.get_option_range(cur_row) > 0) { + uint8_t number_of_options = get_menu()->get_number_of_options(cur_row); + if (get_menu()->get_option_range(cur_row) > 0) { - pgp = menu.get_option_name(cur_row, *(menu.get_dest_variable(cur_row))); + pgp = get_menu()->get_option_name(cur_row, *(get_menu()->get_dest_variable(cur_row))); if (pgp == NULL) { - GUI.put_value_at(10, *(menu.get_dest_variable(cur_row))); + GUI.put_value_at(10, *(get_menu()->get_dest_variable(cur_row))); } else { m_strncpy_p(str, pgp, 11); GUI.put_string_at(10, str); @@ -181,10 +181,10 @@ void MenuPage::display() { #endif } -bool MenuPage::enter() { +bool MenuPageBase::enter() { DEBUG_PRINT_FN(); - void (*row_func)() = menu.get_row_function(encoders[1]->cur); - Page *page_callback = menu.get_page_callback(encoders[1]->cur); + void (*row_func)() = get_menu()->get_row_function(encoders[1]->cur); + Page *page_callback = get_menu()->get_page_callback(encoders[1]->cur); if (page_callback != NULL) { DEBUG_PRINTLN("setting page"); DEBUG_PRINTLN((uint16_t)page_callback); @@ -197,9 +197,9 @@ bool MenuPage::enter() { } } -bool MenuPage::exit() { - // Page *exit_page_callback = menu.get_exit_page_callback(); - void (*exit_func)() = menu.get_exit_function(); +bool MenuPageBase::exit() { + // Page *exit_page_callback = get_menu()->get_exit_page_callback(); + void (*exit_func)() = get_menu()->get_exit_function(); if (exit_func != NULL) { (*exit_func)(); // @@ -209,7 +209,7 @@ bool MenuPage::exit() { //} } -bool MenuPage::handleEvent(gui_event_t *event) { +bool MenuPageBase::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { return true; diff --git a/avr/cores/megacommand/MCL/MenuPage.h b/avr/cores/megacommand/MCL/MenuPage.h index d29426720..143cf9661 100644 --- a/avr/cores/megacommand/MCL/MenuPage.h +++ b/avr/cores/megacommand/MCL/MenuPage.h @@ -13,21 +13,20 @@ #endif #define MENU_WIDTH 78 -class MenuPage : public LightPage { + +class MenuPageBase : public LightPage { public: uint8_t cur_col = 0; uint8_t cur_row = 0; - Menu menu; + MenuPageBase(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, + Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) {} - MenuPage(const menu_t *layout, Encoder *e1 = NULL, Encoder *e2 = NULL, - Encoder *e3 = NULL, Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4) { - menu.set_layout(layout); - } void draw_scrollbar(uint8_t x_offset); void draw_item(uint8_t item_n, uint8_t row); - void draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width = MENU_WIDTH); + void draw_menu(uint8_t x_offset, uint8_t y_offset, + uint8_t width = MENU_WIDTH); void loop(); void display(); void setup(); @@ -35,6 +34,23 @@ class MenuPage : public LightPage { bool enter(); bool exit(); virtual bool handleEvent(gui_event_t *event); + +protected: + virtual MenuBase *get_menu() = 0; +}; + +template class MenuPage : public MenuPageBase { +public: + Menu menu; + + MenuPage(const menu_t *layout, Encoder *e1 = NULL, Encoder *e2 = NULL, + Encoder *e3 = NULL, Encoder *e4 = NULL) + : MenuPageBase(e1, e2, e3, e4) { + menu.set_layout(layout); + } + +protected: + virtual MenuBase* get_menu() { return &menu; } }; #endif /* MENUPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index a02575c46..faeb5a3fe 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -27,7 +27,7 @@ SeqExtStepPage seq_extstep_page(&seq_param1, &seq_param2, &seq_param3, SeqPtcPage seq_ptc_page(&ptc_param_oct, &ptc_param_finetune, &ptc_param_len, &ptc_param_scale); MCLEncoder track_menu_param1(0, 8, ENCODER_RES_PAT); MCLEncoder track_menu_param2(0, 8, ENCODER_RES_PAT); -MenuPage track_menu_page(&track_menu_layout, &track_menu_param1, &track_menu_param2); +MenuPage<5> track_menu_page(&track_menu_layout, &track_menu_param1, &track_menu_param2); //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 5b7b92496..183a1168b 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -51,14 +51,13 @@ extern SeqPtcPage seq_ptc_page; extern MCLEncoder track_menu_param1; extern MCLEncoder track_menu_param2; -extern MenuPage track_menu_page; +extern MenuPage<5> track_menu_page; extern void mcl_save_sound(); extern void mcl_load_sound(); -const menu_t track_menu_layout PROGMEM = { +const menu_t<5> track_menu_layout PROGMEM = { "TRACk", - 5, { {"LENGTH:", 0, 64, 0, (uint8_t *) &SeqPage::length, (Page*) NULL, (void*)NULL, {}}, {"MULTI:", 1, 2, 2, (uint8_t *) &SeqPage::resolution, (Page*) NULL, (void*)NULL, {{1, "1x"},{2, "2x"}}}, From 2f7c0457616a1418b8f4af8b734eef3b5d3c8e70 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 20 Oct 2019 01:26:11 +0800 Subject: [PATCH 088/469] MenuPage: specify rows in draw_menu --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 1 - avr/cores/megacommand/MCL/MCLMenus.cpp | 2 +- avr/cores/megacommand/MCL/Menu.h | 1 + avr/cores/megacommand/MCL/MenuPage.cpp | 6 +++--- avr/cores/megacommand/MCL/MenuPage.h | 3 ++- avr/cores/megacommand/MCL/SeqPages.cpp | 13 +++++++++++++ avr/cores/megacommand/MCL/SeqPages.h | 16 ---------------- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index fd1183a11..5deba4b9e 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -265,7 +265,6 @@ void FileBrowserPage::_handle_filemenu() { char buf2[32] = {'\0'}; switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { - //switch (file_menu_encoder.cur) { case 0: // new folder create_folder(); break; diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index 06bf8d444..d333113e0 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -92,7 +92,7 @@ const menu_t<2> mclconfig_menu_layout PROGMEM = { const menu_t<5> file_menu_layout PROGMEM = { "FILE", { - {"NEW FOLDER", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"NEW DIR.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, {"DELETE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, {"RENAME", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, {"OVERWRITE", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 527e5f88e..6cb25d1c6 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -58,6 +58,7 @@ class MenuBase { virtual uint8_t get_entry_count() = 0; }; +// TODO raise error if N > MAX_MENU_ITEMS template class Menu : public MenuBase { public: diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 9cf2952ef..594abe65f 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -94,13 +94,13 @@ void MenuPageBase::draw_item(uint8_t item_n, uint8_t row) { #endif } -void MenuPageBase::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width) { +void MenuPageBase::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width, uint8_t rows) { #ifdef OLED_DISPLAY oled_display.setCursor(x_offset, y_offset); uint8_t number_of_items = get_menu()->get_number_of_items(); uint8_t max_items; - if (number_of_items > MAX_VISIBLE_ROWS) { - max_items = MAX_VISIBLE_ROWS; + if (number_of_items > rows) { + max_items = rows; } else { max_items = number_of_items; } diff --git a/avr/cores/megacommand/MCL/MenuPage.h b/avr/cores/megacommand/MCL/MenuPage.h index 143cf9661..12650a2e2 100644 --- a/avr/cores/megacommand/MCL/MenuPage.h +++ b/avr/cores/megacommand/MCL/MenuPage.h @@ -26,7 +26,8 @@ class MenuPageBase : public LightPage { void draw_scrollbar(uint8_t x_offset); void draw_item(uint8_t item_n, uint8_t row); void draw_menu(uint8_t x_offset, uint8_t y_offset, - uint8_t width = MENU_WIDTH); + uint8_t width = MENU_WIDTH, + uint8_t rows = MAX_VISIBLE_ROWS); void loop(); void display(); void setup(); diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index faeb5a3fe..35e539bb8 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -1,5 +1,18 @@ #include "MCL.h" +const menu_t<5> track_menu_layout PROGMEM = { + "TRACk", + { + {"LENGTH:", 0, 64, 0, (uint8_t *) &SeqPage::length, (Page*) NULL, NULL, {}}, + {"MULTI:", 1, 2, 2, (uint8_t *) &SeqPage::resolution, (Page*) NULL, NULL, {{1, "1x"},{2, "2x"}}}, + + {"APPLY:", 0, 1, 2, (uint8_t *) &SeqPage::apply, (Page*) NULL, NULL, {{1, "--"},{1, "ALL"}}}, +// {"LOAD SND:", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, (void*) &mcl_load_sound, {}}, +// {"SAVE SND:", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, (void*) &mcl_save_sound, {}}, + }, + (&mclsys_apply_config), +}; + MCLEncoder seq_param1(0, 3, ENCODER_RES_SEQ); MCLEncoder seq_param2(0, 4, ENCODER_RES_SEQ); MCLEncoder seq_param3(0, 10, ENCODER_RES_SEQ); diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 183a1168b..4f1d32c6b 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -56,20 +56,4 @@ extern MenuPage<5> track_menu_page; extern void mcl_save_sound(); extern void mcl_load_sound(); -const menu_t<5> track_menu_layout PROGMEM = { - "TRACk", - { - {"LENGTH:", 0, 64, 0, (uint8_t *) &SeqPage::length, (Page*) NULL, (void*)NULL, {}}, - {"MULTI:", 1, 2, 2, (uint8_t *) &SeqPage::resolution, (Page*) NULL, (void*)NULL, {{1, "1x"},{2, "2x"}}}, - - {"APPLY:", 0, 1, 2, (uint8_t *) &SeqPage::apply, (Page*) NULL, (void*)NULL, {{1, "--"},{1, "ALL"}}}, -// {"LOAD SND:", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, (void*) &mcl_load_sound, {}}, -// {"SAVE SND:", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, (void*) &mcl_save_sound, {}}, - }, - - (void*)(&mclsys_apply_config), - -}; - - #endif /* SEQPAGES_H__ */ From 4a69b9539b17943b818bfb7135f171d15588af6a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 20 Oct 2019 01:28:55 +0800 Subject: [PATCH 089/469] cleanup --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 5deba4b9e..a286db6fe 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -276,7 +276,13 @@ void FileBrowserPage::_handle_filemenu() { on_delete(buf1); } break; - case 2: // overwrite + case 2: // rename + strcat(buf2, buf1); + if (mcl_gui.wait_for_input(buf2, "RENAME TO:", 16)) { + on_rename(buf1, buf2); + } + break; + case 3: // overwrite strcat(buf2, "Overwrite "); strcat(buf2, buf1); strcat(buf2, "?"); @@ -285,12 +291,6 @@ void FileBrowserPage::_handle_filemenu() { on_select(buf1); } break; - case 3: - strcat(buf2, buf1); - if (mcl_gui.wait_for_input(buf2, "RENAME TO:", 16)) { - on_rename(buf1, buf2); - } - break; } } From ea087373e17cbcab06a516cd569a45148fe3a862 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 20 Oct 2019 01:46:39 +0800 Subject: [PATCH 090/469] adding default implementation for file ops --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 24 ++++++++++++++++--- avr/cores/megacommand/MCL/FileBrowserPage.h | 4 ++-- avr/cores/megacommand/MCL/SDDrivePage.cpp | 4 ---- avr/cores/megacommand/MCL/SDDrivePage.h | 1 - 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index a286db6fe..6d632e3b4 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -101,8 +101,9 @@ void FileBrowserPage::init() { void FileBrowserPage::display() { #ifdef OLED_DISPLAY if (filemenu_active) { - oled_display.fillRect(0, 8, 38, 24, BLACK); - file_menu_page.draw_menu(0, 14, 38); + oled_display.fillRect(0, 3, 42, 28, BLACK); + oled_display.drawRect(1, 4, 40, 26, WHITE); + file_menu_page.draw_menu(3, 12, 39, 3); oled_display.display(); return; } @@ -294,6 +295,22 @@ void FileBrowserPage::_handle_filemenu() { } } +void FileBrowserPage::on_delete(const char *entry) { + if (SD.remove(entry)) { + gfx.alert("SUCCESS", "File removed."); + } else { + gfx.alert("ERROR", "File not removed."); + } +} + +void FileBrowserPage::on_rename(const char* from, const char* to) { + if (SD.rename(from, to)) { + gfx.alert("SUCCESS", "File renamed."); + } else { + gfx.alert("ERROR", "File not renamed."); + } +} + bool FileBrowserPage::handleEvent(gui_event_t *event) { DEBUG_PRINT_FN(); @@ -304,6 +321,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON3)) { filemenu_active = true; + file_menu_encoder.cur = file_menu_encoder.old = 0; encoders[0] = &config_param1; encoders[1] = &file_menu_encoder; file_menu_page.init(); @@ -311,10 +329,10 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { - filemenu_active = false; encoders[0] = param1; encoders[1] = param2; _handle_filemenu(); + init(); return false; } diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 7b14e0124..7f4389386 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -65,8 +65,8 @@ class FileBrowserPage : public LightPage { virtual void on_new() {} virtual void on_select(const char *) {} - virtual void on_delete(const char *) {} - virtual void on_rename(const char *from, const char *to) {} + virtual void on_delete(const char *); + virtual void on_rename(const char *from, const char *to); // on cancel, the page will be popped, // and there's a last chance to clean up. virtual void on_cancel() { GUI.popPage(); } diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index d55e083ff..bee4076e7 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -148,10 +148,6 @@ void SDDrivePage::on_new() { init(); } -void SDDrivePage::on_delete(const char *file) { - gfx.alert("SDDrivePage::on_delete", file); -} - void SDDrivePage::on_select(const char *__) { load_snapshot(); } MCLEncoder sddrive_param1(1, 10, ENCODER_RES_SYS); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.h b/avr/cores/megacommand/MCL/SDDrivePage.h index 4e3736c59..0c17bc008 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.h +++ b/avr/cores/megacommand/MCL/SDDrivePage.h @@ -18,7 +18,6 @@ class SDDrivePage : public FileBrowserPage { void load_snapshot(); virtual void on_select(const char*); virtual void on_new(); - virtual void on_delete(const char*); }; extern SDDrivePage sddrive_page; From 29c979971881a46cc1ac5f7f5f68b38b2498c5f3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 14:01:44 +1100 Subject: [PATCH 091/469] First round of forcing inline --- avr/cores/megacommand/CommonTools/helpers.h | 4 +- avr/cores/megacommand/Core.h | 7 +++ avr/cores/megacommand/MCL/ExtSeqTrack.h | 11 +++-- avr/cores/megacommand/MCL/MDSeqTrack.h | 8 ++-- avr/cores/megacommand/MD/MD.h | 4 +- avr/cores/megacommand/Midi/MidiUartParent.hh | 50 ++++++++++---------- avr/cores/megacommand/memory.h | 2 +- 7 files changed, 47 insertions(+), 39 deletions(-) diff --git a/avr/cores/megacommand/CommonTools/helpers.h b/avr/cores/megacommand/CommonTools/helpers.h index cf640da2b..84c25f413 100644 --- a/avr/cores/megacommand/CommonTools/helpers.h +++ b/avr/cores/megacommand/CommonTools/helpers.h @@ -27,7 +27,7 @@ #define HELPERS_H__ #include - +#include #include #ifdef __cplusplus @@ -222,7 +222,7 @@ void m_toupper(char* str); extern uint16_t read_clock(void); extern uint16_t read_slowclock(void); -uint16_t clock_diff(uint16_t old_clock, uint16_t new_clock); +ALWAYS_INLINE() uint16_t clock_diff(uint16_t old_clock, uint16_t new_clock); #ifdef HOST_MIDIDUINO #else diff --git a/avr/cores/megacommand/Core.h b/avr/cores/megacommand/Core.h index fd1f3a207..81504775a 100644 --- a/avr/cores/megacommand/Core.h +++ b/avr/cores/megacommand/Core.h @@ -3,4 +3,11 @@ #define MEGACOMMAND +#ifdef MEGACOMMAND + // #define ALWAYS_INLINE() + #define ALWAYS_INLINE() __attribute__((always_inline)) +#else + #define ALWAYS_INLINE() +#endif + #endif /* CORE_H__ */ diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.h b/avr/cores/megacommand/MCL/ExtSeqTrack.h index 4abf6c3e8..74759802b 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.h +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.h @@ -40,11 +40,12 @@ class ExtSeqTrack : public ExtSeqTrackData { uint8_t iterations; bool mute_until_start = false; - void seq(); - void set_step(uint8_t step, uint8_t note_num, uint8_t velocity); - void note_on(uint8_t note); - void note_off(uint8_t note); - void noteon_conditional(uint8_t condition, uint8_t note); + ALWAYS_INLINE() void seq(); + ALWAYS_INLINE() void set_step(uint8_t step, uint8_t note_num, uint8_t velocity); + ALWAYS_INLINE() void note_on(uint8_t note); + ALWAYS_INLINE() void note_off(uint8_t note); + ALWAYS_INLINE() void noteon_conditional(uint8_t condition, uint8_t note); + void record_ext_track_noteon(uint8_t note_num, uint8_t velocity); void record_ext_track_noteoff(uint8_t note_num, uint8_t velocity); diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.h b/avr/cores/megacommand/MCL/MDSeqTrack.h index cb2cbe846..03a54e34e 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.h +++ b/avr/cores/megacommand/MCL/MDSeqTrack.h @@ -35,13 +35,13 @@ class MDSeqTrack : public MDSeqTrackData { bool mute_until_start = false; uint8_t mute_state = SEQ_MUTE_OFF; - void seq(); void mute() { mute_state = SEQ_MUTE_ON; } void unmute() { mute_state = SEQ_MUTE_OFF; } - inline void send_trig(); - inline void trig_conditional(uint8_t condition); - inline void send_parameter_locks(uint8_t step); + ALWAYS_INLINE() void seq(); + ALWAYS_INLINE() void send_trig(); + ALWAYS_INLINE() void trig_conditional(uint8_t condition); + ALWAYS_INLINE() void send_parameter_locks(uint8_t step); void set_track_pitch(uint8_t step, uint8_t pitch); void set_track_step(uint8_t step, uint8_t utiming, uint8_t note_num, diff --git a/avr/cores/megacommand/MD/MD.h b/avr/cores/megacommand/MD/MD.h index a4704238c..2b0bc8b60 100644 --- a/avr/cores/megacommand/MD/MD.h +++ b/avr/cores/megacommand/MD/MD.h @@ -202,14 +202,14 @@ class MDClass { * * track goes from 0 to 15, velocity from 0 to 127. **/ - void triggerTrack(uint8_t track, uint8_t velocity); + ALWAYS_INLINE() void triggerTrack(uint8_t track, uint8_t velocity); /** * Set the parameter param (0 to 23, or 32 for mute, and 33 for * LEVEL) of the given track (from 0 to 15) to value. * * Uses the channel settings out of the global settings. **/ - void setTrackParam(uint8_t track, uint8_t param, uint8_t value); + ALWAYS_INLINE() void setTrackParam(uint8_t track, uint8_t param, uint8_t value); void setSampleName(uint8_t slot, char *name); /** Send the given sysex buffer to the MachineDrum. **/ diff --git a/avr/cores/megacommand/Midi/MidiUartParent.hh b/avr/cores/megacommand/Midi/MidiUartParent.hh index d7308529b..66d957fc0 100644 --- a/avr/cores/megacommand/Midi/MidiUartParent.hh +++ b/avr/cores/megacommand/Midi/MidiUartParent.hh @@ -7,7 +7,7 @@ #include "Vector.hh" #include #include "MidiID.hh" - +#include "Core.h" //#define MIDI_VALIDATE //#define MIDI_RUNNING_STATUS @@ -64,7 +64,7 @@ public: } } - inline void tickActiveSense() { + ALWAYS_INLINE() void tickActiveSense() { if (recvActiveSenseTimer < 65535) { recvActiveSenseTimer++; } @@ -91,15 +91,15 @@ public: virtual uint8_t getc() { return 0; } - inline virtual void sendMessage(uint8_t cmdByte) { sendCommandByte(cmdByte); } - inline virtual void sendMessage(uint8_t cmdByte, uint8_t byte1) { + ALWAYS_INLINE() virtual void sendMessage(uint8_t cmdByte) { sendCommandByte(cmdByte); } + ALWAYS_INLINE() virtual void sendMessage(uint8_t cmdByte, uint8_t byte1) { uart_block = 1; sendCommandByte(cmdByte); m_putc(byte1); uart_block = 0; } - inline virtual void sendMessage(uint8_t cmdByte, uint8_t byte1, uint8_t byte2) { + ALWAYS_INLINE() virtual void sendMessage(uint8_t cmdByte, uint8_t byte1, uint8_t byte2) { uart_block = 1; sendCommandByte(cmdByte); m_putc(byte1); @@ -107,7 +107,7 @@ public: uart_block = 0; } - inline void sendCommandByte(uint8_t byte) { + ALWAYS_INLINE() void sendCommandByte(uint8_t byte) { #ifdef MIDI_RUNNING_STATUS if (MIDI_IS_REALTIME_STATUS_BYTE(byte) || MIDI_IS_SYSCOMMON_STATUS_BYTE(byte)) { @@ -185,51 +185,51 @@ public: ccCallbacks.remove(obj); } - inline void resetRunningStatus() { running_status = 0; } + ALWAYS_INLINE() void resetRunningStatus() { running_status = 0; } - inline void sendNoteOn(uint8_t note, uint8_t velocity) { + ALWAYS_INLINE() void sendNoteOn(uint8_t note, uint8_t velocity) { sendNoteOn(currentChannel, note, velocity); } - inline void sendNoteOff(uint8_t note, uint8_t velocity) { + ALWAYS_INLINE() void sendNoteOff(uint8_t note, uint8_t velocity) { sendNoteOff(currentChannel, note, velocity); } - inline void sendNoteOff(uint8_t note) { + ALWAYS_INLINE() void sendNoteOff(uint8_t note) { sendNoteOff(currentChannel, note, 0); } - inline void sendCC(uint8_t cc, uint8_t value) { + ALWAYS_INLINE() void sendCC(uint8_t cc, uint8_t value) { sendCC(currentChannel, cc, value); } - inline void sendProgramChange(uint8_t program) { + ALWAYS_INLINE() void sendProgramChange(uint8_t program) { sendProgramChange(currentChannel, program); } - inline void sendPolyKeyPressure(uint8_t note, uint8_t pressure) { + ALWAYS_INLINE() void sendPolyKeyPressure(uint8_t note, uint8_t pressure) { sendPolyKeyPressure(currentChannel, note, pressure); } - inline void sendChannelPressure(uint8_t pressure) { + ALWAYS_INLINE() void sendChannelPressure(uint8_t pressure) { sendChannelPressure(currentChannel, pressure); } - inline void sendPitchBend(int16_t pitchbend) { + ALWAYS_INLINE() void sendPitchBend(int16_t pitchbend) { sendPitchBend(currentChannel, pitchbend); } - inline void sendNRPN(uint16_t parameter, uint8_t value) { + ALWAYS_INLINE() void sendNRPN(uint16_t parameter, uint8_t value) { sendNRPN(currentChannel, parameter, value); } - inline void sendNRPN(uint16_t parameter, uint16_t value) { + ALWAYS_INLINE() void sendNRPN(uint16_t parameter, uint16_t value) { sendNRPN(currentChannel, parameter, value); } - inline void sendRPN(uint16_t parameter, uint8_t value) { + ALWAYS_INLINE() void sendRPN(uint16_t parameter, uint8_t value) { sendRPN(currentChannel, parameter, value); } - inline void sendRPN(uint16_t parameter, uint16_t value) { + ALWAYS_INLINE() void sendRPN(uint16_t parameter, uint16_t value) { sendRPN(currentChannel, parameter, value); } - inline void sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { + ALWAYS_INLINE() void sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { #ifdef MIDI_VALIDATE if ((channel >= 16) || (note >= 128) || (velocity >= 128)) return; @@ -240,7 +240,7 @@ public: sendMessage(msg[0], msg[1], msg[2]); } - inline void sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) { + ALWAYS_INLINE() void sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) { #ifdef MIDI_VALIDATE if ((channel >= 16) || (note >= 128) || (velocity >= 128)) return; @@ -251,7 +251,7 @@ public: sendMessage(msg[0], msg[1], msg[2]); } - inline void sendCC(uint8_t channel, uint8_t cc, uint8_t value) { + ALWAYS_INLINE() void sendCC(uint8_t channel, uint8_t cc, uint8_t value) { #ifdef MIDI_VALIDATE if ((channel >= 16) || (note >= 128) || (velocity >= 128)) return; @@ -262,7 +262,7 @@ public: sendMessage(msg[0], msg[1], msg[2]); } - inline void sendProgramChange(uint8_t channel, uint8_t program) { + ALWAYS_INLINE() void sendProgramChange(uint8_t channel, uint8_t program) { #ifdef MIDI_VALIDATE if ((channel >= 16) || (note >= 128) || (velocity >= 128)) return; @@ -324,8 +324,8 @@ public: puts(data, cnt); sendCommandByte(0xF7); } - inline void sendRaw(uint8_t *msg, uint16_t cnt) { puts(msg, cnt); } - inline void sendRaw(uint8_t byte) { m_putc(byte); } + ALWAYS_INLINE() void sendRaw(uint8_t *msg, uint16_t cnt) { puts(msg, cnt); } + ALWAYS_INLINE() void sendRaw(uint8_t byte) { m_putc(byte); } void sendString(const char *data) { sendString(data, m_strlen(data)); } void sendString(const char *data, uint16_t cnt); diff --git a/avr/cores/megacommand/memory.h b/avr/cores/megacommand/memory.h index 5ccdaf708..ef7958dea 100644 --- a/avr/cores/megacommand/memory.h +++ b/avr/cores/megacommand/memory.h @@ -118,7 +118,7 @@ extern inline uint8_t get_byte_bank1(volatile uint8_t *dst) { extern volatile uint8_t *rand_ptr; -extern inline uint8_t get_random_byte() { +ALWAYS_INLINE() extern inline uint8_t get_random_byte() { return (pgm_read_byte(rand_ptr++) ^ get_byte_bank1(rand_ptr) ^ slowclock) & 0x7F; } From fb88f424454a6db377453ad1ffbe5b327957b63a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 14:59:06 +1100 Subject: [PATCH 092/469] Midi inlining --- avr/cores/megacommand/Midi/MidiClock.h | 18 +++++++++--------- avr/cores/megacommand/Midi/MidiSysex.hh | 22 +++++++++++----------- avr/cores/megacommand/MidiUart.h | 16 ++++++++-------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index 152bbdc21..0b688da1b 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -217,8 +217,8 @@ class MidiClockClass { onClockCallbacks.remove(obj); } - void init(); - void callCallbacks() { + ALWAYS_INLINE() void init(); + ALWAYS_INLINE() void callCallbacks() { if (state != STARTED) return; @@ -251,7 +251,7 @@ class MidiClockClass { inCallback = false; } - void handleImmediateClock() { + ALWAYS_INLINE() void handleImmediateClock() { // if (clock > clock_last_time) { // div192th_time = (clock - clock_last_time) / 2; // DEBUG_PRINTLN( (clock - clock_last_time) / 2); @@ -278,14 +278,14 @@ class MidiClockClass { } /* in interrupt on receiving 0xF8 */ - void handleClock() { + ALWAYS_INLINE() void handleClock() { if (useImmediateClock) { handleImmediateClock(); return; } } - void increment192Counter() { + ALWAYS_INLINE() void increment192Counter() { if (state == STARTED) { div192th_counter++; mod12_counter++; @@ -310,7 +310,7 @@ class MidiClockClass { return tempo; } - void MidiClockClass::incrementCounters() { + ALWAYS_INLINE() void MidiClockClass::incrementCounters() { mod6_free_counter++; if (mod6_free_counter == 6) { diff_clock16 = midi_clock_diff(last_clock16, clock); @@ -365,7 +365,7 @@ class MidiClockClass { bool clock_less_than(uint32_t a, uint32_t b); uint32_t clock_diff_div192(uint32_t old_clock, uint32_t new_clock); - void MidiClockClass::handleImmediateMidiStart() { + ALWAYS_INLINE() void MidiClockClass::handleImmediateMidiStart() { if (transmit_uart1) { MidiUart.sendRaw(MIDI_START); } @@ -380,7 +380,7 @@ class MidiClockClass { DEBUG_PRINTLN("START"); } - void MidiClockClass::handleImmediateMidiStop() { + ALWAYS_INLINE() void MidiClockClass::handleImmediateMidiStop() { state = PAUSED; if (transmit_uart1) { MidiUart.sendRaw(MIDI_STOP); @@ -392,7 +392,7 @@ class MidiClockClass { // init(); } - void MidiClockClass::handleImmediateMidiContinue() { + ALWAYS_INLINE() void MidiClockClass::handleImmediateMidiContinue() { if (transmit_uart1) { MidiUart.sendRaw(MIDI_CONTINUE); } diff --git a/avr/cores/megacommand/Midi/MidiSysex.hh b/avr/cores/megacommand/Midi/MidiSysex.hh index fc37a2455..b5413535c 100644 --- a/avr/cores/megacommand/Midi/MidiSysex.hh +++ b/avr/cores/megacommand/Midi/MidiSysex.hh @@ -74,14 +74,14 @@ protected: volatile uint8_t *sysex_highmem_buf; public: - void startRecord(uint8_t *buf = NULL, uint16_t maxLen = 0) { + ALWAYS_INLINE() void startRecord(uint8_t *buf = NULL, uint16_t maxLen = 0) { resetRecord(buf, maxLen); recording = true; } - void stopRecord() { recording = false; } + ALWAYS_INLINE() void stopRecord() { recording = false; } - void resetRecord(uint8_t *buf = NULL, uint16_t maxLen = 0) { + ALWAYS_INLINE() void resetRecord(uint8_t *buf = NULL, uint16_t maxLen = 0) { if ((buf == NULL) && (data != NULL)) { recordBuf = data; maxRecordLen = max_len; @@ -96,11 +96,11 @@ public: recordLen = 0; } - void putByte(uint16_t offset, uint8_t c) { + ALWAYS_INLINE() void putByte(uint16_t offset, uint8_t c) { put_byte_bank1(sysex_highmem_buf + offset, c); } - uint8_t getByte(uint16_t n) { + ALWAYS_INLINE() uint8_t getByte(uint16_t n) { if (n < maxRecordLen) { // Retrieve data from specified memory buffer if (recordBuf != NULL) { @@ -114,7 +114,7 @@ public: return 255; } - bool recordByte(uint8_t c) { + ALWAYS_INLINE() bool recordByte(uint8_t c) { if (recordLen < maxRecordLen) { // Record data to specified memory buffer if (recordBuf != NULL) { @@ -170,7 +170,7 @@ public: listeners[i] = NULL; } } - bool isListenerActive(MidiSysexListenerClass *listener) { + ALWAYS_INLINE() bool isListenerActive(MidiSysexListenerClass *listener) { if (listener == NULL) return false; /* catch all */ @@ -190,7 +190,7 @@ public: } } - void reset() { + ALWAYS_INLINE() void reset() { len = 0; aborted = false; recording = false; @@ -212,7 +212,7 @@ public: } */ } - void abort() { + ALWAYS_INLINE() void abort() { // don't reset len, leave at maximum when aborted // len = 0; aborted = true; @@ -225,7 +225,7 @@ public: // Handled by main loop void end(); // Handled by interrupts - void end_immediate() { + ALWAYS_INLINE() void end_immediate() { recvIds[0] = getByte(0); if (recvIds[0] == 0x00) { sysexLongId = true; @@ -243,7 +243,7 @@ public: } } - void handleByte(uint8_t byte) { + ALWAYS_INLINE() void handleByte(uint8_t byte) { if (recording) { len++; recordByte(byte); diff --git a/avr/cores/megacommand/MidiUart.h b/avr/cores/megacommand/MidiUart.h index 0a34713af..2de7c4e75 100644 --- a/avr/cores/megacommand/MidiUart.h +++ b/avr/cores/megacommand/MidiUart.h @@ -108,7 +108,7 @@ class MidiUartClass : public MidiUartParent { MidiUartClass(volatile uint8_t *rx_buf = NULL, uint16_t rx_buf_size = 0, volatile uint8_t *tx_buf = NULL, uint16_t tx_buf_size = 0); - inline void m_putc(uint8_t c) { + ALWAYS_INLINE() void m_putc(uint8_t c) { if (c == 0xF0) { uart_block = 1; } @@ -118,10 +118,10 @@ class MidiUartClass : public MidiUartParent { txRb.put_h(c); UART_SET_ISR_TX_BIT(); } - inline bool avail() { return !rxRb.isEmpty(); } - inline uint8_t m_getc() { return rxRb.get(); } + ALWAYS_INLINE() bool avail() { return !rxRb.isEmpty(); } + ALWAYS_INLINE() uint8_t m_getc() { return rxRb.get(); } - virtual void m_putc_immediate(uint8_t c); + ALWAYS_INLINE() virtual void m_putc_immediate(uint8_t c); void set_speed(uint32_t speed, uint8_t port); @@ -139,10 +139,10 @@ class MidiUartClass2 : public MidiUartParent { public: MidiUartClass2(volatile uint8_t *rx_buf = NULL, uint16_t rx_buf_size = 0, volatile uint8_t *tx_buf = NULL, uint16_t tx_buf_size = 0); - inline bool avail() { return !rxRb.isEmpty(); } - inline uint8_t m_getc() { return rxRb.get(); } + ALWAYS_INLINE() bool avail() { return !rxRb.isEmpty(); } + ALWAYS_INLINE() uint8_t m_getc() { return rxRb.get(); } - inline void m_putc(uint8_t c) { + ALWAYS_INLINE() void m_putc(uint8_t c) { #ifdef UART2_TX if (c == 0xF0) { uart_block = 1; @@ -155,7 +155,7 @@ class MidiUartClass2 : public MidiUartParent { #endif } - virtual void m_putc_immediate(uint8_t c); + ALWAYS_INLINE() virtual void m_putc_immediate(uint8_t c); volatile RingBuffer<0, RX_BUF_TYPE> rxRb; #ifdef UART2_TX volatile RingBuffer<0, TX_BUF_TYPE> txRb; From 7a708678d34dffee814ae5ba044b09e407bf2b66 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 14:59:28 +1100 Subject: [PATCH 093/469] Memory inlining --- avr/cores/megacommand/memory.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/memory.h b/avr/cores/megacommand/memory.h index ef7958dea..a4b129165 100644 --- a/avr/cores/megacommand/memory.h +++ b/avr/cores/megacommand/memory.h @@ -46,7 +46,7 @@ #define PL6_MASK (1 << PL6) -extern inline uint8_t switch_ram_bank(uint8_t x) { +ALWAYS_INLINE() extern inline uint8_t switch_ram_bank(uint8_t x) { uint8_t old_bank = (uint8_t) (PORTL & PL6_MASK); if (x != old_bank) { @@ -60,7 +60,7 @@ extern inline uint8_t switch_ram_bank(uint8_t x) { #define PB0_MASK (1 << PB0) -extern inline uint8_t switch_ram_bank(uint8_t x) { +ALWAYS_INLINE() extern inline uint8_t switch_ram_bank(uint8_t x) { uint8_t old_bank = (uint8_t) (PORTB & PB0_MASK); if (x != old_bank) { @@ -79,38 +79,38 @@ class RamBankSelector { private: uint8_t m_oldbank; public: - RamBankSelector(uint8_t bank) { m_oldbank = switch_ram_bank(bank); } - ~RamBankSelector() { switch_ram_bank(m_oldbank); } + ALWAYS_INLINE() RamBankSelector(uint8_t bank) { m_oldbank = switch_ram_bank(bank); } + ALWAYS_INLINE() ~RamBankSelector() { switch_ram_bank(m_oldbank); } }; #define select_bank(x) RamBankSelector __bank_selector(x) template -extern inline T get_bank1(volatile T *dst) { +ALWAYS_INLINE() extern inline T get_bank1(volatile T *dst) { select_bank(1); T c = *dst; return c; } template -extern inline void put_bank1(volatile T *dst, T data) { +ALWAYS_INLINE() extern inline void put_bank1(volatile T *dst, T data) { select_bank(1); *dst = data; } #endif// __cplusplus -extern inline void memcpy_bank1(volatile void *dst, volatile void *src, uint32_t len) { +ALWAYS_INLINE() extern inline void memcpy_bank1(volatile void *dst, volatile void *src, uint32_t len) { select_bank(1); memcpy(dst, src, len); } -extern inline void put_byte_bank1(volatile uint8_t *dst, uint8_t byte) { +ALWAYS_INLINE() extern inline void put_byte_bank1(volatile uint8_t *dst, uint8_t byte) { select_bank(1); *dst = byte; } -extern inline uint8_t get_byte_bank1(volatile uint8_t *dst) { +ALWAYS_INLINE() extern inline uint8_t get_byte_bank1(volatile uint8_t *dst) { select_bank(1); uint8_t c = *dst; return c; From d427589393c229998e3ac48a40e40954812039f1 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 14:59:41 +1100 Subject: [PATCH 094/469] RingBuffer inlining --- .../megacommand/CommonTools/RingBuffer.h | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/CommonTools/RingBuffer.h b/avr/cores/megacommand/CommonTools/RingBuffer.h index 697ad4dbf..1ee2488f6 100644 --- a/avr/cores/megacommand/CommonTools/RingBuffer.h +++ b/avr/cores/megacommand/CommonTools/RingBuffer.h @@ -46,29 +46,29 @@ template class CRingBuffer { #endif CRingBuffer(volatile uint8_t *ptr = NULL); /** Add a new element c to the ring buffer. **/ - bool put(C c) volatile; + ALWAYS_INLINE() bool put(C c) volatile; /** A slightly more efficient version of put, if ptr == NULL */ - bool put_h(C c) volatile; + ALWAYS_INLINE() bool put_h(C c) volatile; /** put_h but when running from within isr that is already blocking**/ - bool put_h_isr(C c) volatile; + ALWAYS_INLINE() bool put_h_isr(C c) volatile; /** Copy a new element pointed to by c to the ring buffer. **/ - bool putp(C *c) volatile; + ALWAYS_INLINE() bool putp(C *c) volatile; /** Return the next element in the ring buffer. **/ - C get() volatile; + ALWAYS_INLINE() C get() volatile; /** A slightly more efficient version of get, if ptr == NULL */ - C get_h() volatile; + ALWAYS_INLINE() C get_h() volatile; /** get_h but when running from within isr that is already blocking**/ - C get_h_isr() volatile; + ALWAYS_INLINE() C get_h_isr() volatile; /** Copy the next element into dst. **/ - bool getp(C *dst) volatile; + ALWAYS_INLINE() bool getp(C *dst) volatile; /** Get the next element without removing it from the ring buffer. **/ - C peek() volatile; + ALWAYS_INLINE() C peek() volatile; /** Returns true if the ring buffer is empty. **/ - inline bool isEmpty() volatile; + ALWAYS_INLINE() bool isEmpty() volatile; /** Returns true if the ring buffer is empty. Use in isr**/ - inline bool isEmpty_isr() volatile; + ALWAYS_INLINE() bool isEmpty_isr() volatile; /** Returns true if the ring buffer is full. **/ - inline bool isFull() volatile; + ALWAYS_INLINE() bool isFull() volatile; /** Returns the number of elements in the ring buffer. **/ T size() volatile; From 2e61a2c24f9f0224ecdda8b5ae1e37f2c17be5c3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 15:00:07 +1100 Subject: [PATCH 095/469] GUI inlining --- avr/cores/megacommand/GUI_private.cpp | 10 +++++----- avr/cores/megacommand/GUI_private.h | 22 ++++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/GUI_private.cpp b/avr/cores/megacommand/GUI_private.cpp index ccc08bc20..5f6e0883f 100644 --- a/avr/cores/megacommand/GUI_private.cpp +++ b/avr/cores/megacommand/GUI_private.cpp @@ -26,12 +26,12 @@ #define SR165_DELAY() { } // asm("nop"); } // asm("nop"); asm("nop"); } -inline void SR165Class::clk() { +ALWAYS_INLINE() inline void SR165Class::clk() { CLEAR_BIT8(SR165_DATA_PORT, SR165_CLK); SET_BIT8(SR165_DATA_PORT, SR165_CLK); } -inline void SR165Class::rst() { +ALWAYS_INLINE() inline void SR165Class::rst() { CLEAR_BIT8(SR165_DATA_PORT, SR165_SHLOAD); SET_BIT8(SR165_DATA_PORT, SR165_SHLOAD); } @@ -44,7 +44,7 @@ SR165Class::SR165Class() { SET_BIT8(SR165_DATA_PORT, SR165_SHLOAD); } -uint8_t SR165Class::read() { +ALWAYS_INLINE() uint8_t SR165Class::read() { rst(); uint8_t res = 0; @@ -58,7 +58,7 @@ uint8_t SR165Class::read() { return res; } -uint8_t SR165Class::read_norst() { +ALWAYS_INLINE() uint8_t SR165Class::read_norst() { uint8_t res = 0; uint8_t i = 0; for (i = 0; i < 8; i++) { @@ -71,7 +71,7 @@ uint8_t SR165Class::read_norst() { } -uint16_t SR165Class::read16() { +ALWAYS_INLINE() uint16_t SR165Class::read16() { rst(); uint16_t res = 0; diff --git a/avr/cores/megacommand/GUI_private.h b/avr/cores/megacommand/GUI_private.h index ac7684ec3..9d4042758 100644 --- a/avr/cores/megacommand/GUI_private.h +++ b/avr/cores/megacommand/GUI_private.h @@ -3,14 +3,16 @@ #include #include +#include "Core.h" + class SR165Class { inline void rst(); inline void clk(); public: SR165Class(); - uint8_t read(); - uint16_t read16(); - uint8_t read_norst(); + ALWAYS_INLINE() uint8_t read(); + ALWAYS_INLINE() uint16_t read16(); + ALWAYS_INLINE() uint8_t read_norst(); }; #define GUI_NUM_ENCODERS 4 @@ -30,13 +32,13 @@ class EncodersClass { EncodersClass(); - void poll(uint16_t sr); - void clearEncoders(); + ALWAYS_INLINE() void poll(uint16_t sr); + ALWAYS_INLINE() void clearEncoders(); - inline int8_t getNormal(uint8_t i) { return encoders[i].normal; } - inline int8_t getButton(uint8_t i) { return encoders[i].button; } + ALWAYS_INLINE() int8_t getNormal(uint8_t i) { return encoders[i].normal; } + ALWAYS_INLINE() int8_t getButton(uint8_t i) { return encoders[i].button; } - int8_t limitValue(int8_t value, int8_t min, int8_t max) { + ALWAYS_INLINE() int8_t limitValue(int8_t value, int8_t min, int8_t max) { if (value > max) return max; if (value < min) @@ -132,8 +134,8 @@ class ButtonsClass { static const uint16_t BUTTON4_MASK = _BV(BUTTON4); ButtonsClass(); - void clear(); - void poll(uint8_t sr); + ALWAYS_INLINE() void clear(); + ALWAYS_INLINE() void poll(uint8_t sr); }; extern SR165Class SR165; From d1acb5f23d10486175588495b447389c4bb72878 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 15:00:36 +1100 Subject: [PATCH 096/469] gui_poll_inline --- avr/cores/megacommand/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index 8475ad815..f57434dd1 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -221,7 +221,7 @@ ISR(TIMER1_COMPA_vect) { static uint16_t oldsr = 0; volatile uint8_t *rand_ptr = 0; -void gui_poll() { +ALWAYS_INLINE() void gui_poll() { static bool inGui = false; if (inGui) { return; From 568b52c755047dcdf3c547dbddf37adbda789cb1 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 20 Oct 2019 13:23:47 +0800 Subject: [PATCH 097/469] reduce menu_option_t number in menu_item_t --- avr/cores/megacommand/MCL/Menu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/Menu.h b/avr/cores/megacommand/MCL/Menu.h index 6cb25d1c6..ea77a4dd6 100644 --- a/avr/cores/megacommand/MCL/Menu.h +++ b/avr/cores/megacommand/MCL/Menu.h @@ -2,7 +2,7 @@ #define MENU_H__ #define MAX_MENU_ITEMS 16 -#define MAX_MENU_OPTIONS 16 +#define MAX_MENU_OPTIONS 6 typedef void (*FP)(); From ab4b397feb81786b5095ff0a59e9793815a6dc23 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 20 Oct 2019 14:11:02 +0800 Subject: [PATCH 098/469] finalize file menu layout --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 8 +++++--- avr/cores/megacommand/MCL/MenuPage.cpp | 14 +++++++------- avr/cores/megacommand/MCL/MenuPage.h | 5 +++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 6d632e3b4..5ae7548e2 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -25,6 +25,7 @@ void FileBrowserPage::init() { char temp_entry[16]; // config menu + file_menu_page.visible_rows = 3; file_menu_page.menu.enable_entry(0, show_new_folder); file_menu_page.menu.enable_entry(1, true); // delete file_menu_page.menu.enable_entry(2, true); // rename @@ -101,9 +102,9 @@ void FileBrowserPage::init() { void FileBrowserPage::display() { #ifdef OLED_DISPLAY if (filemenu_active) { - oled_display.fillRect(0, 3, 42, 28, BLACK); - oled_display.drawRect(1, 4, 40, 26, WHITE); - file_menu_page.draw_menu(3, 12, 39, 3); + oled_display.fillRect(0, 3, 45, 28, BLACK); + oled_display.drawRect(1, 4, 43, 26, WHITE); + file_menu_page.draw_menu(6, 12, 39); oled_display.display(); return; } @@ -322,6 +323,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON3)) { filemenu_active = true; file_menu_encoder.cur = file_menu_encoder.old = 0; + file_menu_page.cur_row = 0; encoders[0] = &config_param1; encoders[1] = &file_menu_encoder; file_menu_page.init(); diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 594abe65f..864a7d4ff 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -34,8 +34,8 @@ void MenuPageBase::loop() { uint8_t diff = encoders[1]->cur - encoders[1]->old; int8_t new_val = cur_row + diff; #ifdef OLED_DISPLAY - if (new_val > MAX_VISIBLE_ROWS - 1) { - new_val = MAX_VISIBLE_ROWS - 1; + if (new_val > visible_rows - 1) { + new_val = visible_rows - 1; } if (new_val < 0) { new_val = 0; @@ -61,7 +61,7 @@ void MenuPageBase::loop() { void MenuPageBase::draw_scrollbar(uint8_t x_offset) { #ifdef OLED_DISPLAY mcl_gui.draw_vertical_scrollbar(x_offset, get_menu()->get_number_of_items(), - MAX_VISIBLE_ROWS, encoders[1]->cur - cur_row); + visible_rows, encoders[1]->cur - cur_row); #endif } @@ -94,13 +94,13 @@ void MenuPageBase::draw_item(uint8_t item_n, uint8_t row) { #endif } -void MenuPageBase::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width, uint8_t rows) { +void MenuPageBase::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width) { #ifdef OLED_DISPLAY oled_display.setCursor(x_offset, y_offset); uint8_t number_of_items = get_menu()->get_number_of_items(); uint8_t max_items; - if (number_of_items > rows) { - max_items = rows; + if (number_of_items > visible_rows) { + max_items = visible_rows; } else { max_items = number_of_items; } @@ -143,7 +143,7 @@ void MenuPageBase::display() { draw_menu(x_offset, 8); - if (number_of_items > MAX_VISIBLE_ROWS) { + if (number_of_items > visible_rows) { draw_scrollbar(120); } oled_display.display(); diff --git a/avr/cores/megacommand/MCL/MenuPage.h b/avr/cores/megacommand/MCL/MenuPage.h index 12650a2e2..614815a1d 100644 --- a/avr/cores/megacommand/MCL/MenuPage.h +++ b/avr/cores/megacommand/MCL/MenuPage.h @@ -19,6 +19,8 @@ class MenuPageBase : public LightPage { uint8_t cur_col = 0; uint8_t cur_row = 0; + uint8_t visible_rows = MAX_VISIBLE_ROWS; + MenuPageBase(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) {} @@ -26,8 +28,7 @@ class MenuPageBase : public LightPage { void draw_scrollbar(uint8_t x_offset); void draw_item(uint8_t item_n, uint8_t row); void draw_menu(uint8_t x_offset, uint8_t y_offset, - uint8_t width = MENU_WIDTH, - uint8_t rows = MAX_VISIBLE_ROWS); + uint8_t width = MENU_WIDTH); void loop(); void display(); void setup(); From afb201303af6ba74e5c02a49e1e524dfc0842a24 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 18:58:08 +1100 Subject: [PATCH 099/469] add medium size encoder sprites --- art/sprites/encoder0.png | Bin 0 -> 147 bytes art/sprites/encoder_medium_1.png | Bin 0 -> 171 bytes art/sprites/encoder_medium_2.png | Bin 0 -> 173 bytes art/sprites/encoder_medium_3.png | Bin 0 -> 173 bytes art/sprites/encoder_medium_4.png | Bin 0 -> 169 bytes art/sprites/encoder_medium_5.png | Bin 0 -> 171 bytes art/sprites/encoder_medium_6.png | Bin 0 -> 168 bytes 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/encoder0.png create mode 100644 art/sprites/encoder_medium_1.png create mode 100644 art/sprites/encoder_medium_2.png create mode 100644 art/sprites/encoder_medium_3.png create mode 100644 art/sprites/encoder_medium_4.png create mode 100644 art/sprites/encoder_medium_5.png create mode 100644 art/sprites/encoder_medium_6.png diff --git a/art/sprites/encoder0.png b/art/sprites/encoder0.png new file mode 100644 index 0000000000000000000000000000000000000000..db88b5e2a9befd1d2b9bbeae0a37c4aabc263108 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|d_7$pLnI`V zQx35FpV%TX@rH)a1f`T#1<#x$iLL;jM#)1hdR`szopr010a=*#H0l literal 0 HcmV?d00001 diff --git a/art/sprites/encoder_medium_1.png b/art/sprites/encoder_medium_1.png new file mode 100644 index 0000000000000000000000000000000000000000..56d6df6596e4d9256100f980a4a63ceb4e40c554 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^{2M)HD1L>R~zQR)CPOao360GnX}=&HS|dSy@Gn+2XjOvLk=BFRbt6o}8v%He=Od1ICC6OWiry5-x^)za+~1 TlyTBKpoI*cu6{1-oD!M<66`-p literal 0 HcmV?d00001 diff --git a/art/sprites/encoder_medium_2.png b/art/sprites/encoder_medium_2.png new file mode 100644 index 0000000000000000000000000000000000000000..83bbc3f0cb93d2ae95f4268f6651f44014f5eb3e GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^{2cx9_fwDZY2F|8Vq*uDQ8zG1m>2X9^ut1ePq1(<*v*%4W71SETvB zlUe5L{hrSGmHEm4==X;^XPkLE$=m5&{DZI2hZc)(kTZOALeU|NNx)hE_9P>YJKS1I WVt;k!mfQeZ$>8bg=d#Wzp$PzGNI}^E literal 0 HcmV?d00001 diff --git a/art/sprites/encoder_medium_3.png b/art/sprites/encoder_medium_3.png new file mode 100644 index 0000000000000000000000000000000000000000..4126f7b8d9d381d956bed55ab309535861192c57 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^{2zYGzHmdeF34qQzuk#*wMP0V0mQ#{;+-l(Qv% UvYh+g3AB>I)78&qol`;+08SA)DF6Tf literal 0 HcmV?d00001 diff --git a/art/sprites/encoder_medium_4.png b/art/sprites/encoder_medium_4.png new file mode 100644 index 0000000000000000000000000000000000000000..a1a7043862631f6897e3703b10f92a00096e5879 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^{2&W(Q~QmP1QU>@+UEs>?Oezs|W# z+JE;V|N2*d`hR5Acj!FoYfmgJdUWnblQrA9l5mAeHVuPBiOanTJVNCR6PUz*O>N@U Q1X{=7>FVdQ&MBb@03ZT7rvLx| literal 0 HcmV?d00001 diff --git a/art/sprites/encoder_medium_5.png b/art/sprites/encoder_medium_5.png new file mode 100644 index 0000000000000000000000000000000000000000..9b7d62b73443e60c02dc6f8d99b975d54acea5d7 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^{2E0c$8zLv Date: Sun, 20 Oct 2019 17:34:39 +0800 Subject: [PATCH 100/469] FileBrowserPage general fixes, rename handles suffix. --- avr/cores/megacommand/CommonTools/helpers.c | 13 ++ avr/cores/megacommand/CommonTools/helpers.h | 1 + avr/cores/megacommand/MCL/FileBrowserPage.cpp | 120 ++++++++++++------ avr/cores/megacommand/MCL/FileBrowserPage.h | 2 +- avr/cores/megacommand/MCL/MCLGUI.cpp | 1 + avr/cores/megacommand/MCL/MCLGUI.h | 3 + avr/cores/megacommand/memory.h | 2 +- 7 files changed, 100 insertions(+), 42 deletions(-) diff --git a/avr/cores/megacommand/CommonTools/helpers.c b/avr/cores/megacommand/CommonTools/helpers.c index 9f56c38bd..ec7ec4ed3 100644 --- a/avr/cores/megacommand/CommonTools/helpers.c +++ b/avr/cores/megacommand/CommonTools/helpers.c @@ -300,6 +300,19 @@ void m_toupper(char* str) } } +/** Trim ending spaces **/ +void m_trim_space(char* str) +{ + for(int i = m_strlen(str) - 1; i >= 0; --i) { + if (str[i] == ' ') { + str[i] = '\0'; + } + // break on first visible character + if (str[i] != '\0') { + break; + } + } +} /** @} **/ /** diff --git a/avr/cores/megacommand/CommonTools/helpers.h b/avr/cores/megacommand/CommonTools/helpers.h index cf640da2b..a65218e3a 100644 --- a/avr/cores/megacommand/CommonTools/helpers.h +++ b/avr/cores/megacommand/CommonTools/helpers.h @@ -212,6 +212,7 @@ void m_str16cpy_p(void *dst, PGM_P src); void m_strnappend(void *dst, const char *src, int len); uint16_t m_strlen(const char *str); void m_toupper(char* str); +void m_trim_space(char* str); /** @} */ diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 5ae7548e2..2a8497731 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -13,10 +13,12 @@ void FileBrowserPage::setup() { DEBUG_PRINT_FN(); } -void FileBrowserPage::add_entry(char *entry) { - uint32_t pos = BANK1_FILE_ENTRIES_START + numEntries * 16; - volatile uint8_t *ptr = (uint8_t *)pos; - memcpy_bank1(ptr, entry, 16); +void FileBrowserPage::add_entry(const char *entry) { + char buf[16]; + m_strncpy(buf, entry, sizeof(buf)); + buf[15] = '\0'; + volatile uint8_t *ptr = (uint8_t *)BANK1_FILE_ENTRIES_START + numEntries * 16; + memcpy_bank1(ptr, buf, sizeof(buf)); numEntries++; } @@ -41,16 +43,14 @@ void FileBrowserPage::init() { numEntries = 0; cur_file = 255; if (show_save) { - char create_new[9] = "[ SAVE ]"; - add_entry(&create_new[0]); + add_entry("[ SAVE ]"); } - char up_one_dir[3] = ".."; SD.vwd()->getName(temp_entry, 16); DEBUG_DUMP(temp_entry); if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { - add_entry(up_one_dir); + add_entry(".."); } encoders[1]->cur = 1; @@ -140,8 +140,8 @@ void FileBrowserPage::display() { } char temp_entry[16]; uint16_t entry_num = encoders[1]->cur - cur_row + n; - uint32_t pos = BANK1_FILE_ENTRIES_START + entry_num * 16; - volatile uint8_t *ptr = (uint8_t *)pos; + volatile uint8_t *ptr = + (uint8_t *)BANK1_FILE_ENTRIES_START + entry_num * 16; memcpy_bank1(temp_entry, ptr, 16); oled_display.println(temp_entry); } @@ -205,11 +205,6 @@ void FileBrowserPage::loop() { bool FileBrowserPage::create_folder() { char new_dir[17] = "new_folder "; if (mcl_gui.wait_for_input(new_dir, "Create Folder", 8)) { - for (uint8_t n = 0; n < strlen(new_dir); n++) { - if (new_dir[n] == ' ') { - new_dir[n] = '\0'; - } - } SD.mkdir(new_dir); init(); } @@ -221,50 +216,71 @@ void FileBrowserPage::_calcindices(int &saveidx) { } void FileBrowserPage::_cd_up() { - char dir_entry[16]; + DEBUG_PRINT_FN(); + file.close(); - SD.chdir(lwd); - SD.vwd()->getName(dir_entry, 16); + // don't cd up if we are at the root auto len_lwd = strlen(lwd); - auto len_dir_entry = strlen(dir_entry); + if (len_lwd < 2) { + init(); + return; + } // trim ending '/' if (lwd[len_lwd - 1] == '/') { lwd[--len_lwd] = '\0'; } - if (dir_entry[len_dir_entry - 1] == '/') { - dir_entry[--len_dir_entry] = '\0'; + + // find parent path separator and trim it + for (int i = len_lwd - 1; i >= 0; --i) { + if (lwd[i] == '/') { + lwd[i] = '\0'; + break; + } + } + + // in case root is trimmed, add it back + if (lwd[0] == '\0') { + strcpy(lwd, "/"); } - lwd[len_lwd - len_dir_entry] = '\0'; - DEBUG_DUMP(dir_entry); DEBUG_DUMP(lwd); + SD.chdir(lwd); init(); } void FileBrowserPage::_cd(const char *child) { - char dir_entry[16]; file.close(); - SD.vwd()->getName(dir_entry, 16); - strcat(lwd, dir_entry); - if (dir_entry[strlen(dir_entry) - 1] != '/') { + if (!SD.chdir(child)) { + gfx.alert("ERROR", "Failed to change dir."); + init(); + return; + } + if (strcmp(lwd, "/") != 0) { strcat(lwd, "/"); } + strcat(lwd, child); + auto len_lwd = strlen(lwd); + // trim ending '/' + if (lwd[len_lwd - 1] == '/') { + lwd[--len_lwd] = '\0'; + } + DEBUG_DUMP(lwd); DEBUG_DUMP(child); - SD.chdir(child); init(); } void FileBrowserPage::_handle_filemenu() { char buf1[16]; - uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = (uint8_t *)pos; - memcpy_bank1(&buf1[0], ptr, 16); - + volatile uint8_t *ptr = + (uint8_t *)BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; + memcpy_bank1(&buf1[0], ptr, sizeof(buf1)); + char *suffix_pos = strchr(buf1, '.'); char buf2[32] = {'\0'}; + uint8_t name_length = 8; switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { case 0: // new folder @@ -279,8 +295,19 @@ void FileBrowserPage::_handle_filemenu() { } break; case 2: // rename + // trim the suffix is present, add back later strcat(buf2, buf1); - if (mcl_gui.wait_for_input(buf2, "RENAME TO:", 16)) { + if (suffix_pos != nullptr) { + buf2[suffix_pos - buf1] = '\0'; + } + // default max length = 8, can extend if buf2 without suffix + // is longer than 8. + name_length = max(name_length, strlen(buf2)); + if (mcl_gui.wait_for_input(buf2, "RENAME TO:", name_length)) { + if (suffix_pos != nullptr) { + // paste the suffix back + strcat(buf2, suffix_pos); + } on_rename(buf1, buf2); } break; @@ -289,6 +316,8 @@ void FileBrowserPage::_handle_filemenu() { strcat(buf2, buf1); strcat(buf2, "?"); if (mcl_gui.wait_for_confirm("CONFIRM", buf2)) { + // the derived class may expect the file to be open + // when on_select is called. file.open(buf1, O_READ); on_select(buf1); } @@ -297,14 +326,25 @@ void FileBrowserPage::_handle_filemenu() { } void FileBrowserPage::on_delete(const char *entry) { - if (SD.remove(entry)) { - gfx.alert("SUCCESS", "File removed."); + file.open(entry, O_READ); + bool dir = file.isDirectory(); + file.close(); + if (dir) { + if (SD.rmdir(entry)) { + gfx.alert("SUCCESS", "Folder removed."); + } else { + gfx.alert("ERROR", "Folder not removed."); + } } else { - gfx.alert("ERROR", "File not removed."); + if (SD.remove(entry)) { + gfx.alert("SUCCESS", "File removed."); + } else { + gfx.alert("ERROR", "File not removed."); + } } } -void FileBrowserPage::on_rename(const char* from, const char* to) { +void FileBrowserPage::on_rename(const char *from, const char *to) { if (SD.rename(from, to)) { gfx.alert("SUCCESS", "File renamed."); } else { @@ -352,9 +392,9 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { } char temp_entry[16]; - uint32_t pos = BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; - volatile uint8_t *ptr = (uint8_t *)pos; - memcpy_bank1(&temp_entry[0], ptr, 16); + volatile uint8_t *ptr = + (uint8_t *)BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; + memcpy_bank1(temp_entry, ptr, 16); // chdir to parent if ((temp_entry[0] == '.') && (temp_entry[1] == '.')) { diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index 7f4389386..e668bad81 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -56,7 +56,7 @@ class FileBrowserPage : public LightPage { } virtual bool handleEvent(gui_event_t *event); virtual void display(); - void add_entry(char *entry); + void add_entry(const char *entry); void draw_scrollbar(uint8_t x_offset); bool create_folder(); virtual void loop(); diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 0d4a60429..cdc0c6876 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -7,6 +7,7 @@ bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { while (GUI.currentPage() == &text_input_page) { GUI.loop(); } + m_trim_space(dst); return text_input_page.return_state; } diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 2e7e43669..9add14f25 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -8,6 +8,9 @@ class MCLGUI { public: + // fills dst buffer with input text. ensures that: + // 1. dst is null-terminated + // 2. dst has no trailing spaces bool wait_for_input(char *dst, const char *title, uint8_t len); bool wait_for_confirm(const char *title, const char* text); void draw_infobox(const char* line1, const char* line2, const int line2_offset = 0); diff --git a/avr/cores/megacommand/memory.h b/avr/cores/megacommand/memory.h index 5ccdaf708..f8dee485a 100644 --- a/avr/cores/megacommand/memory.h +++ b/avr/cores/megacommand/memory.h @@ -100,7 +100,7 @@ extern inline void put_bank1(volatile T *dst, T data) { #endif// __cplusplus -extern inline void memcpy_bank1(volatile void *dst, volatile void *src, uint32_t len) { +extern inline void memcpy_bank1(volatile void *dst, volatile const void *src, uint32_t len) { select_bank(1); memcpy(dst, src, len); } From 5b10fe7893d7dc4ce2602f24810a832db711ce0a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 19 Oct 2019 18:42:01 +1100 Subject: [PATCH 101/469] underlying encoder functionality working --- avr/cores/megacommand/MCL/AuxPages.cpp | 25 ++- avr/cores/megacommand/MCL/AuxPages.h | 10 + avr/cores/megacommand/MCL/FXPage.cpp | 186 +++++++++++++++++++ avr/cores/megacommand/MCL/FXPage.h | 53 ++++++ avr/cores/megacommand/MCL/PageSelectPage.cpp | 7 +- 5 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 avr/cores/megacommand/MCL/FXPage.cpp create mode 100644 avr/cores/megacommand/MCL/FXPage.h diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index 1be626882..a18412371 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -5,8 +5,27 @@ extern MCLEncoder mixer_param2(0, 127); extern MCLEncoder mixer_param3(0, 127); extern MCLEncoder mixer_param4(0, 127); -extern MCLEncoder route_param1(2,5); -extern MCLEncoder route_param2(0,6); +extern MCLEncoder route_param1(2, 5); +extern MCLEncoder route_param2(0, 6); -MixerPage mixer_page(&mixer_param1, &mixer_param2, &mixer_param3, &mixer_param4); +extern MCLEncoder fx_param1(0, 127); +extern MCLEncoder fx_param2(0, 127); +extern MCLEncoder fx_param3(0, 127); +extern MCLEncoder fx_param4(0, 127); + +MixerPage mixer_page(&mixer_param1, &mixer_param2, &mixer_param3, + &mixer_param4); RoutePage route_page(&route_param1, &route_param2, &route_param2); + +fx_param_t fx_echo_params[8] = { + {MD_FX_ECHO, MD_ECHO_TIME}, + {MD_FX_ECHO, MD_ECHO_MOD}, + {MD_FX_ECHO, MD_ECHO_MFRQ}, + {MD_FX_ECHO, MD_ECHO_FB}, + {MD_FX_ECHO, MD_ECHO_FLTF}, + {MD_FX_ECHO, MD_ECHO_FLTW}, + {MD_FX_ECHO, MD_ECHO_MONO}, + {MD_FX_ECHO, MD_ECHO_LEV}}; + +FXPage fx_page_a(&fx_param1, &fx_param2, &fx_param3, &fx_param4, + (fx_param_t*) &fx_echo_params, 8); diff --git a/avr/cores/megacommand/MCL/AuxPages.h b/avr/cores/megacommand/MCL/AuxPages.h index d395b2f29..c9513f24e 100644 --- a/avr/cores/megacommand/MCL/AuxPages.h +++ b/avr/cores/megacommand/MCL/AuxPages.h @@ -7,6 +7,8 @@ #include "MixerPage.h" #include "RoutePage.h" #include "RAMPage.h" +#include "FXPage.h" +#include "MD.h" extern MCLEncoder mixer_param1; extern MCLEncoder mixer_param2; @@ -20,4 +22,12 @@ extern RoutePage route_page; extern RAMPage ram_page_a; extern RAMPage ram_page_b; + +extern MCLEncoder fx_param1; +extern MCLEncoder fx_param2; +extern MCLEncoder fx_param3; +extern MCLEncoder fx_param4; + +extern FXPage fx_page_a; + #endif /* AUXPAGES_H__ */ diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp new file mode 100644 index 000000000..fa91eae4a --- /dev/null +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -0,0 +1,186 @@ +#include "FXPage.h" +#include "MCL.h" + +#define FX_TYPE 0 +#define FX_PARAM 1 +#define NUM_OF_ENCODERS 4 + +void FXPage::setup() { DEBUG_PRINT_FN(); } + +void FXPage::init() { + DEBUG_PRINT_FN(); +#ifdef OLED_DISPLAY + classic_display = false; + oled_display.clearDisplay(); + oled_display.setFont(); +#endif + md_exploit.off(); + for (uint8_t a = 0; a < num_of_params; a++) { + DEBUG_PRINTLN("params"); + DEBUG_PRINTLN(params[a].type); + DEBUG_PRINTLN(params[a].param); + } + + + for (uint8_t n = 0; n < num_of_params; n++) { + + uint8_t fx_param = params[n].param; + switch (params[n].type) { + case MD_FX_ECHO: + DEBUG_PRINTLN("setting delay"); + DEBUG_PRINTLN(n); + DEBUG_PRINTLN(fx_param); + encoders[n]->cur = MD.kit.delay[fx_param]; + break; + case MD_FX_REV: + encoders[n]->cur = MD.kit.reverb[fx_param]; + break; + case MD_FX_EQ: + encoders[n]->cur = MD.kit.eq[fx_param]; + break; + case MD_FX_DYN: + encoders[n]->cur = MD.kit.dynamics[fx_param]; + break; + } + + encoders[n]->old = encoders[n]->cur; + } +} +void FXPage::cleanup() { + // md_exploit.off(); +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif +} + +void FXPage::loop() { + + for (uint8_t i = 0; i < NUM_OF_ENCODERS; i++) { + uint8_t n = i + ((page_mode ? 1 : 0) * NUM_OF_ENCODERS); + + if (encoders[i]->hasChanged()) { + uint8_t fx_param = params[n].param; + uint8_t fx_type = params[n].type; + + uint8_t val; + //Interpolation. + for (val = encoders[i]->old; val < encoders[i]->cur; val++) { + MD.sendFXParam(fx_param, val, fx_type); + } + for (val = encoders[i]->old; val > encoders[i]->cur; val--) { + MD.sendFXParam(fx_param, val, fx_type); + } + + } + } +} +void FXPage::display() { + + if (!classic_display) { +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif + } +#ifndef OLED_DISPLAY + GUI.clearLines(); + GUI.setLine(GUI.LINE1); + uint8_t x; + + GUI.put_string_at(0, "FX"); + GUI.put_value_at1(4, page_mode ? 1 : 0); + GUI.setLine(GUI.LINE2); + /* + if (mcl_cfg.ram_page_mode == 0) { + GUI.put_string_at(0, "MON"); + } else { + GUI.put_string_at(0, "LNK"); + } + */ + +#endif +#ifdef OLED_DISPLAY + oled_display.setFont(); + oled_display.setCursor(0, 0); + + oled_display.print("FX"); + oled_display.print(page_mode ? 1 : 0); + + for (uint8_t i = 0; i < NUM_OF_ENCODERS; i++) { + uint8_t n = i + ((page_mode ? 1 : 0) * NUM_OF_ENCODERS); + + uint8_t fx_param = params[n].param; + uint8_t fx_type = params[n].type; + + + oled_display.print(encoders[i]->cur); + oled_display.print(" "); + } + oled_display.display(); + +#endif +} +void FXPage::onControlChangeCallback_Midi(uint8_t *msg) { + uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); + uint8_t param = msg[1]; + uint8_t value = msg[2]; + uint8_t track; + uint8_t track_param; + // If external keyboard controlling MD pitch, send parameter updates + // to all polyphonic tracks + uint8_t param_true = 0; + + MD.parseCC(channel, param, &track, &track_param); +} + +void FXPage::setup_callbacks() { + if (midi_state) { + return; + } + Midi.addOnControlChangeCallback( + this, (midi_callback_ptr_t)&FXPage::onControlChangeCallback_Midi); + + midi_state = true; +} + +void FXPage::remove_callbacks() { + if (!midi_state) { + return; + } + + Midi.removeOnControlChangeCallback( + this, (midi_callback_ptr_t)&FXPage::onControlChangeCallback_Midi); + + midi_state = false; +} + +bool FXPage::handleEvent(gui_event_t *event) { + if (note_interface.is_event(event)) { + uint8_t track = event->source - 128; + if (midi_active_peering.get_device(event->port) != DEVICE_MD) { + return true; + } + } + if (event->mask == EVENT_BUTTON_RELEASED) { + return true; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || + EVENT_PRESSED(event, Buttons.ENCODER2) || + EVENT_PRESSED(event, Buttons.ENCODER3) || + EVENT_PRESSED(event, Buttons.ENCODER4)) { + GUI.setPage(&grid_page); + } + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + page_mode = !(page_mode); + } + + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + } + + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + } + + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + } + + return false; +} diff --git a/avr/cores/megacommand/MCL/FXPage.h b/avr/cores/megacommand/MCL/FXPage.h new file mode 100644 index 000000000..1f1312259 --- /dev/null +++ b/avr/cores/megacommand/MCL/FXPage.h @@ -0,0 +1,53 @@ +/* Justin Mammarella jmamma@gmail.com 2018 */ + +#ifndef FXPAGE_H__ +#define FXPAGE_H__ + +#include "GUI.h" +#include "MCLEncoder.h" + +#define NUM_FX_PAGES 2 +typedef struct fx_param_t { + uint8_t type; + uint8_t param; +} fx_param_t; + +//params_ 2 dimensional array, consisting of [MD_FX_TYPE][MD_FX_PARAM_NUMBER] +// +class FXPage : public LightPage, MidiCallback { +public: + FXPage(Encoder *e1 = NULL, Encoder *e2 = NULL, + Encoder *e3 = NULL, Encoder *e4 = NULL, fx_param_t *params_ = NULL, uint8_t num_of_params_ = 0) + : LightPage(e1, e2, e3, e4) { + + params = params_; + num_of_params = num_of_params_; + } + + bool handleEvent(gui_event_t *event); + bool midi_state = false; + + fx_param_t *params; + uint8_t num_of_params; + + bool page_mode; + uint8_t page_id; + + void display(); + void setup(); + void init(); + void loop(); + void cleanup(); + + void setup_callbacks(); + void remove_callbacks(); + + void onControlChangeCallback_Midi(uint8_t *msg); +}; + +extern MCLEncoder fx_page_param1; +extern MCLEncoder fx_page_param2; +extern MCLEncoder fx_page_param3; +extern MCLEncoder fx_page_param4; + +#endif /* FXPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index e96d7e70c..584aceccf 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -7,7 +7,7 @@ #define RAM_PAGE_B 15 #define WAVD_PAGE 8 #define SOUND 7 - +#define FX_PAGE_A 9 #define LOUDNESS 10 void PageSelectPage::setup() {} @@ -45,6 +45,11 @@ LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { strncpy(str, "RAM 2", 6); r_page = &ram_page_b; break; + case FX_PAGE_A: + if (str) + strncpy(str, "FX CTRL", 8); + r_page = &fx_page_a; + break; #ifdef WAV_DESIGNER case WAVD_PAGE: if (str) From 694b101981bba8a9b5a7ece565045b677728875d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 20:40:31 +1100 Subject: [PATCH 102/469] MD style encoder mostly working --- avr/cores/megacommand/MCL/FXPage.cpp | 14 ++--- avr/cores/megacommand/MCL/MCLGUI.cpp | 77 +++++++++++++++++++++++++--- avr/cores/megacommand/MCL/MCLGUI.h | 70 ++++++++++++++++++++++++- 3 files changed, 147 insertions(+), 14 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index fa91eae4a..fe36e2c95 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -1,6 +1,6 @@ #include "FXPage.h" #include "MCL.h" - +#include "RAMPage.h" #define FX_TYPE 0 #define FX_PARAM 1 #define NUM_OF_ENCODERS 4 @@ -102,9 +102,9 @@ void FXPage::display() { oled_display.setFont(); oled_display.setCursor(0, 0); - oled_display.print("FX"); + oled_display.print("FX "); oled_display.print(page_mode ? 1 : 0); - + oled_display.print(" "); for (uint8_t i = 0; i < NUM_OF_ENCODERS; i++) { uint8_t n = i + ((page_mode ? 1 : 0) * NUM_OF_ENCODERS); @@ -112,12 +112,14 @@ void FXPage::display() { uint8_t fx_type = params[n].type; - oled_display.print(encoders[i]->cur); - oled_display.print(" "); + mcl_gui.draw_encoder(10 + 20 * i, 10, encoders[i]->cur); } - oled_display.display(); + + mcl_gui.draw_encoder(100,10,0); + oled_display.display(); #endif + } void FXPage::onControlChangeCallback_Midi(uint8_t *msg) { uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index cdc0c6876..3c1bf5ac7 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -11,8 +11,7 @@ bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { return text_input_page.return_state; } -bool MCLGUI::wait_for_confirm(const char *title, const char* text) -{ +bool MCLGUI::wait_for_confirm(const char *title, const char *text) { questiondialog_page.init(title, text); GUI.pushPage(&questiondialog_page); while (GUI.currentPage() == &questiondialog_page) { @@ -71,8 +70,8 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display) { oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 2, BLACK); oled_display.setTextColor(BLACK); - //auto len = strlen(title_buf) * 5; - //oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); + // auto len = strlen(title_buf) * 5; + // oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); oled_display.setCursor(s_title_x + 2, s_menu_y + 4); oled_display.println(title_buf); oled_display.setTextColor(WHITE); @@ -121,11 +120,12 @@ void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, } // ref: Design/infobox.png -void MCLGUI::draw_infobox(const char* line1, const char* line2, const int line2_offset) -{ +void MCLGUI::draw_infobox(const char *line1, const char *line2, + const int line2_offset) { auto oldfont = oled_display.getFont(); - oled_display.fillRect(info_x1 - 1, info_y1 - 1, info_w + 3, info_h + 3, BLACK); + oled_display.fillRect(info_x1 - 1, info_y1 - 1, info_w + 3, info_h + 3, + BLACK); oled_display.drawRect(info_x1, info_y1, info_w, info_h, WHITE); oled_display.drawFastHLine(info_x1 + 1, info_y2 + 1, info_w, WHITE); oled_display.drawFastVLine(info_x2 + 1, info_y1 + 1, info_h - 1, WHITE); @@ -149,3 +149,66 @@ void MCLGUI::draw_infobox(const char* line1, const char* line2, const int line2_ oled_display.setFont(oldfont); } +void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { + + auto oldfont = oled_display.getFont(); + + bool vert_flip = false; + bool horiz_flip = false; + uint8_t image_w = 11; + uint8_t image_h = 11; + + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + oled_display.setCursor(x, y + image_h + 1 + 2 + 8); + oled_display.print(value); + + if (value < 32) { + vert_flip = false; + horiz_flip = false; + if (value < 5) { + value = 5; + } + } else if (value < 64) { + vert_flip = false; + horiz_flip = true; + value = 32 - (value - 32); + } else if (value < 96) { + vert_flip = true; + horiz_flip = true; + value = value - 64; + } else if (value < 128) { + vert_flip = true; + horiz_flip = false; + value = 32 - (value - 96); + if (value < 5) { + value = 5; + } + } + + if (value < 5) { + oled_display.drawBitmap(x, y + 2, encoder_small_0, image_w, image_h, WHITE, + vert_flip, horiz_flip); + } else if (value < 11) { + oled_display.drawBitmap(x, y + 2, encoder_small_1, image_w, image_h, WHITE, + vert_flip, horiz_flip); + } else if (value < 16) { + oled_display.drawBitmap(x, y + 2, encoder_small_2, image_w, image_h, WHITE, + vert_flip, horiz_flip); + } else if (value < 22) { + oled_display.drawBitmap(x, y + 2, encoder_small_3, image_w, image_h, WHITE, + vert_flip, horiz_flip); + } else if (value < 27) { + oled_display.drawBitmap(x, y + 2, encoder_small_4, image_w, image_h, WHITE, + vert_flip, horiz_flip); + } else if (value <= 32) { + oled_display.drawBitmap(x, y + 2, encoder_small_5, image_w, image_h, WHITE, + vert_flip, horiz_flip); + } + + oled_display.drawPixel(x + image_w / 2, y, WHITE); + oled_display.drawPixel(x, y + 2 + image_h, WHITE); + oled_display.drawPixel(x + image_w - 1, y + 2 + image_h, WHITE); + +oled_display.setFont(oldfont); +} diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 9add14f25..dc9940324 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -21,7 +21,7 @@ class MCLGUI { void clear_popup(); void draw_popup(const char* title, bool deferred_display = false); void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); - + void draw_encoder(uint8_t x, uint8_t y, uint8_t value); static constexpr uint8_t s_menu_w = 96; static constexpr uint8_t s_menu_h = 24; static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; @@ -43,4 +43,72 @@ class MCLGUI { extern MCLGUI mcl_gui; +// 'encoder_medium_1', 15x15px +const unsigned char encoder_medium_0 [] PROGMEM = { + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, + 0x80, 0x02, 0x80, 0x02, 0x40, 0x04, 0x43, 0x84, 0x23, 0x88, 0x18, 0x30, 0x07, 0xc0 +}; +// 'encoder_medium_2', 15x15px +const unsigned char encoder_medium_1 [] PROGMEM = { + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, + 0x80, 0x02, 0x80, 0x02, 0x40, 0x04, 0x47, 0x04, 0x23, 0x08, 0x18, 0x30, 0x07, 0xc0 +}; +// 'encoder_medium_3', 15x15px +const unsigned char encoder_medium_2 [] PROGMEM = { + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, + 0x80, 0x02, 0x80, 0x02, 0x48, 0x04, 0x46, 0x04, 0x26, 0x08, 0x18, 0x30, 0x07, 0xc0 +}; +// 'encoder_medium_4', 15x15px +const unsigned char encoder_medium_3 [] PROGMEM = { + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, + 0x80, 0x02, 0x90, 0x02, 0x58, 0x04, 0x4c, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0 +}; +// 'encoder_medium_5', 15x15px +const unsigned char encoder_medium_4 [] PROGMEM = { + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, + 0xb0, 0x02, 0xb0, 0x02, 0x50, 0x04, 0x40, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0 +}; +// 'encoder_medium_6', 15x15px +const unsigned char encoder_medium_5 [] PROGMEM = { + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0xa0, 0x02, + 0xb0, 0x02, 0xb0, 0x02, 0x40, 0x04, 0x40, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0 +}; + +// 'encoder0', 11x11px +const unsigned char encoder_small_0 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, + 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder1', 11x11px +const unsigned char encoder_small_1 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x5c, 0x40, + 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder2', 11x11px +const unsigned char encoder_small_2 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x90, 0x20, 0x58, 0x40, + 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder3', 11x11px +const unsigned char encoder_small_3 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0xb0, 0x20, 0x58, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder4', 11x11px +const unsigned char encoder_small_4 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x58, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder5', 11x11px +const unsigned char encoder_small_5 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x50, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder6', 11x11px +const unsigned char encoder_small_6 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xa0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; + + #endif /* MCLGUI_H__ */ From 5c901999f0c7556c106710c8e983805136cda2fd Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 20 Oct 2019 22:14:23 +1100 Subject: [PATCH 103/469] Encoder now rotates smoothly --- art/sprites/encoder6.png | Bin 150 -> 147 bytes avr/cores/megacommand/MCL/MCLGUI.cpp | 31 +++++++++++++++------------ avr/cores/megacommand/MCL/MCLGUI.h | 6 +++--- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/art/sprites/encoder6.png b/art/sprites/encoder6.png index 2e39d58337d121e98afaf829db9deeaf95dd1a2b..1559b2c1b680fb0a7ca02980bd8ce6682e7eef2a 100644 GIT binary patch delta 105 zcmV-v0G9uj0h0lcF=9m z53?Gpl}MWZlVmbUiea|EoC4#c(_neBOa_^R&4Van*kTCTNhtCFayt?GTtz7_00000 LNkvXXu0mjfkCiDf delta 108 zcmV-y0F(cd0hR%fF=bFmL_t&-8J&^A3IHGoL$m+?XUjm?)C}s9&A084jC>R%ftf~w z5VU2bC5TcyWJdM%_G-UxyvF3#5aC-Y>22aB&;BdfYRnxQNUMGb4YXfHXc7Afcn~ZA O0000 122) { value = 122; } value = 32 - (value - 96); - if (value < 5) { - value = 5; - } } - if (value < 5) { + if (value < 4) { oled_display.drawBitmap(x, y + 2, encoder_small_0, image_w, image_h, WHITE, vert_flip, horiz_flip); - } else if (value < 11) { + } else if (value < 9) { oled_display.drawBitmap(x, y + 2, encoder_small_1, image_w, image_h, WHITE, vert_flip, horiz_flip); - } else if (value < 16) { + } else if (value < 14) { oled_display.drawBitmap(x, y + 2, encoder_small_2, image_w, image_h, WHITE, vert_flip, horiz_flip); - } else if (value < 22) { + } else if (value < 19) { oled_display.drawBitmap(x, y + 2, encoder_small_3, image_w, image_h, WHITE, vert_flip, horiz_flip); - } else if (value < 27) { + } else if (value < 24) { oled_display.drawBitmap(x, y + 2, encoder_small_4, image_w, image_h, WHITE, vert_flip, horiz_flip); - } else if (value <= 32) { + } else if (value < 30) { oled_display.drawBitmap(x, y + 2, encoder_small_5, image_w, image_h, WHITE, vert_flip, horiz_flip); + } else { + oled_display.drawBitmap(x, y + 2, encoder_small_6, image_w, image_h, WHITE, + vert_flip, horiz_flip); } oled_display.drawPixel(x + image_w / 2, y, WHITE); oled_display.drawPixel(x, y + 2 + image_h, WHITE); oled_display.drawPixel(x + image_w - 1, y + 2 + image_h, WHITE); -oled_display.setFont(oldfont); + oled_display.setFont(oldfont); } diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index dc9940324..44592024e 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -42,7 +42,7 @@ class MCLGUI { }; extern MCLGUI mcl_gui; - +/* // 'encoder_medium_1', 15x15px const unsigned char encoder_medium_0 [] PROGMEM = { 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, @@ -73,7 +73,7 @@ const unsigned char encoder_medium_5 [] PROGMEM = { 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0xa0, 0x02, 0xb0, 0x02, 0xb0, 0x02, 0x40, 0x04, 0x40, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0 }; - +*/ // 'encoder0', 11x11px const unsigned char encoder_small_0 [] PROGMEM = { 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, @@ -106,7 +106,7 @@ const unsigned char encoder_small_5 [] PROGMEM = { }; // 'encoder6', 11x11px const unsigned char encoder_small_6 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xa0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 }; From e0a132126f246d7307a38bd5ef4454ab0a7d1953 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 20 Oct 2019 19:21:25 +0800 Subject: [PATCH 104/469] removing force inline of heavy or frequently called routines --- avr/cores/megacommand/MCL/MDSeqTrack.h | 2 +- avr/cores/megacommand/MD/MD.h | 4 ++-- avr/cores/megacommand/Midi/MidiSysex.hh | 2 +- avr/cores/megacommand/Midi/MidiUartParent.hh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.h b/avr/cores/megacommand/MCL/MDSeqTrack.h index 03a54e34e..23d321714 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.h +++ b/avr/cores/megacommand/MCL/MDSeqTrack.h @@ -39,7 +39,7 @@ class MDSeqTrack : public MDSeqTrackData { void unmute() { mute_state = SEQ_MUTE_OFF; } ALWAYS_INLINE() void seq(); - ALWAYS_INLINE() void send_trig(); + void send_trig(); ALWAYS_INLINE() void trig_conditional(uint8_t condition); ALWAYS_INLINE() void send_parameter_locks(uint8_t step); diff --git a/avr/cores/megacommand/MD/MD.h b/avr/cores/megacommand/MD/MD.h index 2b0bc8b60..a4704238c 100644 --- a/avr/cores/megacommand/MD/MD.h +++ b/avr/cores/megacommand/MD/MD.h @@ -202,14 +202,14 @@ class MDClass { * * track goes from 0 to 15, velocity from 0 to 127. **/ - ALWAYS_INLINE() void triggerTrack(uint8_t track, uint8_t velocity); + void triggerTrack(uint8_t track, uint8_t velocity); /** * Set the parameter param (0 to 23, or 32 for mute, and 33 for * LEVEL) of the given track (from 0 to 15) to value. * * Uses the channel settings out of the global settings. **/ - ALWAYS_INLINE() void setTrackParam(uint8_t track, uint8_t param, uint8_t value); + void setTrackParam(uint8_t track, uint8_t param, uint8_t value); void setSampleName(uint8_t slot, char *name); /** Send the given sysex buffer to the MachineDrum. **/ diff --git a/avr/cores/megacommand/Midi/MidiSysex.hh b/avr/cores/megacommand/Midi/MidiSysex.hh index b5413535c..7003872cc 100644 --- a/avr/cores/megacommand/Midi/MidiSysex.hh +++ b/avr/cores/megacommand/Midi/MidiSysex.hh @@ -100,7 +100,7 @@ public: put_byte_bank1(sysex_highmem_buf + offset, c); } - ALWAYS_INLINE() uint8_t getByte(uint16_t n) { + uint8_t getByte(uint16_t n) { if (n < maxRecordLen) { // Retrieve data from specified memory buffer if (recordBuf != NULL) { diff --git a/avr/cores/megacommand/Midi/MidiUartParent.hh b/avr/cores/megacommand/Midi/MidiUartParent.hh index 66d957fc0..9a9642ede 100644 --- a/avr/cores/megacommand/Midi/MidiUartParent.hh +++ b/avr/cores/megacommand/Midi/MidiUartParent.hh @@ -251,7 +251,7 @@ public: sendMessage(msg[0], msg[1], msg[2]); } - ALWAYS_INLINE() void sendCC(uint8_t channel, uint8_t cc, uint8_t value) { + void sendCC(uint8_t channel, uint8_t cc, uint8_t value) { #ifdef MIDI_VALIDATE if ((channel >= 16) || (note >= 128) || (velocity >= 128)) return; From 739e363516ed25141107cae4f9860d627a90b212 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 21 Oct 2019 16:23:22 +1100 Subject: [PATCH 105/469] Add inline versions of send_trig and setTrackParam --- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 62 +++++++++++++----------- avr/cores/megacommand/MCL/MDSeqTrack.h | 1 + avr/cores/megacommand/MD/MD.cpp | 5 ++ avr/cores/megacommand/MD/MD.h | 2 + 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index a140220aa..f301117ac 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -136,14 +136,14 @@ void MDSeqTrack::send_parameter_locks(uint8_t step) { uint8_t c; bool lock_mask_step = IS_BIT_SET64(lock_mask, step); bool pattern_mask_step = IS_BIT_SET64(pattern_mask, step); + uint8_t send_param = 255; if (lock_mask_step && pattern_mask_step) { for (c = 0; c < 4; c++) { if (locks[c][step] > 0) { - MD.setTrackParam(track_number, locks_params[c] - 1, locks[c][step] - 1); + send_param = locks[c][step] - 1; } else if (locks_params[c] > 0) { - MD.setTrackParam(track_number, locks_params[c] - 1, - locks_params_orig[c]); + send_param = locks_params_orig[c]; } } } @@ -151,7 +151,7 @@ void MDSeqTrack::send_parameter_locks(uint8_t step) { else if (lock_mask_step) { for (c = 0; c < 4; c++) { if (locks[c][step] > 0) { - MD.setTrackParam(track_number, locks_params[c] - 1, locks[c][step] - 1); + send_param = locks[c][step] - 1; } } } @@ -160,15 +160,18 @@ void MDSeqTrack::send_parameter_locks(uint8_t step) { for (c = 0; c < 4; c++) { if (locks_params[c] > 0) { - - MD.setTrackParam(track_number, locks_params[c] - 1, - locks_params_orig[c]); + send_param = locks_params_orig[c]; } } } + if (send_param != 255) { + MD.setTrackParam_inline(track_number, locks_params[c] - 1, send_param); + } } -void MDSeqTrack::send_trig() { +void MDSeqTrack::send_trig() { send_trig_inline(); } + +void MDSeqTrack::send_trig_inline() { mixer_page.disp_levels[track_number] = MD.kit.levels[track_number]; if (MD.kit.trigGroups[track_number] < 16) { mixer_page.disp_levels[MD.kit.trigGroups[track_number]] = @@ -176,73 +179,78 @@ void MDSeqTrack::send_trig() { } MD.triggerTrack(track_number, 127); } + void MDSeqTrack::trig_conditional(uint8_t condition) { + bool send_trig = false; switch (condition) { case 0: - send_trig(); + send_trig = true; break; case 1: - send_trig(); + send_trig = true; break; case 2: if (!IS_BIT_SET(iterations, 0)) { - send_trig(); + send_trig = true; } case 4: if ((iterations == 4) || (iterations == 8)) { - send_trig(); + send_trig = true; } case 8: if ((iterations == 8)) { - send_trig(); + send_trig = true; } break; case 3: if ((iterations == 3) || (iterations == 6)) { - send_trig(); + send_trig = true; } break; case 5: if (iterations == 5) { - send_trig(); + send_trig = true; } break; case 7: if (iterations == 7) { - send_trig(); + send_trig = true; } break; case 9: if (get_random_byte() <= 13) { - send_trig(); + send_trig = true; } break; case 10: if (get_random_byte() <= 32) { - send_trig(); + send_trig = true; } break; case 11: if (get_random_byte() <= 64) { - send_trig(); + send_trig = true; } break; case 12: if (get_random_byte() <= 96) { - send_trig(); + send_trig = true; } break; case 13: if (get_random_byte() <= 115) { - send_trig(); + send_trig = true; } break; case 14: if (!IS_BIT_SET64(oneshot_mask, step_count)) { SET_BIT64(oneshot_mask, step_count); - send_trig(); + send_trig = true; } } + if (send_trig) { + send_trig_inline(); + } } uint8_t MDSeqTrack::get_track_lock(uint8_t step, uint8_t track_param) { @@ -423,13 +431,13 @@ void MDSeqTrack::clear_locks(bool reset_params) { } lock_mask = 0; if (reset_params) { - for (uint8_t c = 0; c < 4; c++) { - if (locks_params_buf[c] > 0) { - MD.setTrackParam(track_number, locks_params_buf[c] - 1, - locks_params_orig[c]); + for (uint8_t c = 0; c < 4; c++) { + if (locks_params_buf[c] > 0) { + MD.setTrackParam(track_number, locks_params_buf[c] - 1, + locks_params_orig[c]); + } } } - } } void MDSeqTrack::clear_track(bool locks, bool reset_params) { diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.h b/avr/cores/megacommand/MCL/MDSeqTrack.h index 23d321714..dd073ccb3 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.h +++ b/avr/cores/megacommand/MCL/MDSeqTrack.h @@ -40,6 +40,7 @@ class MDSeqTrack : public MDSeqTrackData { ALWAYS_INLINE() void seq(); void send_trig(); + ALWAYS_INLINE() void send_trig_inline(); ALWAYS_INLINE() void trig_conditional(uint8_t condition); ALWAYS_INLINE() void send_parameter_locks(uint8_t step); diff --git a/avr/cores/megacommand/MD/MD.cpp b/avr/cores/megacommand/MD/MD.cpp index 939443a9e..257fb96b6 100644 --- a/avr/cores/megacommand/MD/MD.cpp +++ b/avr/cores/megacommand/MD/MD.cpp @@ -161,7 +161,12 @@ void MDClass::triggerTrack(uint8_t track, uint8_t velocity) { } } + void MDClass::setTrackParam(uint8_t track, uint8_t param, uint8_t value) { + setTrackParam_inline(track,param,value); +} + +void MDClass::setTrackParam_inline(uint8_t track, uint8_t param, uint8_t value) { uint8_t channel = track >> 2; uint8_t b = track & 3; diff --git a/avr/cores/megacommand/MD/MD.h b/avr/cores/megacommand/MD/MD.h index a4704238c..f2f011374 100644 --- a/avr/cores/megacommand/MD/MD.h +++ b/avr/cores/megacommand/MD/MD.h @@ -209,6 +209,8 @@ class MDClass { * * Uses the channel settings out of the global settings. **/ + + ALWAYS_INLINE() void setTrackParam_inline(uint8_t track, uint8_t param, uint8_t value); void setTrackParam(uint8_t track, uint8_t param, uint8_t value); void setSampleName(uint8_t slot, char *name); From f757f864c3351786c2ada5a4ecd76e8fc596d049 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 21 Oct 2019 16:33:01 +1100 Subject: [PATCH 106/469] inline puts --- avr/cores/megacommand/Midi/MidiUartParent.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/Midi/MidiUartParent.hh b/avr/cores/megacommand/Midi/MidiUartParent.hh index 9a9642ede..d7a08cfaa 100644 --- a/avr/cores/megacommand/Midi/MidiUartParent.hh +++ b/avr/cores/megacommand/Midi/MidiUartParent.hh @@ -80,7 +80,7 @@ public: virtual void initSerial() { running_status = 0; } - virtual void puts(uint8_t *data, uint16_t cnt) { + ALWAYS_INLINE() virtual void puts(uint8_t *data, uint16_t cnt) { while (cnt--) m_putc(*(data++)); } From 2efc1d79b60a42856b30fbaa21cee70d0e3e41e7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 21 Oct 2019 20:13:14 +1100 Subject: [PATCH 107/469] Fix bad implementation of send_parameter_locks --- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index f301117ac..249f0209a 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -145,6 +145,7 @@ void MDSeqTrack::send_parameter_locks(uint8_t step) { } else if (locks_params[c] > 0) { send_param = locks_params_orig[c]; } + MD.setTrackParam_inline(track_number, locks_params[c] - 1, send_param); } } @@ -152,6 +153,7 @@ void MDSeqTrack::send_parameter_locks(uint8_t step) { for (c = 0; c < 4; c++) { if (locks[c][step] > 0) { send_param = locks[c][step] - 1; + MD.setTrackParam_inline(track_number, locks_params[c] - 1, send_param); } } } @@ -161,12 +163,10 @@ void MDSeqTrack::send_parameter_locks(uint8_t step) { for (c = 0; c < 4; c++) { if (locks_params[c] > 0) { send_param = locks_params_orig[c]; + MD.setTrackParam_inline(track_number, locks_params[c] - 1, send_param); } } } - if (send_param != 255) { - MD.setTrackParam_inline(track_number, locks_params[c] - 1, send_param); - } } void MDSeqTrack::send_trig() { send_trig_inline(); } From 74727d314e4762c6aa2cd0b8838bd1e882bef34a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 21 Oct 2019 20:49:01 +1100 Subject: [PATCH 108/469] ISR block when "sending" sysex. Note: this is not actually sending but copying bytes to the MIDI TX buffer. This should prevent note drop out of the sequencer as MIDI messages cannot be interleaved within sysex. --- avr/cores/megacommand/MD/MD.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avr/cores/megacommand/MD/MD.cpp b/avr/cores/megacommand/MD/MD.cpp index 257fb96b6..eb7e65ef2 100644 --- a/avr/cores/megacommand/MD/MD.cpp +++ b/avr/cores/megacommand/MD/MD.cpp @@ -194,10 +194,13 @@ void MDClass::setTrackParam_inline(uint8_t track, uint8_t param, uint8_t value) // 0x5E, 0x5D, 0x5F, 0x60 void MDClass::sendSysex(uint8_t *bytes, uint8_t cnt) { + USE_LOCK(); + SET_LOCK(); MidiUart.m_putc(0xF0); MidiUart.sendRaw(machinedrum_sysex_hdr, sizeof(machinedrum_sysex_hdr)); MidiUart.sendRaw(bytes, cnt); MidiUart.m_putc(0xf7); + CLEAR_LOCK(); } void MDClass::setSampleName(uint8_t slot, char *name) { From c9f219006943784594129f360e9d939ef230073e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 22 Oct 2019 22:23:51 +1100 Subject: [PATCH 109/469] Add Pages::last_used_clock[] to store times that encoder was last rotated --- avr/cores/megacommand/GUI/Pages.cpp | 1 + avr/cores/megacommand/GUI/Pages.hh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/avr/cores/megacommand/GUI/Pages.cpp b/avr/cores/megacommand/GUI/Pages.cpp index 1ca412e7f..4a78c470a 100644 --- a/avr/cores/megacommand/GUI/Pages.cpp +++ b/avr/cores/megacommand/GUI/Pages.cpp @@ -46,6 +46,7 @@ void LightPage::update() { GUI.screen_saver = false; clock_minutes = 0; minuteclock = 0; + last_used_clock[i] = slowclock; redisplay = true; } } diff --git a/avr/cores/megacommand/GUI/Pages.hh b/avr/cores/megacommand/GUI/Pages.hh index 377b59f7a..02a54e9df 100644 --- a/avr/cores/megacommand/GUI/Pages.hh +++ b/avr/cores/megacommand/GUI/Pages.hh @@ -166,6 +166,8 @@ public: PageContainer *parent; Encoder *encoders[GUI_NUM_ENCODERS]; + static uint16_t last_used_clock[GUI_NUM_ENCODERS]; + LightPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) { setEncoders(e1, e2, e3, e4); From 7b9b11313e24ae67f579e0fb845eacf39dcab52d Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Wed, 23 Oct 2019 13:16:21 +0800 Subject: [PATCH 110/469] add sequencer design files --- design/Sequencer.gif | Bin 0 -> 8470 bytes design/Sequencer.png | Bin 0 -> 1145 bytes design/Sequencer.xcf | Bin 0 -> 37563 bytes design/Sequencer_anim.xcf | Bin 0 -> 105213 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 design/Sequencer.gif create mode 100644 design/Sequencer.png create mode 100644 design/Sequencer.xcf create mode 100644 design/Sequencer_anim.xcf diff --git a/design/Sequencer.gif b/design/Sequencer.gif new file mode 100644 index 0000000000000000000000000000000000000000..4e637c2d1a6e6f668194e7185a009df525426055 GIT binary patch literal 8470 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 LNkvXXu0mjfBGnNp literal 0 HcmV?d00001 diff --git a/design/Sequencer.xcf b/design/Sequencer.xcf new file mode 100644 index 0000000000000000000000000000000000000000..a30c9b0ac53b0505efd1403b06e1973d7a554d75 GIT binary patch literal 37563 zcmeHQZEPIJdEUL_k@}=0%9dlrl19`=B+F-gxmIGy(wPcO+j1;bvYpmZ9Z}+ux|qC^ zNy-w{xVN(9#A+?eZPEZmTR?S#_D9gPC<-L~0+gg^{v`d8{%BFOXi>K?;^a?~!r$@s zdFGwjom(Dt{5nUZcF1?$ciwq-c6N5=*>`7WZ~IGwr_|XKed?KQ+qZiN0Z4%uz5-eX z@_j_hLG$)QMMAg<)Z%&BTfkR{?9mSC*1N$On9d#Q89Av_u-2OM;8`OtBJWb3SGWSn z^D;?!ujsXnk+n@EZz*_@O)O`+67oJ<4)jLWHj%v5vwFWPmNQAY@BpicI>W}u+9r}w zeqZ-W{zPSPuvi{JT3VEK3hnTZeo0bN9`iuGgp;E80 z_PtHRBclUFHC!1e^=@9UsjvS;WuP*oHffe0>ObD2w%xf+hdRP`leHO}9J*Cm*PnSz0s#HF4vN+7`D}8;##gV9j+l=IwQN&3Pw z!e2Zi{H1$@UwTXUW!;@bj^Y540{Ngtj~;%o`-tj3eDE>dzg9uV&lu-njQL+=pZC z2CsVGtS{vPY?(az@zS-e5m&#SOJ4gq;Y-Zbz^ji3FfS^mPck)X~sJM|gr`w6Z;aUXka0o2%MxQnJPD$rk;OQ;ZyU>+!DgpUi? zae!EX3@WVn0%U|+9}}+Q0I|}Nx8hHP-||)ADJuM8P$Bl=BYa3vVMbBm>2fMh73pu14;erpF*)jw95)UvA zo!^Y*BrbIRyX0;7S{)mxI^P%lx_51U`fQ>(&nB)h{%an=q~1bvyoK%T;4Uf<5gMTa zx0x-d@LQ~G95((_EGJQ6<4+}T^SA2QK=rW>(QmR-IO%f zg9_W&(Op#F+JQ!>Fq?dZ-;|MT%GFa?G~PbLUTc(F&w0zFjJsVyTEA|JGhGq9PM)m6@0UV)Q;nwTvlmA z06~F_tFTkgy?Wl=Tsmn(06|G&=Mmv|{j%^I9u;yw;0=U7!k7ym8>XjzOEFP|NO5AO zaH7U}81wX!E1Mg^gb$n9;yk=)hqUlr*I56!%dCIAXS1_S09i?Tms$V#@#qO?I{Vn1 z*~b(Vyqa0oxIGfmjIEBY0fjNX178(r1PUA@vjr5sC@f~(oAS3ME zD_q9`Vx=W-w^>TLCwlyug2GILLfq-a3z$fZZPEvo(|k}_0}5l_nBe0fl`RAS3XF+b9H(l%)23Re0f5;WzvZMZM<`u`V~c+lQ&ZL3;bpl-q|f zFG}!5$GHRwFW3PreE(@D9Y9tf0}A`ijKuu|(Af|`P?FgH*TO#;Er4i_rx3jhnS2Fk zF27mHtG)AP=tMjO*CzEACiIf!eeA&nkbTk~Kvp0hRO2m-c~_zx7IO(0j0b?D(xKaZ zcOrmFfebKo|0QIEht3Pvae!E9$?N`s@CTwb5Hk%7_KK-+TX`d)!c^a?OudDscnf2e zKy%K1E`h@HGLUV2;E$~Y9)J|cfWiUulNgVD1v(o72uczMUKjrGYr<#p7Q(`T4388P zCIb_xr!d2w!VdOt_dz9ni@5{}&)ESi{9xq%BOQPg$biB@v;O(;IP0Gen)T0z-o`dM z%!H><3kNAEOa=~8PoXKE!kFdH%h22B5-6NErbd(mNfpimDEQcy@i!Q4AKvn+8>Nk}vHSh7_wbY!P?Jp``zpG#n17A5Mj zi|Et;W+m_dq(BA?KK%h?geSfyT*m=or6uoI*iHz)daLm1+&0uaf_7)iD5rM~lR*TG zkMwEeG@nK;j2At#Z@Yl79O*`Y;KOFNtWO9~S0{YtM@}MutUv}3j+%9kPyQElHUtoq zB#v$s{%d*R(*cC^=NxONPbY%_N&L)sDJD#RCZYAfBM0Rnd^3kXp@U1H%#icA^1;e< zx~VKr}ReR*`qEA*IRec2R6)KsHhZRJR+*&uCW+xYR> zNZYu!1I>zlECZfhe~HFw89&RzEQZ5ekXHZ?HUd(9L?{{amm%u#ubTC}b~M5Y(4uf5 zHXvArUXHlIGFEtRX+uUior#P)ymycmm06z9p>1`!HKtoM^7*=5qav4>J>G<-=Px>w zlL*!DCiEt4uwgm|PLXr4BXBMZ-)9vl@&L$NV9w1bXX9)pEfL7X#DY4JbrFa>ivq~y zKy~?&q{^B!DnNds+SWoKn?K8FD}h#!B#aqb*NuE8WSMox? zSw=^Wo(xU|hf4@$JRIIOdTpD**ocM>+#hQubDvTo#$MHb9dDG*$z8Qv^;U&z$DykK zs#nGIfJ-<91(4f?dD?u?f=3QM`tbe(YMov+Wq#LSMmn=D9ec2yM>+DOIy1J*fTr4+ zw_w{f&b}eqaOyv-1Rj9cDyF_qv;GLfUlgw605NS4o?a__SdV_n)m#A`dk@abR-ouw zeHpNwoQ|U}Zg4JK<^4o=Xxpnf24wmJ{T`n4>N@$f;YCK($&0tmAm0uam~%ETs|T9d zcU!m#w-=j;Y?-Ii3Tv<-f9s3-aYt+T3Pi~$|Lt4?HueK zDD|HlQJ*UImQD{I?KHW=rE^8(?)=2g9g(iDGF0pzs+=zOB4=Hn4#)(G?%B#B_a@i` zc2irVd}S`$Uu_+L)QR%#PR(I;;-haoDAjqSr#ze=E)JFYtS-;8)ZIJm*XqPlYvprr zr%Gpw1N@R)PkE%&Gf?UoR^Q8iHP8)1=^v-!bFZ&CH3V*D;2 z6=zDl_@dv=UGY&+d$Ooc;11{DsMa6Iiw=v&!SCzwpx85<@XYH@!j36(B{*Y85KU|z z#4{hY+f=>(jL<$4-s1hA3-2FkkaX}Gu~;!HzL`wPQW=n(1b6i&ihn~qah>-Sl2wXW}(I)zPg3Ip>V1P4px_?gji{v%o|k>eDe-)8ky z@5i;!$4N;S`XnvTcgvW(TIlnyz%9Jq%X8h<>(VtelU;BPd6HcGBi>w3_{aM3#&)VV z$n23$c>W)qL;zWVOizCP4af)=J||qq0b->k?}A)VybI=c%dB6>i*Pk_+_@yzOWDgG zFM;)_>P=|6ay{z8bF}o)p`H^@Jy`CmsJz0xCac)!ywRr7l1!G(<1}sJw$J4JdCa`O zhjONR-gul&c;PpkL;#ujrj9SXDtRwHC0xe=VpGNo-xmFgpAnAp=HH6Dr)?T)I5kMO zEKccVQy~;wKuc88zwi*}i#4Amaa+zs?gsUO^wXnVQL(Kk_D)ku8+hKyVajaKimW{_ zt+xDGTC*v)8Qn^@KN^$IB(qpuvCeywGn)MK@SKIu60_qe`3Mlt60N0dPNiNp%7>6y zz2G4m?sVi%tkUp3&S|$`N!_Aw2Z-~`q>WmZM~)k=DT{rz9_$n zJGCC95dbi=(a`4>vt{UW7S{ixvp1pF|W74wa>ovodLm8m}vO}0t zrFQ;p-jU+dhcI~z)pa$12^Q_3sb0J9=K;mx(mk>4+2c3Y?n{r?$%E=r+3JbgmKG9? zvs^YE#YASCo%3QT#Ja(mG(LnYgBO6TL%FaE{+B!eVO3`WfLYG<;Cp3QkvP2kZY)Pl zU>*frHo$uMWE~r*F7Fln<)b#g>R0iEvD$VOQ~Y(L`Gv4*2wN0DcH{(*6^Olav3@rx zf4tP+FVD6bH9xkQ^7W59obXh(S#ZMrO?CL_A9-T|5?)DO{}l9Q_`G7Sf4}qR#+WPs zDGF;#!rwNttO#VDhDYiWaCaH?0~YG+7e-ume&H#p^UKUMGB zsMU8&9hW=2>bFcSC-<$yKJ4+-axSK=IX1w@csj1!{l(e}2G$-ye5S0v>V1bNz5vo| GUj83=ctY6# literal 0 HcmV?d00001 diff --git a/design/Sequencer_anim.xcf b/design/Sequencer_anim.xcf new file mode 100644 index 0000000000000000000000000000000000000000..257dd13165f315ef1b03a405d66c9c1fbd5b3ac9 GIT binary patch literal 105213 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 g%;Ffqs}lG0Q Date: Wed, 23 Oct 2019 13:29:43 +0800 Subject: [PATCH 111/469] annotated --- design/Sequencer_large.gif | Bin 0 -> 37835 bytes design/Sequencer_large.xcf | Bin 0 -> 213787 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 design/Sequencer_large.gif create mode 100644 design/Sequencer_large.xcf diff --git a/design/Sequencer_large.gif b/design/Sequencer_large.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2bfd866c06003bb17e7f2c8c586d6fe23d35dbb GIT binary patch literal 37835 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%0D|!Un@EwdU5^p*` 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 C)6X;j literal 0 HcmV?d00001 diff --git a/design/Sequencer_large.xcf b/design/Sequencer_large.xcf new file mode 100644 index 0000000000000000000000000000000000000000..5b8b06e6205928add3120741e3da8b06e4353349 GIT binary patch literal 213787 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

40^U=dn!I&9ZO{kaZU! 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 Date: Wed, 23 Oct 2019 13:31:11 +0800 Subject: [PATCH 112/469] cleanup --- design/Sequencer_large.gif | Bin 37835 -> 0 bytes design/Sequencer_large.png | Bin 0 -> 52478 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 design/Sequencer_large.gif create mode 100644 design/Sequencer_large.png diff --git a/design/Sequencer_large.gif b/design/Sequencer_large.gif deleted file mode 100644 index a2bfd866c06003bb17e7f2c8c586d6fe23d35dbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37835 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%0D|!Un@EwdU5^p*` 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 C)6X;j diff --git a/design/Sequencer_large.png b/design/Sequencer_large.png new file mode 100644 index 0000000000000000000000000000000000000000..3325613fc1725657bae5aee07d037580d74f7dc9 GIT binary patch literal 52478 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 z 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 Date: Wed, 23 Oct 2019 17:10:36 +1100 Subject: [PATCH 113/469] check-in --- avr/cores/megacommand/MCL/FXPage.cpp | 17 +++-- avr/cores/megacommand/MCL/MCLGUI.cpp | 99 +++++++++++++++++++++++----- avr/cores/megacommand/MCL/MCLGUI.h | 10 +++ 3 files changed, 102 insertions(+), 24 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index fe36e2c95..cf5ea7607 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -4,7 +4,7 @@ #define FX_TYPE 0 #define FX_PARAM 1 #define NUM_OF_ENCODERS 4 - +#define INTERPOLATE void FXPage::setup() { DEBUG_PRINT_FN(); } void FXPage::init() { @@ -64,13 +64,16 @@ void FXPage::loop() { uint8_t val; //Interpolation. +#ifdef INTERPOLATE for (val = encoders[i]->old; val < encoders[i]->cur; val++) { MD.sendFXParam(fx_param, val, fx_type); } for (val = encoders[i]->old; val > encoders[i]->cur; val--) { MD.sendFXParam(fx_param, val, fx_type); } - +#else + MD.sendFXParam(fx_param, encoders[i]->cur, fx_type); +#endif } } } @@ -111,10 +114,9 @@ void FXPage::display() { uint8_t fx_param = params[n].param; uint8_t fx_type = params[n].type; - - mcl_gui.draw_encoder(10 + 20 * i, 10, encoders[i]->cur); + mcl_gui.draw_encoder(10 + 20 * i, 10, encoders[i], str, show_value); } - + mcl_gui.draw_encoder(100,10,0); oled_display.display(); @@ -169,7 +171,7 @@ bool FXPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { - GUI.setPage(&grid_page); +// GUI.setPage(&grid_page); } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { page_mode = !(page_mode); @@ -180,8 +182,9 @@ bool FXPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON4)) { } - if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + GUI.setPage(&page_select_page); + return true; } return false; diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index b66b628c9..e33f122e3 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -151,19 +151,13 @@ void MCLGUI::draw_infobox(const char *line1, const char *line2, void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { - auto oldfont = oled_display.getFont(); bool vert_flip = false; bool horiz_flip = false; uint8_t image_w = 11; uint8_t image_h = 11; - oled_display.setFont(&TomThumb); - oled_display.setTextColor(WHITE); - oled_display.setCursor(x, y + image_h + 1 + 2 + 8); - oled_display.print(value); - - //Scale encoder values to 123. encoder animation does not start and stop on 0. + //Scale encoder values to 123. encoder animation does not start and stop on 0. value = (uint8_t) ((float) value * .95); value += 4; @@ -187,31 +181,102 @@ void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { } if (value < 4) { - oled_display.drawBitmap(x, y + 2, encoder_small_0, image_w, image_h, WHITE, + oled_display.drawBitmap(x, y, encoder_small_0, image_w, image_h, WHITE, vert_flip, horiz_flip); } else if (value < 9) { - oled_display.drawBitmap(x, y + 2, encoder_small_1, image_w, image_h, WHITE, + oled_display.drawBitmap(x, y, encoder_small_1, image_w, image_h, WHITE, vert_flip, horiz_flip); } else if (value < 14) { - oled_display.drawBitmap(x, y + 2, encoder_small_2, image_w, image_h, WHITE, + oled_display.drawBitmap(x, y, encoder_small_2, image_w, image_h, WHITE, vert_flip, horiz_flip); } else if (value < 19) { - oled_display.drawBitmap(x, y + 2, encoder_small_3, image_w, image_h, WHITE, + oled_display.drawBitmap(x, y, encoder_small_3, image_w, image_h, WHITE, vert_flip, horiz_flip); } else if (value < 24) { - oled_display.drawBitmap(x, y + 2, encoder_small_4, image_w, image_h, WHITE, + oled_display.drawBitmap(x, y, encoder_small_4, image_w, image_h, WHITE, vert_flip, horiz_flip); } else if (value < 30) { - oled_display.drawBitmap(x, y + 2, encoder_small_5, image_w, image_h, WHITE, + oled_display.drawBitmap(x, y, encoder_small_5, image_w, image_h, WHITE, vert_flip, horiz_flip); } else { - oled_display.drawBitmap(x, y + 2, encoder_small_6, image_w, image_h, WHITE, + oled_display.drawBitmap(x, y, encoder_small_6, image_w, image_h, WHITE, vert_flip, horiz_flip); } +} + +void MCLGUI::draw_encoder(uint8_t x, uint8_t y, Encoder *encoder) { + draw_encoder(x , y, encoder->value); +} + +bool MCLGUI::show_encoder_value(Encoder *encoder) { + uint8_t match = 255; + for (uint8_t i = 0; i < GUI_NUM_ENCODERS && match != 255; i++) { + if (GUI.page->encoders[i] == encoder) { + match = i; + } + } + if (match != 255) { + if (clock_diff(GUI.page->last_used_clock[match], slowclock) > SHOW_VALUE_TIMEOUT)) { show_value = true; } + } + +} + +void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name) { + bool show_value = show_encoder_value(encoder); + draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); +} + +void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value) { + + auto oldfont = oled_display.getFont(); + + uint8_t image_w = 11; + uint8_t image_h = 11; + + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + + //Find the encoder number matching the encoder. + if (show_value) { + oled_display.setCursor(x, y + image_h + 1 + 2 + 8); + oled_display.print(value); + } + + oled_display.setCursor(x, y); + oled_display.print(name); + + y += 10; + + draw_encoder(x, y, value); + + oled_display.drawPixel(x + image_w / 2, y - 2, WHITE); + oled_display.drawPixel(x, y + image_h, WHITE); + oled_display.drawPixel(x + image_w - 1, y + image_h + 2, WHITE); - oled_display.drawPixel(x + image_w / 2, y, WHITE); - oled_display.drawPixel(x, y + 2 + image_h, WHITE); - oled_display.drawPixel(x + image_w - 1, y + 2 + image_h, WHITE); oled_display.setFont(oldfont); } + + +void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name) { + bool show_value = show_encoder_value(encoder); + draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); +} + + +void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value) { + + if (show_value) { + oled_display.setCursor(x, y); + oled_display.print(value); + } + else { + oled_display.setCursor(x, y); + oled_display.print(name); + } + y += 6; + + draw_encoder(x, y, value); + + +} diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 44592024e..02d84d3f9 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -21,7 +21,17 @@ class MCLGUI { void clear_popup(); void draw_popup(const char* title, bool deferred_display = false); void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); + void draw_encoder(uint8_t x, uint8_t y, uint8_t value); + void draw_encoder(uint8_t x, uint8_t y, Encoder *encoder); + + bool show_encoder_value(Encoder *encoder); + + void draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); + void draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); + void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name); + void draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); + static constexpr uint8_t s_menu_w = 96; static constexpr uint8_t s_menu_h = 24; static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; From 626175464738426e7deb0e6c21d0d0328af4adb6 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 00:27:24 +0800 Subject: [PATCH 114/469] seqpage info pane upper part --- avr/cores/megacommand/MCL/GridPage.cpp | 8 +- avr/cores/megacommand/MCL/SeqPage.cpp | 139 ++++++++++++++++------- avr/cores/megacommand/MCL/SeqPage.h | 22 ++++ avr/cores/megacommand/Midi/MidiClock.cpp | 4 + avr/cores/megacommand/Midi/MidiClock.h | 2 +- 5 files changed, 126 insertions(+), 49 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 6a9bc85ee..54645dc0a 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -451,8 +451,7 @@ void GridPage::display_grid() { oled_display.setTextColor(WHITE, BLACK); } - if ((MidiClock.step_counter == 2 || MidiClock.step_counter == 3) && - MidiClock.state == 2 && row_idx == active_slots[track_idx]) { + if (MidiClock.getBlinkHint() && row_idx == active_slots[track_idx]) { // blink, don't print blink = true; } else if (model == 0) { @@ -486,7 +485,6 @@ void GridPage::display_grid() { #endif } void GridPage::display_slot_menu() { - uint8_t x_offset = 43; uint8_t y_offset = 8; grid_slot_page.draw_menu(1, y_offset, 39); // grid_slot_page.draw_scrollbar(36); @@ -494,8 +492,6 @@ void GridPage::display_slot_menu() { void GridPage::display_oled() { #ifdef OLED_DISPLAY - uint8_t x_offset = 43; - uint8_t y_offset = 8; oled_display.clearDisplay(); oled_display.setTextWrap(false); @@ -518,7 +514,7 @@ void GridPage::display() { #ifdef OLED_DISPLAY display_oled(); return; -#endif; +#endif // Rendering code for HD44780 below char str[3] = " "; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index f0b4fe1ab..d45b89a7c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -102,7 +102,6 @@ bool SeqPage::handleEvent(gui_event_t *event) { ignore_button_release = 2; } return true; - //} } // TI + SHIFT2 = select track. @@ -167,12 +166,9 @@ bool SeqPage::handleEvent(gui_event_t *event) { trackselect_enc.cur = trackselect_enc.old = last_ext_track; } #endif - } - else if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + } else if (EVENT_RELEASED(event, Buttons.BUTTON3)) { encoders[3] = &seq_param4; } - - /* #ifdef OLED_DISPLAY @@ -240,24 +236,19 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { (MidiClock.state == 2)) { str[i] = ' '; } else { - if (IS_BIT_SET64(active_track.lock_mask, - i + offset)) { + if (IS_BIT_SET64(active_track.lock_mask, i + offset)) { str[i] = 'x'; } - if (IS_BIT_SET64(active_track.pattern_mask, - i + offset) && - !IS_BIT_SET64(active_track.lock_mask, - i + offset)) { + if (IS_BIT_SET64(active_track.pattern_mask, i + offset) && + !IS_BIT_SET64(active_track.lock_mask, i + offset)) { #ifdef OLED_DISPLAY str[i] = (char)0xF8; #else str[i] = (char)165; #endif } - if (IS_BIT_SET64(active_track.pattern_mask, - i + offset) && - IS_BIT_SET64(active_track.lock_mask, - i + offset)) { + if (IS_BIT_SET64(active_track.pattern_mask, i + offset) && + IS_BIT_SET64(active_track.lock_mask, i + offset)) { #ifdef OLED_DISPLAY str[i] = (char)2; #else @@ -493,9 +484,9 @@ void SeqPage::loop() { track = last_ext_track; } #endif - if(plus && track < 15) { + if (plus && track < 15) { ++track; - }else if (!plus && track > 0) { + } else if (!plus && track > 0) { --track; } select_track(midi_device, track); @@ -503,8 +494,8 @@ void SeqPage::loop() { } } +#ifndef OLED_DISPLAY void SeqPage::display() { - for (uint8_t i = 0; i < 2; i++) { for (int j = 0; j < 16; j++) { if (GUI.lines[i].data[j] == 0) { @@ -516,40 +507,104 @@ void SeqPage::display() { GUI.lines[i].changed = false; } GUI.setLine(GUI.LINE1); -#ifdef OLED_DISPLAY +} +#else +// ref: design/Sequencer.png +void SeqPage::display() { - // Display current active track - oled_display.fillRect(110, 0, 18, 32, BLACK); - oled_display.setCursor(110, 0); - oled_display.print("TRK"); - oled_display.setCursor(110, 8); + oled_display.fillRect(pane_x1, 0, pane_w, 32, BLACK); oled_display.setFont(&TomThumb); - if (midi_device == DEVICE_MD) { - oled_display.print("MD "); - oled_display.print(last_md_track + 1); - } + + bool is_md = (midi_device == DEVICE_MD); #ifdef EXT_TRACKS - else if (midi_device == DEVICE_A4) { - oled_display.print("A4 "); - oled_display.print(last_ext_track + 1); + bool ext_is_a4 = (seq_extstep_page.midi_device == DEVICE_A4); +#else + bool ext_is_a4 = false; +#endif + uint8_t track_id = last_md_track; + if (!is_md) + { + track_id = last_ext_track; } - else { - oled_display.print("MI "); - oled_display.print(last_ext_track + 1); + track_id += 1; + + // draw MD/EXT label + if (is_md) { + oled_display.fillRect(label_x, label_md_y, label_w, label_h, WHITE); + oled_display.setCursor(label_x + 1, label_md_y + 6); + oled_display.setTextColor(BLACK); + oled_display.print("MD"); + oled_display.setTextColor(WHITE); + } else { + oled_display.setCursor(label_x + 1, label_md_y + 6); + oled_display.setTextColor(WHITE); + oled_display.print("MD"); + oled_display.fillRect(label_x, label_ex_y, label_w, label_h, WHITE); + oled_display.setTextColor(BLACK); + } + oled_display.setCursor(label_x + 1, label_ex_y + 6); + if (ext_is_a4) { + oled_display.print("A4"); + } else { + oled_display.print("MI"); } -#endif - if (show_track_menu) { - uint8_t x_offset = 43; - uint8_t y_offset = 8; - oled_display.setFont(&TomThumb); - oled_display.fillRect(84, 0, 40, 32, BLACK); - track_menu_page.draw_menu(86, y_offset, 39); + // draw current active track + oled_display.setTextColor(WHITE); + oled_display.setFont(&Elektrothic); + oled_display.setCursor(trackid_x, trackid_y); + oled_display.print(track_id); + + // draw stop/play/rec state + if (recording) { + oled_display.fillRect(22, 8, 4, 5, WHITE); + oled_display.drawPixel(22, 8, BLACK); + oled_display.drawPixel(25, 8, BLACK); + oled_display.drawPixel(22, 12, BLACK); + oled_display.drawPixel(25, 12, BLACK); + } else if (MidiClock.state == 2) { + oled_display.drawLine(tri_x, tri_y, tri_x, tri_y + 4, WHITE); + oled_display.fillTriangle(tri_x + 1, tri_y, tri_x + 3, tri_y + 2, tri_x + 1, + tri_y + 4, WHITE); + } else { + oled_display.fillRect(tri_x, tri_y, 4, 5, WHITE); } + + // draw page index + uint8_t pidx_x = pidx_x0; + bool blink = MidiClock.getBlinkHint(); + uint8_t playing_idx = MidiClock.bar_counter % 4; + for (uint8_t i = 0; i < 4; ++i) { + oled_display.drawRect(pidx_x, pidx_y, pidx_w, pidx_h, WHITE); + + // highlight page_select + if (page_select == i) { + oled_display.drawFastHLine(pidx_x+1, pidx_y+1, pidx_w-2, WHITE); + } + + // blink playing_idx + if(playing_idx == i && blink) { + if(page_select == i) { + oled_display.drawFastHLine(pidx_x+2, pidx_y+1, 2, BLACK); + }else{ + oled_display.drawFastHLine(pidx_x+1, pidx_y+1, pidx_w-2, WHITE); + } + } + + pidx_x += pidx_w + 1; + } + + // if (show_track_menu) { + // uint8_t x_offset = 43; + // uint8_t y_offset = 8; + // oled_display.setFont(&TomThumb); + // oled_display.fillRect(84, 0, 40, 32, BLACK); + // track_menu_page.draw_menu(86, y_offset, 39); + //} oled_display.display(); oled_display.setFont(); -#endif } +#endif void SeqPageMidiEvents::setup_callbacks() { // Midi.addOnControlChangeCallback( diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 1316d4577..53abc3b67 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -27,6 +27,8 @@ class SeqPage : public LightPage { static uint8_t ignore_button_release; static bool show_track_menu; + bool recording = false; + SeqPageMidiEvents seqpage_midi_events; SeqPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) @@ -42,6 +44,26 @@ class SeqPage : public LightPage { void select_track(uint8_t device, uint8_t track); void init(); void cleanup(); + + static constexpr uint8_t pane_x1 = 0; + static constexpr uint8_t pane_x2 = 30; + static constexpr uint8_t pane_w = pane_x2 - pane_x1; + static constexpr uint8_t label_x = 0; + static constexpr uint8_t label_md_y = 0; + static constexpr uint8_t label_ex_y = 7; + static constexpr uint8_t label_w = 13; + static constexpr uint8_t label_h = 7; + + static constexpr uint8_t trackid_x = 15; + static constexpr uint8_t trackid_y = 8; + + static constexpr uint8_t tri_x = 16; + static constexpr uint8_t tri_y = 9; + + static constexpr uint8_t pidx_x0 = 1; + static constexpr uint8_t pidx_y = 15; + static constexpr uint8_t pidx_w = 6; + static constexpr uint8_t pidx_h = 3; }; #endif /* SEQPAGE_H__ */ diff --git a/avr/cores/megacommand/Midi/MidiClock.cpp b/avr/cores/megacommand/Midi/MidiClock.cpp index 01ab90fbe..baa59af69 100644 --- a/avr/cores/megacommand/Midi/MidiClock.cpp +++ b/avr/cores/megacommand/Midi/MidiClock.cpp @@ -131,6 +131,10 @@ void MidiClockClass::setTempo(uint16_t _tempo) { CLEAR_LOCK(); } +bool MidiClockClass::getBlinkHint() { + return state == STARTED && (step_counter == 2 || step_counter == 3); +} + void MidiClockClass::handleSongPositionPtr(uint8_t *msg) { /* if (mode == EXTERNAL || mode == EXTERNAL_UART2) { diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index 0b688da1b..690a77143 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -416,7 +416,7 @@ class MidiClockClass { void stop(); void pause(); void setTempo(uint16_t tempo); - uint16_t getTempo(); + bool getBlinkHint(); bool isStarted() { return state == STARTED; } From a27f9de6d15ad5cbbb409a8883750243e3238c12 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 00:27:58 +0800 Subject: [PATCH 115/469] cleanup --- avr/cores/megacommand/MCL/SeqPage.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index d45b89a7c..d916f1783 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -557,11 +557,11 @@ void SeqPage::display() { // draw stop/play/rec state if (recording) { - oled_display.fillRect(22, 8, 4, 5, WHITE); - oled_display.drawPixel(22, 8, BLACK); - oled_display.drawPixel(25, 8, BLACK); - oled_display.drawPixel(22, 12, BLACK); - oled_display.drawPixel(25, 12, BLACK); + oled_display.fillRect(22, tri_y, 4, 5, WHITE); + oled_display.drawPixel(22, tri_y, BLACK); + oled_display.drawPixel(25, tri_y, BLACK); + oled_display.drawPixel(22, tri_y + 4, BLACK); + oled_display.drawPixel(25, tri_y + 4, BLACK); } else if (MidiClock.state == 2) { oled_display.drawLine(tri_x, tri_y, tri_x, tri_y + 4, WHITE); oled_display.fillTriangle(tri_x + 1, tri_y, tri_x + 3, tri_y + 2, tri_x + 1, From 440eda02d577327a78755c6ecce4339daeeacc9f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 12:23:24 +1100 Subject: [PATCH 116/469] Add paramater names for re,gb,eq,dx --- avr/cores/megacommand/MD/MDParams.cpp | 53 +++++++++++++++++++++++++++ avr/cores/megacommand/MD/MDParams.hh | 1 + 2 files changed, 54 insertions(+) diff --git a/avr/cores/megacommand/MD/MDParams.cpp b/avr/cores/megacommand/MD/MDParams.cpp index 242215479..1d87e6567 100644 --- a/avr/cores/megacommand/MD/MDParams.cpp +++ b/avr/cores/megacommand/MD/MDParams.cpp @@ -764,6 +764,43 @@ const model_param_name_t ctr_8p_model_names[] PROGMEM = { { "P1", 0}, { "P7P", 21}, { "P8T", 22}, { "P8P", 23}, {"", 127} }; + +const model_param_name_t ctr_re_model_names[] PROGMEM = { { "TIM", 0}, + { "MOD", 1}, + { "MFQ", 2}, + { "FB", 3}, + { "FLF", 4}, + { "FLW", 5}, + { "MON", 6}, + { "LEV", 7} }; + +const model_param_name_t ctr_gb_model_names[] PROGMEM = { { "DVL", 0}, + { "PRE", 1}, + { "DEC", 2}, + { "DMP", 3}, + { "HP", 4}, + { "LP", 5}, + { "GAT", 6}, + { "LEV", 7} }; + +const model_param_name_t ctr_eq_model_names[] PROGMEM = { { "LF", 0}, + { "LG", 1}, + { "HF", 2}, + { "HG", 3}, + { "PF", 4}, + { "PG", 5}, + { "PQ", 6}, + { "GAI", 7} }; + +const model_param_name_t ctr_dx_model_names[] PROGMEM = { { "ATK", 0}, + { "REL", 1}, + { "THR", 2}, + { "RAT", 3}, + { "KNE", 4}, + { "HP", 5}, + { "GAI", 6}, + { "MIX", 7} }; + const model_param_name_t rom_model_names[] PROGMEM = { { "PTC", 0}, { "DEC", 1}, { "HLD", 2}, @@ -872,6 +909,12 @@ model_to_param_names_t model_param_names[] = { { CTR_AL_MODEL, ctr_al_model_names }, { CTR_8P_MODEL, ctr_8p_model_names }, + + { CTR_RE_MODEL, ctr_re_model_names }, + { CTR_GB_MODEL, ctr_gb_model_names }, + { CTR_EQ_MODEL, ctr_eq_model_names }, + { CTR_DX_MODEL, ctr_dx_model_names }, + { ROM_MODEL, rom_model_names }, { RAM_R1_MODEL, ram_r_model_names }, { RAM_R2_MODEL, ram_r_model_names }, @@ -944,6 +987,16 @@ PGM_P model_param_name(uint8_t model, uint8_t param) { } } +uint8_t map_fx_to_model(uint8_t fx_type) { + if ((fx_type > MD_FX_DYN) || (fx_type < MD_FX_ECHO)) { return 255; } + return fx_type - MD_FX_ECHO + CTR_RE_MODEL; +} + + +PGM_P fx_param_name(uint8_t fx_type, uint8_t param) { + return model_param_name(map_fx_to_model(fx_type), param); +} + static const uint8_t efm_rs_tuning[] PROGMEM = { 1, 3, 6, 9, 11, 14, 17, 19, 22, 25, 27, 30, 33, 35, 38, 41, 43, 46, 49, 51, 54, 57, 59, 62, 65, 67, 70, 73, 75, 78, 81, 83, 86, diff --git a/avr/cores/megacommand/MD/MDParams.hh b/avr/cores/megacommand/MD/MDParams.hh index c36878a7d..3b80cc873 100644 --- a/avr/cores/megacommand/MD/MDParams.hh +++ b/avr/cores/megacommand/MD/MDParams.hh @@ -877,6 +877,7 @@ typedef struct md_machine_name_s_short { extern md_machine_name_t_short const machine_names_short[134] PROGMEM; PGM_P getMachineNameShort(uint8_t machine, uint8_t type); +extern PGM_P fx_param_name(uint8_t fx_type, uint8_t param); /* @} @} */ From b52f1d580a3a153ca4c13bfaa0044a0813c4b78a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 13:53:53 +1100 Subject: [PATCH 117/469] Compiles --- avr/cores/megacommand/GUI/Encoders.hh | 686 +++++++++++++------------- avr/cores/megacommand/GUI/Pages.cpp | 2 +- avr/cores/megacommand/GUI/Pages.hh | 3 +- avr/cores/megacommand/MCL/FXPage.cpp | 8 +- avr/cores/megacommand/MCL/MCLGUI.cpp | 15 +- avr/cores/megacommand/MCL/MCLGUI.h | 2 +- 6 files changed, 361 insertions(+), 355 deletions(-) diff --git a/avr/cores/megacommand/GUI/Encoders.hh b/avr/cores/megacommand/GUI/Encoders.hh index 8f4679a27..fc729e83f 100644 --- a/avr/cores/megacommand/GUI/Encoders.hh +++ b/avr/cores/megacommand/GUI/Encoders.hh @@ -3,9 +3,9 @@ #ifndef ENCODERS_H__ #define ENCODERS_H__ -#include -#include "helpers.h" #include "GUI_private.h" +#include "helpers.h" +#include /** * \addtogroup GUI @@ -28,9 +28,11 @@ class Encoder; **/ typedef void (*encoder_handle_t)(Encoder *enc); -/** Encoder handling function to send a CC value (enc has to be of class CCEncoder). **/ +/** Encoder handling function to send a CC value (enc has to be of class + * CCEncoder). **/ void CCEncoderHandle(Encoder *enc); -/** Encoder handling function to modify the tempo when the encoder value is changed. **/ +/** Encoder handling function to modify the tempo when the encoder value is + * changed. **/ void TempoEncoderHandle(Encoder *enc); /** @@ -41,110 +43,105 @@ void TempoEncoderHandle(Encoder *enc); /** Encoder parent class. **/ class EncoderParent { - /** - * \addtogroup gui_encoder_class - * @{ - **/ - + /** + * \addtogroup gui_encoder_class + * @{ + **/ + public: - /** Old value (before move), current value. **/ - int old, cur; - - /** Counters for encoder pulses **/ - int8_t rot_counter_up = 0; - int8_t rot_counter_down = 0; - /** Number of encoder pulses before increasing/decreasing encoder cur value **/ - /** Number of encoder pulses before increasing/decreasing encoder cur value **/ - uint8_t rot_res = 1; - - /** Handling function. **/ - encoder_handle_t handler; - - /** Create a new encoder with short name and handling function. **/ - EncoderParent(encoder_handle_t _handler = NULL); - - - void clear(); - - - /** Should the encoder be displayed again? **/ - bool redisplay; - - - /** - * Handle a modification of the encoder, the default version calls - * the handling function handler if it is different from NULL. - **/ - virtual void checkHandle(); - - /** Returns true if the encoder value changed. **/ - virtual bool hasChanged(); - - /** Return the current value. **/ - virtual int getValue() { return cur; } - /** Return the old value. **/ - virtual int getOldValue() { return old; } - /** - * Set the value of the encoder to value. If handle is true, - * checkHandle() is called after the modification of the encoder - * value. This will make setValue() behave as if the user had moved - * the encoder. - **/ - virtual void setValue(int value, bool handle = false); - - /** - * Display the encoder at index i on the screen. The index is a - * multiple of 4 characters. This can be overloaded to implement - * custom and fancy ways of displaying encoders. - **/ - virtual void displayAt(int i); - + /** Old value (before move), current value. **/ + int old, cur; + + /** Counters for encoder pulses **/ + int8_t rot_counter_up = 0; + int8_t rot_counter_down = 0; + /** Number of encoder pulses before increasing/decreasing encoder cur value + * **/ + /** Number of encoder pulses before increasing/decreasing encoder cur value + * **/ + uint8_t rot_res = 1; + + /** Handling function. **/ + encoder_handle_t handler; + + /** Create a new encoder with short name and handling function. **/ + EncoderParent(encoder_handle_t _handler = NULL); + + void clear(); + + /** Should the encoder be displayed again? **/ + bool redisplay; + + /** + * Handle a modification of the encoder, the default version calls + * the handling function handler if it is different from NULL. + **/ + virtual void checkHandle(); + + /** Returns true if the encoder value changed. **/ + virtual bool hasChanged(); + + /** Return the current value. **/ + virtual int getValue() { return cur; } + /** Return the old value. **/ + virtual int getOldValue() { return old; } + /** + * Set the value of the encoder to value. If handle is true, + * checkHandle() is called after the modification of the encoder + * value. This will make setValue() behave as if the user had moved + * the encoder. + **/ + virtual void setValue(int value, bool handle = false); + + /** + * Display the encoder at index i on the screen. The index is a + * multiple of 4 characters. This can be overloaded to implement + * custom and fancy ways of displaying encoders. + **/ + virtual void displayAt(int i); + #ifdef HOST_MIDIDUINO - virtual ~Encoder() { } + virtual ~Encoder() {} #endif - - /* @} */ + + /* @} */ }; class Encoder : public EncoderParent { - /** Short name. **/ - public: - - - Encoder(const char *_name = NULL, encoder_handle_t _handler = NULL); - // } - //) : public EncoderParent(encoder_handle_t _handler); - char name[4]; - /** - * If this variable is set to true, and pressmode to false, an - * encoder-turn with the encoder pressed down will lead to an - * increment by 5 times the value (default true). - * - * This will work with the parent update() method, not if update() - * is overloaded. - **/ - bool fastmode; - /** - * If this variable is set to true, turning the encoder while the - * button is pressed will have no effect on the encoder value. - * - * This will work with the parent update() method, not if update() - * is overloaded. - **/ - bool pressmode; - /** Returns the encoder name. **/ - virtual char *getName() { return name; } - /** Set the encoder name (max 3 characters). **/ - virtual void setName(const char *_name); - /** - * Updates the value of an encoder according to the movements of the - * hardware (recorded in the encoder_t structure). The default - * handler adds the normal increment, and handles pressing down the - * encoder according to pressmode and fastmode. - **/ - virtual int update(encoder_t *enc); - - + /** Short name. **/ +public: + Encoder(const char *_name = NULL, encoder_handle_t _handler = NULL); + // } + //) : public EncoderParent(encoder_handle_t _handler); + char name[4]; + /** + * If this variable is set to true, and pressmode to false, an + * encoder-turn with the encoder pressed down will lead to an + * increment by 5 times the value (default true). + * + * This will work with the parent update() method, not if update() + * is overloaded. + **/ + bool fastmode; + /** + * If this variable is set to true, turning the encoder while the + * button is pressed will have no effect on the encoder value. + * + * This will work with the parent update() method, not if update() + * is overloaded. + **/ + bool pressmode; + /** Returns the encoder name. **/ + virtual char *getName() { return name; } + /** Set the encoder name (max 3 characters). **/ + virtual void setName(const char *_name); + /** + * Updates the value of an encoder according to the movements of the + * hardware (recorded in the encoder_t structure). The default + * handler adds the normal increment, and handles pressing down the + * encoder according to pressmode and fastmode. + **/ + virtual int update(encoder_t *enc); }; /** @} **/ @@ -155,57 +152,58 @@ class Encoder : public EncoderParent { /** Encoder with minimum and maximum value. **/ class RangeEncoder : public Encoder { - /** - * \addtogroup gui_rangeencoder_class - * @{ - **/ - + /** + * \addtogroup gui_rangeencoder_class + * @{ + **/ + public: - /** Minimum value of the encoder. **/ - int min; - /** Maximum value of the encoder. **/ - int max; - - /** - * Create a new range-limited encoder with max and min value, short - * name, initial value, and handling function. The initRangeEncoder - * will be called with the constructor arguments. - **/ - RangeEncoder(int _max = 127, int _min = 0, const char *_name = NULL, int init = 0, - encoder_handle_t _handler = NULL) : Encoder(_name, _handler) { - initRangeEncoder(_max, _min, _name, init, _handler); - } - - /** - * Initialize the encoder with the same argument as the constructor. - * - * The initRangeEncoder functions automatically determines which of - * min and max is the minimum value. As of now this can't be used to - * have an "inverted" encoder. - * - * The initial value is called without calling the handling function. - **/ - void initRangeEncoder(int _max = 128, int _min = 0, const char *_name = NULL, int init = 0, - encoder_handle_t _handler = NULL) { - setName(_name); - handler = _handler; - if (_min > _max) { - min = _max; - max = _min; - } else { - min = _min; - max = _max; - } - setValue(init); - } - - /** - * Update the value of the encoder according to pressmode and - * fastmode, and limit the resulting value using limit_value(). - **/ - virtual int update(encoder_t *enc); - - /* @} */ + /** Minimum value of the encoder. **/ + int min; + /** Maximum value of the encoder. **/ + int max; + + /** + * Create a new range-limited encoder with max and min value, short + * name, initial value, and handling function. The initRangeEncoder + * will be called with the constructor arguments. + **/ + RangeEncoder(int _max = 127, int _min = 0, const char *_name = NULL, + int init = 0, encoder_handle_t _handler = NULL) + : Encoder(_name, _handler) { + initRangeEncoder(_max, _min, _name, init, _handler); + } + + /** + * Initialize the encoder with the same argument as the constructor. + * + * The initRangeEncoder functions automatically determines which of + * min and max is the minimum value. As of now this can't be used to + * have an "inverted" encoder. + * + * The initial value is called without calling the handling function. + **/ + void initRangeEncoder(int _max = 128, int _min = 0, const char *_name = NULL, + int init = 0, encoder_handle_t _handler = NULL) { + setName(_name); + handler = _handler; + if (_min > _max) { + min = _max; + max = _min; + } else { + min = _min; + max = _max; + } + setValue(init); + } + + /** + * Update the value of the encoder according to pressmode and + * fastmode, and limit the resulting value using limit_value(). + **/ + virtual int update(encoder_t *enc); + + /* @} */ }; void VarRangeEncoderHandle(Encoder *enc); @@ -222,33 +220,34 @@ void VarRangeEncoderHandle(Encoder *enc); * written to the variable on each modification. **/ class VarRangeEncoder : public RangeEncoder { - /** - * \addtogroup gui_varrangeencoder_class - * @{ - **/ - + /** + * \addtogroup gui_varrangeencoder_class + * @{ + **/ + public: - /** - * Pointer to the variable updated by the encoder. If this is - * different than NULL, the variable will be updated by the value - * of the encoder on each encoder modification. - **/ - uint8_t *var; - - /** - * Create a variable-updating encoder storing its value in the - * variable pointed to by _var. - **/ - VarRangeEncoder(uint8_t *_var, int _max = 127, int _min = 0, const char *_name = NULL, int init = 0) : - RangeEncoder(_max, _min, _name, init, VarRangeEncoderHandle) { - var = _var; - if (var != NULL) { - *var = init; - } - handler = VarRangeEncoderHandle; - } - - /* @} */ + /** + * Pointer to the variable updated by the encoder. If this is + * different than NULL, the variable will be updated by the value + * of the encoder on each encoder modification. + **/ + uint8_t *var; + + /** + * Create a variable-updating encoder storing its value in the + * variable pointed to by _var. + **/ + VarRangeEncoder(uint8_t *_var, int _max = 127, int _min = 0, + const char *_name = NULL, int init = 0) + : RangeEncoder(_max, _min, _name, init, VarRangeEncoderHandle) { + var = _var; + if (var != NULL) { + *var = init; + } + handler = VarRangeEncoderHandle; + } + + /* @} */ }; /** @} **/ @@ -260,97 +259,96 @@ public: /** Enumeration encoder, displaying a limited amount of choices as strings. **/ class EnumEncoder : public RangeEncoder { - /** - * \addtogroup gui_enumencoder_class - * @{ - **/ - + /** + * \addtogroup gui_enumencoder_class + * @{ + **/ + public: - const char **enumStrings; - int cnt; - - /** - * Create an enumeration encoder allowing to choose between _cnt - * different options. Each option should be described by a 3 - * character string in the strings[] array. Turning the encoder will - * display the correct name. - **/ - EnumEncoder(const char *strings[] = NULL, int _cnt = 0, const char *_name = NULL, int init = 0, - encoder_handle_t _handler = NULL) : - RangeEncoder(_cnt - 1, 0, _name, init, _handler) { - enumStrings = strings; - cnt = _cnt; - } - - void initEnumEncoder(const char *strings[], int _cnt, const char *_name = NULL, int init = 0) { - enumStrings = strings; - cnt = _cnt; - min = 0; - max = _cnt - 1; - setValue(init); - setName(_name); - } - - virtual void displayAt(int i); - - /* @} */ + const char **enumStrings; + int cnt; + + /** + * Create an enumeration encoder allowing to choose between _cnt + * different options. Each option should be described by a 3 + * character string in the strings[] array. Turning the encoder will + * display the correct name. + **/ + EnumEncoder(const char *strings[] = NULL, int _cnt = 0, + const char *_name = NULL, int init = 0, + encoder_handle_t _handler = NULL) + : RangeEncoder(_cnt - 1, 0, _name, init, _handler) { + enumStrings = strings; + cnt = _cnt; + } + + void initEnumEncoder(const char *strings[], int _cnt, + const char *_name = NULL, int init = 0) { + enumStrings = strings; + cnt = _cnt; + min = 0; + max = _cnt - 1; + setValue(init); + setName(_name); + } + + virtual void displayAt(int i); + + /* @} */ }; /** Enumeration encoder with enumeration names stored in program space. **/ class PEnumEncoder : public EnumEncoder { - /** - * \addtogroup gui_enumencoder_class - * @{ - **/ - + /** + * \addtogroup gui_enumencoder_class + * @{ + **/ + public: - PEnumEncoder(const char *strings[], int _cnt, const char *_name = NULL, int init = 0, - encoder_handle_t _handler = NULL) : - EnumEncoder(strings, _cnt, _name, init, _handler) { - } - - virtual void displayAt(int i); - - /* @} */ + PEnumEncoder(const char *strings[], int _cnt, const char *_name = NULL, + int init = 0, encoder_handle_t _handler = NULL) + : EnumEncoder(strings, _cnt, _name, init, _handler) {} + + virtual void displayAt(int i); + + /* @} */ }; /** @} **/ /** - * \addtogroup gui_boolencoder_class Boolean encoder + * \addtogroup gui_boolencoder_class Boolean encoder * @{ **/ -static const char *boolEnumStrings[] = { "OFF", "ON" }; +static const char *boolEnumStrings[] = {"OFF", "ON"}; -/** Specialized enumeration encoder allowing the user to choose between "OFF" (0) and "ON" (1). **/ +/** Specialized enumeration encoder allowing the user to choose between "OFF" + * (0) and "ON" (1). **/ class BoolEncoder : public EnumEncoder { - /** - * \addtogroup gui_boolencoder_class - * @{ - **/ - + /** + * \addtogroup gui_boolencoder_class + * @{ + **/ + public: - BoolEncoder(const char *_name = NULL, bool init = false, encoder_handle_t _handler = NULL) : - EnumEncoder(boolEnumStrings, 2, _name, init ? 1 : 0, _handler) { - } - - void initBoolEncoder(const char *_name = NULL, bool init = false) { - initEnumEncoder(boolEnumStrings, 2, _name, init ? 1 : 0); - } - - - bool getBoolValue() { - return getValue() == 1; - } - - /* @} */ + BoolEncoder(const char *_name = NULL, bool init = false, + encoder_handle_t _handler = NULL) + : EnumEncoder(boolEnumStrings, 2, _name, init ? 1 : 0, _handler) {} + + void initBoolEncoder(const char *_name = NULL, bool init = false) { + initEnumEncoder(boolEnumStrings, 2, _name, init ? 1 : 0); + } + + bool getBoolValue() { return getValue() == 1; } + + /* @} */ }; /** @} **/ /** - * \addtogroup gui_miditrackencoder_class MIDI Track encoder + * \addtogroup gui_miditrackencoder_class MIDI Track encoder * @{ **/ @@ -359,23 +357,23 @@ public: * 1 to 16 in order not to confuse musicians. **/ class MidiTrackEncoder : public RangeEncoder { - /** - * \addtogroup gui_miditrackencoder_class - * @{ - **/ + /** + * \addtogroup gui_miditrackencoder_class + * @{ + **/ public: - MidiTrackEncoder(char *_name = NULL, uint8_t init = 0) : RangeEncoder(15, 0, _name, init) { - } - - virtual void displayAt(int i); - - /* @} */ + MidiTrackEncoder(char *_name = NULL, uint8_t init = 0) + : RangeEncoder(15, 0, _name, init) {} + + virtual void displayAt(int i); + + /* @} */ }; /** @} **/ /** - * \addtogroup gui_ccencoder_class CC encoder + * \addtogroup gui_ccencoder_class CC encoder * @{ **/ @@ -383,36 +381,34 @@ public: * Encoder that sends out a CC message on each modification. **/ class CCEncoder : public RangeEncoder { - /** - * \addtogroup gui_ccencoder_class - * @{ - **/ - + /** + * \addtogroup gui_ccencoder_class + * @{ + **/ + public: - /** The CC number used when the CC message is sent. **/ - uint8_t cc; - /** The MIDI channel number (from 0 to 15) to use when sending the CC message. **/ - uint8_t channel; - - virtual uint8_t getCC() { - return cc; - } - virtual uint8_t getChannel() { - return channel; - } - virtual void initCCEncoder(uint8_t _channel, uint8_t _cc) { - cc = _cc; - channel = _channel; - } - - /** Create a CC encoder sending CC messages with number _cc on _channel. **/ - CCEncoder(uint8_t _cc = 0, uint8_t _channel = 0, const char *_name = NULL, int init = 0) : - RangeEncoder(127, 0, _name, init) { - initCCEncoder(_channel, _cc); - handler = CCEncoderHandle; - } - - /* @} */ + /** The CC number used when the CC message is sent. **/ + uint8_t cc; + /** The MIDI channel number (from 0 to 15) to use when sending the CC message. + * **/ + uint8_t channel; + + virtual uint8_t getCC() { return cc; } + virtual uint8_t getChannel() { return channel; } + virtual void initCCEncoder(uint8_t _channel, uint8_t _cc) { + cc = _cc; + channel = _channel; + } + + /** Create a CC encoder sending CC messages with number _cc on _channel. **/ + CCEncoder(uint8_t _cc = 0, uint8_t _channel = 0, const char *_name = NULL, + int init = 0) + : RangeEncoder(127, 0, _name, init) { + initCCEncoder(_channel, _cc); + handler = CCEncoderHandle; + } + + /* @} */ }; char hex2c(uint8_t hex); @@ -420,7 +416,7 @@ char hex2c(uint8_t hex); /** @} **/ /** - * \addtogroup gui_autonameccencoder_class Auto-naming CC encoder + * \addtogroup gui_autonameccencoder_class Auto-naming CC encoder * @{ **/ @@ -430,33 +426,34 @@ char hex2c(uint8_t hex); * in hex, 00 to 7F. **/ class AutoNameCCEncoder : public CCEncoder { - /** - * \addtogroup gui_autonameccencoder_class - * @{ - **/ - + /** + * \addtogroup gui_autonameccencoder_class + * @{ + **/ + public: - AutoNameCCEncoder(uint8_t _cc = 0, uint8_t _channel = 0, const char *_name = NULL, int init = 0) : - CCEncoder(_cc, _channel, _name, init) { - if (_name == NULL) { - setCCName(); - } - } - - /** Automatically set the encoder name according to its CC number. **/ - void setCCName() { - char name[4]; - name[0] = hex2c(getChannel()); - uint8_t cc = getCC(); - name[1] = hex2c(cc >> 4); - name[2] = hex2c(cc & 0xF); - name[3] = '\0'; - setName(name); - } - - virtual void initCCEncoder(uint8_t _channel, uint8_t _cc); - - /* @} */ + AutoNameCCEncoder(uint8_t _cc = 0, uint8_t _channel = 0, + const char *_name = NULL, int init = 0) + : CCEncoder(_cc, _channel, _name, init) { + if (_name == NULL) { + setCCName(); + } + } + + /** Automatically set the encoder name according to its CC number. **/ + void setCCName() { + char name[4]; + name[0] = hex2c(getChannel()); + uint8_t cc = getCC(); + name[1] = hex2c(cc >> 4); + name[2] = hex2c(cc & 0xF); + name[3] = '\0'; + setName(name); + } + + virtual void initCCEncoder(uint8_t _channel, uint8_t _cc); + + /* @} */ }; /** @} **/ @@ -472,17 +469,17 @@ public: * encoder is 255 at the moment). **/ class TempoEncoder : public RangeEncoder { - /** - * \addtogroup gui_tempoencoder_class - * @{ - **/ - + /** + * \addtogroup gui_tempoencoder_class + * @{ + **/ + public: - TempoEncoder(const char *_name = NULL) : RangeEncoder(255, 20, _name) { - handler = TempoEncoderHandle; - } - - /* @} */ + TempoEncoder(const char *_name = NULL) : RangeEncoder(255, 20, _name) { + handler = TempoEncoderHandle; + } + + /* @} */ }; /** @} **/ @@ -493,20 +490,21 @@ public: **/ /** - * Encoder that allows the user to choose between a character (a to z, A to Z, 0 to 9). + * Encoder that allows the user to choose between a character (a to z, A to Z, 0 + *to 9). **/ class CharEncoder : public RangeEncoder { - /** - * \addtogroup gui_charencoder_class - * @{ - **/ - + /** + * \addtogroup gui_charencoder_class + * @{ + **/ + public: - CharEncoder(); - char getChar(); - void setChar(char c); - - /* @} */ + CharEncoder(); + char getChar(); + void setChar(char c); + + /* @} */ }; /** @} **/ @@ -521,16 +519,16 @@ public: * 127), displaying the correct name and octave. **/ class NotePitchEncoder : public RangeEncoder { - /** - * \addtogroup gui_notepitchencoder_class - * @{ - **/ + /** + * \addtogroup gui_notepitchencoder_class + * @{ + **/ public: - NotePitchEncoder(char *_name = NULL); - - void displayAt(int i); - - /* @} */ + NotePitchEncoder(char *_name = NULL); + + void displayAt(int i); + + /* @} */ }; #include "RecordingEncoder.hh" diff --git a/avr/cores/megacommand/GUI/Pages.cpp b/avr/cores/megacommand/GUI/Pages.cpp index 4a78c470a..e725cc74f 100644 --- a/avr/cores/megacommand/GUI/Pages.cpp +++ b/avr/cores/megacommand/GUI/Pages.cpp @@ -46,7 +46,7 @@ void LightPage::update() { GUI.screen_saver = false; clock_minutes = 0; minuteclock = 0; - last_used_clock[i] = slowclock; + encoders_used_clock[i] = slowclock; redisplay = true; } } diff --git a/avr/cores/megacommand/GUI/Pages.hh b/avr/cores/megacommand/GUI/Pages.hh index 02a54e9df..5a376304d 100644 --- a/avr/cores/megacommand/GUI/Pages.hh +++ b/avr/cores/megacommand/GUI/Pages.hh @@ -165,8 +165,7 @@ public: uint8_t curpage; PageContainer *parent; Encoder *encoders[GUI_NUM_ENCODERS]; - - static uint16_t last_used_clock[GUI_NUM_ENCODERS]; + static uint16_t encoders_used_clock[GUI_NUM_ENCODERS]; LightPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) { diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index cf5ea7607..077b6cc40 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -108,21 +108,25 @@ void FXPage::display() { oled_display.print("FX "); oled_display.print(page_mode ? 1 : 0); oled_display.print(" "); + PGM_P param_name = NULL; + char str[4]; for (uint8_t i = 0; i < NUM_OF_ENCODERS; i++) { uint8_t n = i + ((page_mode ? 1 : 0) * NUM_OF_ENCODERS); uint8_t fx_param = params[n].param; uint8_t fx_type = params[n].type; + param_name = fx_param_name(fx_type, encoders[0]->getValue() - 1); + m_strncpy_p(str, param_name, 4); - mcl_gui.draw_encoder(10 + 20 * i, 10, encoders[i], str, show_value); + mcl_gui.draw_light_encoder(10 + 20 * i, 10, encoders[i], str); } - mcl_gui.draw_encoder(100,10,0); oled_display.display(); #endif } + void FXPage::onControlChangeCallback_Midi(uint8_t *msg) { uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); uint8_t param = msg[1]; diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index e33f122e3..70fe25bb8 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -1,4 +1,5 @@ #include "MCL.h" +#define SHOW_VALUE_TIMEOUT 500 bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { text_input_page.init(); @@ -205,25 +206,29 @@ void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { } void MCLGUI::draw_encoder(uint8_t x, uint8_t y, Encoder *encoder) { - draw_encoder(x , y, encoder->value); + draw_encoder(x , y, encoder->cur); } bool MCLGUI::show_encoder_value(Encoder *encoder) { uint8_t match = 255; + for (uint8_t i = 0; i < GUI_NUM_ENCODERS && match != 255; i++) { - if (GUI.page->encoders[i] == encoder) { + if (((LightPage*) GUI.currentPage())->encoders[i] == encoder) { match = i; } } + if (match != 255) { - if (clock_diff(GUI.page->last_used_clock[match], slowclock) > SHOW_VALUE_TIMEOUT)) { show_value = true; } + if (clock_diff(((LightPage*) GUI.currentPage())->encoders_used_clock[match], slowclock) > SHOW_VALUE_TIMEOUT) { return true; } } + return false; + } void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name) { bool show_value = show_encoder_value(encoder); - draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); + draw_md_encoder(x, y, encoder->cur, name, show_value); } void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value) { @@ -260,7 +265,7 @@ void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *na void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name) { bool show_value = show_encoder_value(encoder); - draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); + draw_light_encoder(x, y, encoder->cur, name, show_value); } diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 02d84d3f9..d3203108b 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -29,7 +29,7 @@ class MCLGUI { void draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); void draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); - void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name); + void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); void draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); static constexpr uint8_t s_menu_w = 96; From ca63a512dc5896aeae7ab62dbab3aeaac9fb9c52 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 12:09:05 +0800 Subject: [PATCH 118/469] hook up recording flag --- avr/cores/megacommand/MCL/SeqPage.cpp | 10 +++++----- avr/cores/megacommand/MCL/SeqPage.h | 2 ++ avr/cores/megacommand/MCL/SeqPtcPage.cpp | 17 +++++++++-------- avr/cores/megacommand/MCL/SeqPtcPage.h | 1 - avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 1 + 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index d916f1783..95c3aa4cc 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -557,11 +557,11 @@ void SeqPage::display() { // draw stop/play/rec state if (recording) { - oled_display.fillRect(22, tri_y, 4, 5, WHITE); - oled_display.drawPixel(22, tri_y, BLACK); - oled_display.drawPixel(25, tri_y, BLACK); - oled_display.drawPixel(22, tri_y + 4, BLACK); - oled_display.drawPixel(25, tri_y + 4, BLACK); + oled_display.fillRect(cir_x1, tri_y, 4, 5, WHITE); + oled_display.drawPixel(cir_x1, tri_y, BLACK); + oled_display.drawPixel(cir_x2, tri_y, BLACK); + oled_display.drawPixel(cir_x1, tri_y + 4, BLACK); + oled_display.drawPixel(cir_x2, tri_y + 4, BLACK); } else if (MidiClock.state == 2) { oled_display.drawLine(tri_x, tri_y, tri_x, tri_y + 4, WHITE); oled_display.fillTriangle(tri_x + 1, tri_y, tri_x + 3, tri_y + 2, tri_x + 1, diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 53abc3b67..a9986dd87 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -57,6 +57,8 @@ class SeqPage : public LightPage { static constexpr uint8_t trackid_x = 15; static constexpr uint8_t trackid_y = 8; + static constexpr uint8_t cir_x1 = 22; + static constexpr uint8_t cir_x2 = 25; static constexpr uint8_t tri_x = 16; static constexpr uint8_t tri_y = 9; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 2cc04b38d..3f9b6cc46 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -10,7 +10,7 @@ void SeqPtcPage::setup() { } void SeqPtcPage::cleanup() { SeqPage::cleanup(); - record_mode = false; + recording = false; if (MidiClock.state != 2) { MD.setTrackParam(last_md_track, 0, MD.kit.params[last_md_track][0]); } @@ -55,7 +55,7 @@ void SeqPtcPage::init() { DEBUG_PRINT_FN(); SeqPage::init(); ((MCLEncoder *)encoders[2])->handler = ptc_pattern_len_handler; - record_mode = false; + recording = false; midi_events.setup_callbacks(); DEBUG_PRINTLN("control mode:"); DEBUG_PRINTLN(mcl_cfg.uart2_ctrl_mode); @@ -132,7 +132,7 @@ void SeqPtcPage::display() { const char *str2 = getMachineNameShort(MD.kit.models[dev_num], 2); GUI.setLine(GUI.LINE1); - if (record_mode) { + if (recording) { GUI.put_string_at(0, "RPTC"); } else { GUI.put_string_at(0, "PTC"); @@ -249,7 +249,7 @@ void SeqPtcPage::trig_md(uint8_t note_num) { if (!BUTTON_DOWN(Buttons.BUTTON2)) { MD.triggerTrack(next_track, 127); } - if ((record_mode) && (MidiClock.state == 2)) { + if ((recording) && (MidiClock.state == 2)) { if (!BUTTON_DOWN(Buttons.BUTTON2)) { mcl_seq.md_tracks[next_track].record_track(note_num, 127); @@ -265,7 +265,7 @@ void SeqPtcPage::trig_md_fromext(uint8_t note_num) { if (!BUTTON_DOWN(Buttons.BUTTON2)) { MD.triggerTrack(next_track, 127); } - if ((record_mode) && (MidiClock.state == 2)) { + if ((recording) && (MidiClock.state == 2)) { if (!BUTTON_DOWN(Buttons.BUTTON2)) { mcl_seq.md_tracks[next_track].record_track(note_num, 127); } @@ -305,7 +305,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON1)) { redisplay = true; - record_mode = !record_mode; + recording = !recording; return true; } @@ -397,6 +397,7 @@ uint8_t SeqPtcPage::seq_ext_pitch(uint8_t note_num) { return pitch; } + void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { DEBUG_PRINTLN("note on midi2"); uint8_t note_num = msg[1]; @@ -430,7 +431,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { DEBUG_PRINTLN(mcl_seq.ext_tracks[channel].length); uint8_t pitch = seq_ptc_page.seq_ext_pitch(note_num); MidiUart2.sendNoteOn(channel, pitch, msg[2]); - if ((seq_ptc_page.record_mode) && (MidiClock.state == 2)) { + if ((seq_ptc_page.recording) && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteon(pitch, msg[2]); } #endif @@ -462,7 +463,7 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { uint8_t pitch = seq_ptc_page.seq_ext_pitch(note_num); MidiUart2.sendNoteOff(channel, pitch, msg[2]); - if (seq_ptc_page.record_mode && (MidiClock.state == 2)) { + if (seq_ptc_page.recording && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteoff(pitch, msg[2]); } #endif diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index 20fc798ea..f71b7bde0 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -32,7 +32,6 @@ class SeqPtcPage : public SeqPage { uint8_t poly_max = 0; int8_t poly_notes[MAX_POLY_NOTES]; - bool record_mode = false; SeqPtcMidiEvents midi_events; SeqPtcPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index a9118ed68..3d3691233 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -15,6 +15,7 @@ void SeqRtrkPage::init() { encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; midi_device = DEVICE_MD; curpage = SEQ_RTRK_PAGE; + recording = true; md_exploit.on(); } void SeqRtrkPage::cleanup() { From fc97f8b9b665204638b5b1294cc9be0dcc0f46b8 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 14:06:35 +0800 Subject: [PATCH 119/469] implement sequencer --- avr/cores/megacommand/MCL/SeqPage.cpp | 198 ++++++++++++++++++---- avr/cores/megacommand/MCL/SeqPage.h | 14 ++ avr/cores/megacommand/MCL/SeqStepPage.cpp | 78 +++++++++ avr/cores/megacommand/Midi/MidiClock.cpp | 10 +- avr/cores/megacommand/Midi/MidiClock.h | 2 +- 5 files changed, 263 insertions(+), 39 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 95c3aa4cc..27fa6a812 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -216,6 +216,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { return false; } +#ifndef OLED_DISPLAY void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { GUI.setLine(GUI.LINE2); @@ -241,28 +242,16 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { } if (IS_BIT_SET64(active_track.pattern_mask, i + offset) && !IS_BIT_SET64(active_track.lock_mask, i + offset)) { -#ifdef OLED_DISPLAY - str[i] = (char)0xF8; -#else str[i] = (char)165; -#endif } if (IS_BIT_SET64(active_track.pattern_mask, i + offset) && IS_BIT_SET64(active_track.lock_mask, i + offset)) { -#ifdef OLED_DISPLAY - str[i] = (char)2; -#else str[i] = (char)219; -#endif } if (note_interface.notes[i] == 1) { -/*Char 219 on the minicommand LCD is a []*/ -#ifdef OLED_DISPLAY - str[i] = (char)3; -#else + /*Char 219 on the minicommand LCD is a []*/ str[i] = (char)255; -#endif } } } @@ -427,6 +416,136 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, /*Display the step sequencer pattern on screen, 16 steps at a time*/ GUI.put_string_at(0, mystr); } +#else +void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { + auto &active_track = mcl_seq.md_tracks[last_md_track]; + uint8_t step_count = active_track.step_count; + + uint8_t led_x = seq_x0; + + for (int i = 0; i < 16; i++) { + + uint8_t idx = i + offset; + bool in_range = idx < active_track.length; + bool current = + show_current_step && step_count == idx && MidiClock.state == 2; + bool locked = in_range && IS_BIT_SET64(active_track.lock_mask, i + offset); + + if (note_interface.notes[i] == 1) { + // TI feedback + oled_display.fillRect(led_x, led_y - 1, seq_w + 2, led_h, WHITE); + } else if (!in_range) { + // don't draw + } else if (current ^ locked) { + // highlight + oled_display.fillRect(led_x, led_y, seq_w, led_h, WHITE); + } else if (current && locked) { + // highlight 2 + oled_display.fillRect(led_x, led_y, seq_w, led_h, WHITE); + oled_display.drawPixel(led_x + 2, led_y, BLACK); + } else { + // frame only + oled_display.drawRect(led_x, led_y, seq_w, led_h, WHITE); + } + + led_x += seq_w + 1; + } +} + +void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, + bool show_current_step) { + + uint8_t trig_x = seq_x0; + + if (device == DEVICE_MD) { + auto &active_track = mcl_seq.md_tracks[last_md_track]; + uint64_t pattern_mask = active_track.pattern_mask; + + for (int i = 0; i < 16; i++) { + + uint8_t idx = i + offset; + bool in_range = idx < active_track.length; + + if (note_interface.notes[i] == 1) { + // TI feedback + oled_display.fillRect(trig_x, trig_y, seq_w, trig_h + 1, WHITE); + } else if (!in_range) { + // don't draw + } else { + if (IS_BIT_SET64(pattern_mask, i + offset)) { + /*If the bit is set, there is a trigger at this position. */ + oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); + } else { + oled_display.drawRect(trig_x, trig_y, seq_w, trig_h, WHITE); + } + oled_display.drawPixel(trig_x, trig_y, BLACK); + oled_display.drawPixel(trig_x, trig_y + trig_h - 1, BLACK); + oled_display.drawPixel(trig_x + seq_w - 1, trig_y, BLACK); + oled_display.drawPixel(trig_x + seq_w - 1, trig_y + trig_h - 1, BLACK); + } + + trig_x += seq_w + 1; + } + } +#ifdef EXT_TRACKS + else { + + int8_t note_held = 0; + auto &active_track = mcl_seq.ext_tracks[last_ext_track]; + for (int i = 0; i < active_track.length; i++) { + + uint8_t step_count = active_track.step_count; + uint8_t noteson = 0; + uint8_t notesoff = 0; + bool in_range = (i >= offset) && (i < offset + 16); + bool right_most = (i == active_track.length - 1); + + for (uint8_t a = 0; a < 4; a++) { + if (active_track.notes[a][i] > 0) { + noteson++; + } + if (active_track.notes[a][i] < 0) { + notesoff++; + } + } + + note_held += noteson; + note_held -= notesoff; + + if (!in_range) { + continue; + } + + if (note_interface.notes[i - offset] == 1) { + oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); + } else if (!note_held) { // -- + oled_display.drawFastHLine(trig_x - 1, trig_y + 2, seq_w + 2, WHITE); + } else { // draw top, bottom + oled_display.drawFastHLine(trig_x - 1, trig_y, seq_w + 2, WHITE); + oled_display.drawFastHLine(trig_x - 1, trig_y + trig_h - 1, seq_w + 2, + WHITE); + } + + if (noteson > 0 || notesoff > 0) { // left | + oled_display.drawFastVLine(trig_x - 1, trig_y, trig_h, WHITE); + } + + if (right_most) { // right | + oled_display.drawFastVLine(trig_x + seq_w, trig_y, trig_h, WHITE); + } + + if ((step_count == i) && (MidiClock.state == 2) && show_current_step) { + oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, INVERT); + } + + trig_x += seq_w + 1; + } + } +#endif +} + +#endif // OLED_DISPLAY + void pattern_len_handler(Encoder *enc) { MCLEncoder *enc_ = (MCLEncoder *)enc; if (!enc_->hasChanged()) { @@ -511,9 +630,7 @@ void SeqPage::display() { #else // ref: design/Sequencer.png void SeqPage::display() { - - oled_display.fillRect(pane_x1, 0, pane_w, 32, BLACK); - oled_display.setFont(&TomThumb); + oled_display.clearDisplay(); bool is_md = (midi_device == DEVICE_MD); #ifdef EXT_TRACKS @@ -521,13 +638,23 @@ void SeqPage::display() { #else bool ext_is_a4 = false; #endif + uint8_t track_id = last_md_track; - if (!is_md) - { + if (!is_md) { track_id = last_ext_track; } track_id += 1; + // draw current active track + oled_display.setTextColor(WHITE); + oled_display.setFont(&Elektrothic); + oled_display.setCursor(trackid_x, trackid_y); + if (track_id < 10) { + oled_display.print('0'); + } + oled_display.print(track_id); + + oled_display.setFont(&TomThumb); // draw MD/EXT label if (is_md) { oled_display.fillRect(label_x, label_md_y, label_w, label_h, WHITE); @@ -549,12 +676,6 @@ void SeqPage::display() { oled_display.print("MI"); } - // draw current active track - oled_display.setTextColor(WHITE); - oled_display.setFont(&Elektrothic); - oled_display.setCursor(trackid_x, trackid_y); - oled_display.print(track_id); - // draw stop/play/rec state if (recording) { oled_display.fillRect(cir_x1, tri_y, 4, 5, WHITE); @@ -572,28 +693,37 @@ void SeqPage::display() { // draw page index uint8_t pidx_x = pidx_x0; - bool blink = MidiClock.getBlinkHint(); - uint8_t playing_idx = MidiClock.bar_counter % 4; + bool blink = !MidiClock.getBlinkHint(); + uint8_t playing_idx = (MidiClock.bar_counter + 1) % 4; for (uint8_t i = 0; i < 4; ++i) { oled_display.drawRect(pidx_x, pidx_y, pidx_w, pidx_h, WHITE); // highlight page_select if (page_select == i) { - oled_display.drawFastHLine(pidx_x+1, pidx_y+1, pidx_w-2, WHITE); - } + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, pidx_w - 2, WHITE); + } // blink playing_idx - if(playing_idx == i && blink) { - if(page_select == i) { - oled_display.drawFastHLine(pidx_x+2, pidx_y+1, 2, BLACK); - }else{ - oled_display.drawFastHLine(pidx_x+1, pidx_y+1, pidx_w-2, WHITE); + if (playing_idx == i && blink) { + if (page_select == i) { + oled_display.drawFastHLine(pidx_x + 2, pidx_y + 1, 2, BLACK); + } else { + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, pidx_w - 2, WHITE); } } pidx_x += pidx_w + 1; } + // draw info line 1 + oled_display.fillRect(0, info1_y, pane_w, info_h, WHITE); + oled_display.setTextColor(BLACK); + oled_display.setCursor(1, info1_y + 6); + oled_display.print(info1); + oled_display.setTextColor(WHITE); + oled_display.setCursor(1, info2_y + 6); + oled_display.print(info2); + // if (show_track_menu) { // uint8_t x_offset = 43; // uint8_t y_offset = 8; @@ -601,8 +731,6 @@ void SeqPage::display() { // oled_display.fillRect(84, 0, 40, 32, BLACK); // track_menu_page.draw_menu(86, y_offset, 39); //} - oled_display.display(); - oled_display.setFont(); } #endif diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index a9986dd87..74efcd75c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -28,6 +28,8 @@ class SeqPage : public LightPage { static bool show_track_menu; bool recording = false; + char info1[8] = { '\0' }; + char info2[8] = { '\0' }; SeqPageMidiEvents seqpage_midi_events; SeqPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, @@ -38,6 +40,7 @@ class SeqPage : public LightPage { void create_chars_seq(); void draw_lock_mask(uint8_t offset, bool show_current_step = true); void draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step = true); + void draw_knob_frame(); void loop(); void display(); void setup(); @@ -66,6 +69,17 @@ class SeqPage : public LightPage { static constexpr uint8_t pidx_y = 15; static constexpr uint8_t pidx_w = 6; static constexpr uint8_t pidx_h = 3; + + static constexpr uint8_t info1_y = 19; + static constexpr uint8_t info2_y = 26; + static constexpr uint8_t info_h = 7; + + static constexpr uint8_t seq_w = 5; + static constexpr uint8_t seq_x0 = 32; + static constexpr uint8_t led_y = 22; + static constexpr uint8_t trig_y = 26; + static constexpr uint8_t led_h = 3; + static constexpr uint8_t trig_h = 5; }; #endif /* SEQPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 8c902473b..f23c6abd1 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -8,7 +8,22 @@ void SeqStepPage::config() { seq_param3.cur = mcl_seq.md_tracks[last_md_track].length; tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); seq_param4.max = tuning->len - 1; + + const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); + const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); + + constexpr uint8_t len1 = sizeof(info1); + + char buf[len1] = { '\0' }; + m_strncpy_p(buf, str1, len1); + strncpy(info1, buf, len1); + strncat(info1, ">", len1); + m_strncpy_p(buf, str2, len1); + strncat(info1, buf, len1); + + strcpy(info2, "NOTE"); } + void SeqStepPage::init() { DEBUG_PRINT_FN(); DEBUG_PRINTLN("init seqstep"); @@ -28,6 +43,7 @@ void SeqStepPage::init() { config(); note_interface.state = true; } + void SeqStepPage::cleanup() { midi_events.remove_callbacks(); SeqPage::cleanup(); @@ -36,6 +52,7 @@ void SeqStepPage::cleanup() { } } +#ifndef OLED_DISPLAY void SeqStepPage::display() { GUI.setLine(GUI.LINE1); GUI.put_string_at(0, " "); @@ -99,6 +116,67 @@ void SeqStepPage::display() { SeqPage::display(); } +#else +void SeqStepPage::display() { + SeqPage::display(); + + char c[3] = "--"; + + if (seq_param1.getValue() == 0) { + GUI.put_string_at(0, "L1"); + + } else if (seq_param1.getValue() <= 8) { + GUI.put_string_at(0, "L"); + + GUI.put_value_at1(1, seq_param1.getValue()); + + } else if (seq_param1.getValue() <= 13) { + GUI.put_string_at(0, "P"); + uint8_t prob[5] = {1, 2, 5, 7, 9}; + GUI.put_value_at1(1, prob[seq_param1.getValue() - 9]); + } + + else if (seq_param1.getValue() == 14) { + GUI.put_string_at(0, "1S"); + } + + if (seq_param2.getValue() == 0) { + GUI.put_string_at(2, "--"); + } else if ((seq_param2.getValue() < 12) && (seq_param2.getValue() != 0)) { + GUI.put_string_at(2, "-"); + GUI.put_value_at2(3, 12 - seq_param2.getValue()); + + } else { + GUI.put_string_at(2, "+"); + GUI.put_value_at2(3, seq_param2.getValue() - 12); + } + + if (show_pitch) { + tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); + if (tuning != NULL) { + if (seq_param4.cur == 0) { + GUI.put_string_at(10, "--"); + } else { + uint8_t base = tuning->base; + uint8_t notenum = seq_param4.cur + base; + MusicalNotes number_to_note; + uint8_t oct = notenum / 12; + uint8_t note = notenum - 12 * (notenum / 12); + GUI.put_string_at(10, number_to_note.notes_upper[note]); + GUI.put_value_at1(12, oct); + } + } + } else { + } + GUI.put_value_at(6, seq_param3.getValue()); + GUI.put_value_at1(15, page_select + 1); + draw_lock_mask((page_select * 16), DEVICE_MD); + draw_pattern_mask((page_select * 16), DEVICE_MD); + + oled_display.display(); + oled_display.setFont(); +} +#endif void SeqStepPage::loop() { SeqPage::loop(); diff --git a/avr/cores/megacommand/Midi/MidiClock.cpp b/avr/cores/megacommand/Midi/MidiClock.cpp index baa59af69..7b50b071e 100644 --- a/avr/cores/megacommand/Midi/MidiClock.cpp +++ b/avr/cores/megacommand/Midi/MidiClock.cpp @@ -71,7 +71,6 @@ bool MidiClockClass::clock_less_than(uint32_t a, uint32_t b) { return a_new < b_new; } - void MidiClockClass::handleMidiStart() { onMidiStartCallbacks.call(div96th_counter); } @@ -131,8 +130,13 @@ void MidiClockClass::setTempo(uint16_t _tempo) { CLEAR_LOCK(); } -bool MidiClockClass::getBlinkHint() { - return state == STARTED && (step_counter == 2 || step_counter == 3); +bool MidiClockClass::getBlinkHint(bool onbeat) { + if (state != STARTED) + return false; + if (onbeat) + return (step_counter == 1 || step_counter == 2); + else + return (step_counter == 2 || step_counter == 3); } void MidiClockClass::handleSongPositionPtr(uint8_t *msg) { diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index 690a77143..da4cb440d 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -416,7 +416,7 @@ class MidiClockClass { void stop(); void pause(); void setTempo(uint16_t tempo); - bool getBlinkHint(); + bool getBlinkHint(bool onbeat); bool isStarted() { return state == STARTED; } From c91502c8189cd3c7222f0f5eda67d21bdd94aa7f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 14:08:53 +0800 Subject: [PATCH 120/469] cleanup --- avr/cores/megacommand/MCL/GridPage.cpp | 2 +- avr/cores/megacommand/MCL/SeqPage.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 54645dc0a..fffd22d49 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -451,7 +451,7 @@ void GridPage::display_grid() { oled_display.setTextColor(WHITE, BLACK); } - if (MidiClock.getBlinkHint() && row_idx == active_slots[track_idx]) { + if (MidiClock.getBlinkHint(false) && row_idx == active_slots[track_idx]) { // blink, don't print blink = true; } else if (model == 0) { diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 27fa6a812..5c9db0028 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -433,7 +433,7 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(led_x, led_y - 1, seq_w + 2, led_h, WHITE); + oled_display.fillRect(led_x - 1, led_y - 1, seq_w + 2, led_h + 1, WHITE); } else if (!in_range) { // don't draw } else if (current ^ locked) { @@ -442,7 +442,7 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { } else if (current && locked) { // highlight 2 oled_display.fillRect(led_x, led_y, seq_w, led_h, WHITE); - oled_display.drawPixel(led_x + 2, led_y, BLACK); + oled_display.drawPixel(led_x + 2, led_y + 1, BLACK); } else { // frame only oled_display.drawRect(led_x, led_y, seq_w, led_h, WHITE); @@ -693,7 +693,7 @@ void SeqPage::display() { // draw page index uint8_t pidx_x = pidx_x0; - bool blink = !MidiClock.getBlinkHint(); + bool blink = !MidiClock.getBlinkHint(true); uint8_t playing_idx = (MidiClock.bar_counter + 1) % 4; for (uint8_t i = 0; i < 4; ++i) { oled_display.drawRect(pidx_x, pidx_y, pidx_w, pidx_h, WHITE); From aa868707d5401953e57ee23ceb53b1b92c2c5b7c Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 14:11:05 +0800 Subject: [PATCH 121/469] ... --- avr/cores/megacommand/MCL/SeqPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 5c9db0028..66d3ead50 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -694,7 +694,7 @@ void SeqPage::display() { // draw page index uint8_t pidx_x = pidx_x0; bool blink = !MidiClock.getBlinkHint(true); - uint8_t playing_idx = (MidiClock.bar_counter + 1) % 4; + uint8_t playing_idx = (MidiClock.bar_counter - 1) % 4; for (uint8_t i = 0; i < 4; ++i) { oled_display.drawRect(pidx_x, pidx_y, pidx_w, pidx_h, WHITE); From f860e57e89abdd8068e01907a4889334f08b8154 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 14:22:25 +0800 Subject: [PATCH 122/469] draw knob frames --- avr/cores/megacommand/MCL/MCLGUI.cpp | 10 ++++++++-- avr/cores/megacommand/MCL/MCLGUI.h | 3 ++- avr/cores/megacommand/MCL/SeqPage.cpp | 15 ++++++++++++++- avr/cores/megacommand/MCL/SeqPage.h | 5 +++++ avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 ++ 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 33718b4b9..3a7e3105d 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -21,8 +21,14 @@ bool MCLGUI::wait_for_confirm(const char *title, const char* text) return questiondialog_page.return_state; } -void MCLGUI::draw_vertical_dashline(uint8_t x, uint8_t from) { - for (uint8_t y = from; y < 32; y += 2) { +void MCLGUI::draw_vertical_dashline(uint8_t x, uint8_t from, uint8_t to) { + for (uint8_t y = from; y < to; y += 2) { + oled_display.drawPixel(x, y, WHITE); + } +} + +void MCLGUI::draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to) { + for (uint8_t x = from; x < to; x += 2) { oled_display.drawPixel(x, y, WHITE); } } diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index ea8588970..d96a852f5 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -12,7 +12,8 @@ class MCLGUI { // 1. dst is null-terminated // 2. dst has no trailing spaces bool wait_for_input(char *dst, const char *title, uint8_t len); - void draw_vertical_dashline(uint8_t x, uint8_t from = 1); + void draw_vertical_dashline(uint8_t x, uint8_t from = 1, uint8_t to = 32); + void draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to); bool wait_for_confirm(const char *title, const char* text); void draw_infobox(const char* line1, const char* line2, const int line2_offset = 0); void draw_vertical_separator(uint8_t x); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 66d3ead50..6fb73054c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -715,7 +715,7 @@ void SeqPage::display() { pidx_x += pidx_w + 1; } - // draw info line 1 + // draw info lines oled_display.fillRect(0, info1_y, pane_w, info_h, WHITE); oled_display.setTextColor(BLACK); oled_display.setCursor(1, info1_y + 6); @@ -734,6 +734,19 @@ void SeqPage::display() { } #endif + +void SeqPage::draw_knob_frame() +{ +#ifndef OLED_DISPLAY + return; +#endif + for(uint8_t x = knob_x0; x <= knob_xend; x += knob_w) { + mcl_gui.draw_vertical_dashline(x, 0, knob_y); + oled_display.drawPixel(x, knob_y, WHITE); + } + mcl_gui.draw_horizontal_dashline(knob_y, knob_x0 + 1, knob_xend + 1); +} + void SeqPageMidiEvents::setup_callbacks() { // Midi.addOnControlChangeCallback( // this, diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 74efcd75c..e85e0cab9 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -80,6 +80,11 @@ class SeqPage : public LightPage { static constexpr uint8_t trig_y = 26; static constexpr uint8_t led_h = 3; static constexpr uint8_t trig_h = 5; + + static constexpr uint8_t knob_x0 = 31; + static constexpr uint8_t knob_w = 24; + static constexpr uint8_t knob_xend = 127; + static constexpr uint8_t knob_y = 19; }; #endif /* SEQPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index f23c6abd1..bd4a7f97b 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -120,6 +120,8 @@ void SeqStepPage::display() { void SeqStepPage::display() { SeqPage::display(); + draw_knob_frame(); + char c[3] = "--"; if (seq_param1.getValue() == 0) { From 012a773dd00af8a84de8226c89cb281f95c3c93f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 15:26:33 +0800 Subject: [PATCH 123/469] working on the pages --- avr/cores/megacommand/MCL/SeqPage.cpp | 32 +++++++++-- avr/cores/megacommand/MCL/SeqPage.h | 16 ++++-- avr/cores/megacommand/MCL/SeqParamPage.cpp | 65 ++++++++++++++++++++++ avr/cores/megacommand/MCL/SeqParamPage.h | 14 +++-- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 35 ++++++++++++ avr/cores/megacommand/MCL/SeqRtrkPage.h | 11 ++-- avr/cores/megacommand/MCL/SeqStepPage.cpp | 53 +++++++++--------- avr/cores/megacommand/MCL/SeqStepPage.h | 14 ++--- 8 files changed, 184 insertions(+), 56 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 6fb73054c..672a8bb28 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -734,19 +734,43 @@ void SeqPage::display() { } #endif - -void SeqPage::draw_knob_frame() -{ +void SeqPage::draw_knob_frame() { #ifndef OLED_DISPLAY return; #endif - for(uint8_t x = knob_x0; x <= knob_xend; x += knob_w) { + // draw frame + for (uint8_t x = knob_x0; x <= knob_xend; x += knob_w) { mcl_gui.draw_vertical_dashline(x, 0, knob_y); oled_display.drawPixel(x, knob_y, WHITE); } mcl_gui.draw_horizontal_dashline(knob_y, knob_x0 + 1, knob_xend + 1); } +void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { + uint8_t x = knob_x0 + i * knob_w; + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + oled_display.setCursor(x + 4, 7); + oled_display.print(title); + + oled_display.setFont(); + oled_display.setCursor(x + 4, 9); + oled_display.print(text); +} + +void SeqPage::draw_knob(uint8_t i, uint8_t val) { + uint8_t x = knob_x0 + i * knob_w; + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + oled_display.setCursor(x + 4, 7); + + if(val == 0) { + oled_display.print("--"); + }else { + oled_display.print(val); + } +} + void SeqPageMidiEvents::setup_callbacks() { // Midi.addOnControlChangeCallback( // this, diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index e85e0cab9..e58949057 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -30,23 +30,27 @@ class SeqPage : public LightPage { bool recording = false; char info1[8] = { '\0' }; char info2[8] = { '\0' }; + uint8_t timeout_values[4] = { 0 }; // 255 == highlight SeqPageMidiEvents seqpage_midi_events; SeqPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { } - virtual bool handleEvent(gui_event_t *event); void create_chars_seq(); void draw_lock_mask(uint8_t offset, bool show_current_step = true); void draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step = true); void draw_knob_frame(); - void loop(); - void display(); - void setup(); + void draw_knob(uint8_t i, const char* title, const char* text); + void draw_knob(uint8_t i, uint8_t val); void select_track(uint8_t device, uint8_t track); - void init(); - void cleanup(); + + virtual bool handleEvent(gui_event_t *event); + virtual void loop(); + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void cleanup(); static constexpr uint8_t pane_x1 = 0; static constexpr uint8_t pane_x2 = 30; diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 083ec2e2e..60f6168bc 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -2,7 +2,30 @@ #include "SeqParamPage.h" void SeqParamPage::setup() { SeqPage::setup(); } +void SeqParamPage::config() { + // config info labels + const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); + const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); + + constexpr uint8_t len1 = sizeof(info1); + + char buf[len1] = {'\0'}; + m_strncpy_p(buf, str1, len1); + strncpy(info1, buf, len1); + strncat(info1, ">", len1); + m_strncpy_p(buf, str2, len1); + strncat(info1, buf, len1); + + strcpy(info2, "PARAM-"); + if(page_id == 0) { + strcat(info2, "A"); + }else { + strcat(info2, "B"); + } +} + void SeqParamPage::init() { + config(); md_exploit.on(); note_interface.state = true; @@ -32,15 +55,18 @@ void SeqParamPage::init() { oled_display.clearDisplay(); #endif } + void SeqParamPage::construct(uint8_t p1_, uint8_t p2_) { p1 = p1_; p2 = p2_; } + void SeqParamPage::cleanup() { SeqPage::cleanup(); midi_events.remove_callbacks(); } +#ifndef OLED_DISPLAY void SeqParamPage::display() { GUI.setLine(GUI.LINE1); char myName[4] = "-- "; @@ -87,7 +113,45 @@ void SeqParamPage::display() { draw_lock_mask(page_select * 16); SeqPage::display(); } +#else +void SeqParamPage::display() { + SeqPage::display(); + draw_knob_frame(); + + char myName[4] = "-- "; + char myName2[4] = "-- "; + + if (encoders[0]->getValue() != 0) { + PGM_P modelname = NULL; + modelname = model_param_name(MD.kit.models[last_md_track], + encoders[0]->getValue() - 1); + if (modelname != NULL) { + m_strncpy_p(myName, modelname, 4); + } + GUI.put_string_at(0, myName); + } + + if (encoders[2]->getValue() != 0) { + PGM_P modelname = NULL; + modelname = model_param_name(MD.kit.models[last_md_track], + encoders[2]->getValue() - 1); + if (modelname != NULL) { + m_strncpy_p(myName2, modelname, 4); + } + GUI.put_string_at(7, myName2); + } + + draw_knob(0, "TGT", myName); + draw_knob(2, "TGT", myName2); + + draw_knob(1, encoders[1]->getValue()); + draw_knob(3, encoders[3]->getValue()); + draw_pattern_mask(page_select * 16, DEVICE_MD); + draw_lock_mask(page_select * 16); + SeqPage::display(); +} +#endif void SeqParamPage::loop() { if (encoders[0]->hasChanged() || encoders[1]->hasChanged() || @@ -202,6 +266,7 @@ if (utiming == 0) { return true; } +#ifndef OLED_DISPLAY if (EVENT_RELEASED(event, Buttons.BUTTON1)) { uint8_t page_depth = page_id; if (page_depth < NUM_PARAM_PAGES - 1) { diff --git a/avr/cores/megacommand/MCL/SeqParamPage.h b/avr/cores/megacommand/MCL/SeqParamPage.h index 925e4cf1e..61e4f9f67 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.h +++ b/avr/cores/megacommand/MCL/SeqParamPage.h @@ -28,12 +28,14 @@ class SeqParamPage : public SeqPage { : SeqPage(e1, e2, e3, e4) {} void construct(uint8_t p1, uint8_t p2); - bool handleEvent(gui_event_t *event); - void display(); - void loop(); - void setup(); - void init(); - void cleanup(); + + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void config(); + virtual void loop(); + virtual void setup(); + virtual void init(); + virtual void cleanup(); }; #endif /* SEQPARAMPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 3d3691233..5bdd6b450 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -3,6 +3,24 @@ void SeqRtrkPage::setup() { SeqPage::setup(); } +void SeqRtrkPage::config() { + + // config info labels + const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); + const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); + + constexpr uint8_t len1 = sizeof(info1); + + char buf[len1] = {'\0'}; + m_strncpy_p(buf, str1, len1); + strncpy(info1, buf, len1); + strncat(info1, ">", len1); + m_strncpy_p(buf, str2, len1); + strncat(info1, buf, len1); + + strcpy(info2, "RTRK"); +} + void SeqRtrkPage::init() { SeqPage::init(); @@ -16,11 +34,15 @@ void SeqRtrkPage::init() { midi_device = DEVICE_MD; curpage = SEQ_RTRK_PAGE; recording = true; + config(); md_exploit.on(); } + void SeqRtrkPage::cleanup() { SeqPage::cleanup(); } + +#ifndef OLED_DISPLAY void SeqRtrkPage::display() { if ((!redisplay) && (MidiClock.state == 2)) { return; } GUI.setLine(GUI.LINE1); @@ -51,6 +73,19 @@ void SeqRtrkPage::display() { draw_pattern_mask(page_select * 16, DEVICE_MD, show_current_step); SeqPage::display(); } +#else +void SeqRtrkPage::display() { + if ((!redisplay) && (MidiClock.state == 2)) { return; } + SeqPage::display(); + + bool show_current_step = false; + draw_lock_mask(page_select * 16, show_current_step); + draw_pattern_mask(page_select * 16, DEVICE_MD, show_current_step); + + oled_display.display(); +} +#endif + bool SeqRtrkPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.h b/avr/cores/megacommand/MCL/SeqRtrkPage.h index 37068c94a..203192429 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.h +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.h @@ -11,11 +11,12 @@ class SeqRtrkPage : public SeqPage { SeqRtrkPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : SeqPage(e1, e2, e3, e4) {} - bool handleEvent(gui_event_t *event); - void display(); - void setup(); - void init(); - void cleanup(); + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void setup(); + virtual void config(); + virtual void init(); + virtual void cleanup(); }; #endif /* SEQRTRKPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index bd4a7f97b..fbac713ab 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -9,12 +9,13 @@ void SeqStepPage::config() { tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); seq_param4.max = tuning->len - 1; + // config info labels const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); constexpr uint8_t len1 = sizeof(info1); - char buf[len1] = { '\0' }; + char buf[len1] = {'\0'}; m_strncpy_p(buf, str1, len1); strncpy(info1, buf, len1); strncat(info1, ">", len1); @@ -122,56 +123,52 @@ void SeqStepPage::display() { draw_knob_frame(); - char c[3] = "--"; + char K[4]; if (seq_param1.getValue() == 0) { - GUI.put_string_at(0, "L1"); - + strcpy(K, "L1"); } else if (seq_param1.getValue() <= 8) { - GUI.put_string_at(0, "L"); - - GUI.put_value_at1(1, seq_param1.getValue()); - + strcpy(K, "L "); + K[1] = seq_param1.getValue() + '0'; } else if (seq_param1.getValue() <= 13) { - GUI.put_string_at(0, "P"); + strcpy(K, "P "); uint8_t prob[5] = {1, 2, 5, 7, 9}; - GUI.put_value_at1(1, prob[seq_param1.getValue() - 9]); - } - - else if (seq_param1.getValue() == 14) { - GUI.put_string_at(0, "1S"); + K[1] = prob[seq_param1.getValue() - 9] + '0'; + } else if (seq_param1.getValue() == 14) { + strcpy(K, "1S"); } + draw_knob(0, "COND", K); + strcpy(K, "--"); if (seq_param2.getValue() == 0) { - GUI.put_string_at(2, "--"); } else if ((seq_param2.getValue() < 12) && (seq_param2.getValue() != 0)) { - GUI.put_string_at(2, "-"); - GUI.put_value_at2(3, 12 - seq_param2.getValue()); - + K[1] = 12 - seq_param2.getValue() + '0'; } else { - GUI.put_string_at(2, "+"); - GUI.put_value_at2(3, seq_param2.getValue() - 12); + K[0] = '+'; + K[1] = seq_param2.getValue() - 12 + '0'; } + draw_knob(1, "UTIM", K); + + itoa(seq_param3.getValue(), K, 10); + draw_knob(2, "LEN", K); if (show_pitch) { tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); if (tuning != NULL) { - if (seq_param4.cur == 0) { - GUI.put_string_at(10, "--"); - } else { + strcpy(K, "--"); + if (seq_param4.cur != 0) { uint8_t base = tuning->base; uint8_t notenum = seq_param4.cur + base; MusicalNotes number_to_note; uint8_t oct = notenum / 12; uint8_t note = notenum - 12 * (notenum / 12); - GUI.put_string_at(10, number_to_note.notes_upper[note]); - GUI.put_value_at1(12, oct); + strcpy(K, number_to_note.notes_upper[note]); + K[2] = oct + '0'; + K[3] = 0; } + draw_knob(3, "PTC", K); } - } else { } - GUI.put_value_at(6, seq_param3.getValue()); - GUI.put_value_at1(15, page_select + 1); draw_lock_mask((page_select * 16), DEVICE_MD); draw_pattern_mask((page_select * 16), DEVICE_MD); diff --git a/avr/cores/megacommand/MCL/SeqStepPage.h b/avr/cores/megacommand/MCL/SeqStepPage.h index 0bff1ec88..86a3f20e9 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.h +++ b/avr/cores/megacommand/MCL/SeqStepPage.h @@ -23,13 +23,13 @@ class SeqStepPage : public SeqPage { Encoder *e4 = NULL) : SeqPage(e1, e2, e3, e4) { } - bool handleEvent(gui_event_t *event); - void display(); - void setup(); - void init(); - void config(); - void loop(); - void cleanup(); + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void config(); + virtual void loop(); + virtual void cleanup(); }; #endif /* SEQSTEPPAGE_H__ */ From f42cea535c29416a1b6a7b280407116bb4fe9dff Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 15:32:32 +0800 Subject: [PATCH 124/469] param pages --- avr/cores/megacommand/MCL/SeqPage.cpp | 12 ++---------- avr/cores/megacommand/MCL/SeqPage.h | 2 +- avr/cores/megacommand/MCL/SeqParamPage.cpp | 4 ++-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 672a8bb28..f7a74e319 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -758,17 +758,9 @@ void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { oled_display.print(text); } -void SeqPage::draw_knob(uint8_t i, uint8_t val) { +void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* name) { uint8_t x = knob_x0 + i * knob_w; - oled_display.setFont(&TomThumb); - oled_display.setTextColor(WHITE); - oled_display.setCursor(x + 4, 7); - - if(val == 0) { - oled_display.print("--"); - }else { - oled_display.print(val); - } + mcl_gui.draw_md_encoder(x + 1, 1, enc, name); } void SeqPageMidiEvents::setup_callbacks() { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index e58949057..4b47c8b10 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -42,7 +42,7 @@ class SeqPage : public LightPage { void draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step = true); void draw_knob_frame(); void draw_knob(uint8_t i, const char* title, const char* text); - void draw_knob(uint8_t i, uint8_t val); + void draw_knob(uint8_t i, Encoder* enc, const char* name); void select_track(uint8_t device, uint8_t track); virtual bool handleEvent(gui_event_t *event); diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 60f6168bc..477009000 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -144,8 +144,8 @@ void SeqParamPage::display() { draw_knob(0, "TGT", myName); draw_knob(2, "TGT", myName2); - draw_knob(1, encoders[1]->getValue()); - draw_knob(3, encoders[3]->getValue()); + draw_knob(1, encoders[1], "VAL"); + draw_knob(3, encoders[3], "VAL"); draw_pattern_mask(page_select * 16, DEVICE_MD); draw_lock_mask(page_select * 16); SeqPage::display(); From eb0382438a27e1fc5f718c4c53084046d607af0d Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 15:36:03 +0800 Subject: [PATCH 125/469] wrapup --- avr/cores/megacommand/GUI/Pages.cpp | 1 + avr/cores/megacommand/MCL/SeqParamPage.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/GUI/Pages.cpp b/avr/cores/megacommand/GUI/Pages.cpp index e725cc74f..701c639cb 100644 --- a/avr/cores/megacommand/GUI/Pages.cpp +++ b/avr/cores/megacommand/GUI/Pages.cpp @@ -4,6 +4,7 @@ #include "Pages.hh" #include "WProgram.h" +uint16_t LightPage::encoders_used_clock[GUI_NUM_ENCODERS]; /** * \addtogroup GUI * diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 477009000..a5f311099 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -266,7 +266,6 @@ if (utiming == 0) { return true; } -#ifndef OLED_DISPLAY if (EVENT_RELEASED(event, Buttons.BUTTON1)) { uint8_t page_depth = page_id; if (page_depth < NUM_PARAM_PAGES - 1) { From 7c3b8d3657a15624cbf4030a1508bfde64a4a3a0 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 15:37:07 +0800 Subject: [PATCH 126/469] .. --- avr/cores/megacommand/MCL/SeqParamPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index a5f311099..6cfc2ef93 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -148,7 +148,8 @@ void SeqParamPage::display() { draw_knob(3, encoders[3], "VAL"); draw_pattern_mask(page_select * 16, DEVICE_MD); draw_lock_mask(page_select * 16); - SeqPage::display(); + + oled_display.display(); } #endif From d66e8a6d2ed5f7c35eed9686e620775e52f4e7a1 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 24 Oct 2019 15:48:15 +0800 Subject: [PATCH 127/469] tweaking param page --- avr/cores/megacommand/MCL/SeqPage.cpp | 8 ++++++-- avr/cores/megacommand/MCL/SeqParamPage.cpp | 8 +++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index f7a74e319..56d61a1a3 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -758,9 +758,13 @@ void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { oled_display.print(text); } -void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* name) { +void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* title) { uint8_t x = knob_x0 + i * knob_w; - mcl_gui.draw_md_encoder(x + 1, 1, enc, name); + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + oled_display.setCursor(x + 4, 7); + oled_display.print(title); + mcl_gui.draw_encoder(x + 7, 8, enc); } void SeqPageMidiEvents::setup_callbacks() { diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 6cfc2ef93..9a5d5e5db 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "SeqParamPage.h" +#include "MCL.h" void SeqParamPage::setup() { SeqPage::setup(); } void SeqParamPage::config() { @@ -17,9 +17,9 @@ void SeqParamPage::config() { strncat(info1, buf, len1); strcpy(info2, "PARAM-"); - if(page_id == 0) { + if (page_id == 0) { strcat(info2, "A"); - }else { + } else { strcat(info2, "B"); } } @@ -128,7 +128,6 @@ void SeqParamPage::display() { if (modelname != NULL) { m_strncpy_p(myName, modelname, 4); } - GUI.put_string_at(0, myName); } if (encoders[2]->getValue() != 0) { @@ -138,7 +137,6 @@ void SeqParamPage::display() { if (modelname != NULL) { m_strncpy_p(myName2, modelname, 4); } - GUI.put_string_at(7, myName2); } draw_knob(0, "TGT", myName); From 9cc6a29a222924d8745b1cdecd88e20b07b2b533 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 21:20:35 +1100 Subject: [PATCH 128/469] forgot to define array --- avr/cores/megacommand/GUI/Pages.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/cores/megacommand/GUI/Pages.cpp b/avr/cores/megacommand/GUI/Pages.cpp index e725cc74f..db02f75da 100644 --- a/avr/cores/megacommand/GUI/Pages.cpp +++ b/avr/cores/megacommand/GUI/Pages.cpp @@ -30,6 +30,8 @@ void PageParent::redisplayPage() { } } +uint16_t LightPage::encoders_used_clock[GUI_NUM_ENCODERS]; + void LightPage::update() { encoder_t _encoders[GUI_NUM_ENCODERS]; From 99306cfc11576797bf29f1645fe3a32529269ff4 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 22:30:53 +1100 Subject: [PATCH 129/469] FXPages functional, Light encoders working --- avr/cores/megacommand/MCL/AuxPages.cpp | 20 +++++- avr/cores/megacommand/MCL/AuxPages.h | 1 + avr/cores/megacommand/MCL/FXPage.cpp | 64 +++++++++--------- avr/cores/megacommand/MCL/MCLGUI.cpp | 70 +++++++++++++------- avr/cores/megacommand/MCL/PageSelectPage.cpp | 14 ++-- 5 files changed, 104 insertions(+), 65 deletions(-) diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index a18412371..e308ccaa5 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -17,15 +17,29 @@ MixerPage mixer_page(&mixer_param1, &mixer_param2, &mixer_param3, &mixer_param4); RoutePage route_page(&route_param1, &route_param2, &route_param2); -fx_param_t fx_echo_params[8] = { +fx_param_t fx_echo_params[8] = { {MD_FX_ECHO, MD_ECHO_TIME}, - {MD_FX_ECHO, MD_ECHO_MOD}, - {MD_FX_ECHO, MD_ECHO_MFRQ}, {MD_FX_ECHO, MD_ECHO_FB}, {MD_FX_ECHO, MD_ECHO_FLTF}, {MD_FX_ECHO, MD_ECHO_FLTW}, + {MD_FX_ECHO, MD_ECHO_MOD}, + {MD_FX_ECHO, MD_ECHO_MFRQ}, {MD_FX_ECHO, MD_ECHO_MONO}, {MD_FX_ECHO, MD_ECHO_LEV}}; +fx_param_t fx_reverb_params[8] = { + {MD_FX_REV, MD_REV_DVOL}, + {MD_FX_REV, MD_REV_DEC}, + {MD_FX_REV, MD_REV_LP}, + {MD_FX_REV, MD_REV_HP}, + {MD_FX_REV, MD_REV_DAMP}, + {MD_FX_REV, MD_REV_GATE}, + {MD_FX_REV, MD_REV_PRED}, + {MD_FX_REV, MD_REV_LEV}}; + + FXPage fx_page_a(&fx_param1, &fx_param2, &fx_param3, &fx_param4, (fx_param_t*) &fx_echo_params, 8); + +FXPage fx_page_b(&fx_param1, &fx_param2, &fx_param3, &fx_param4, + (fx_param_t*) &fx_reverb_params, 8); diff --git a/avr/cores/megacommand/MCL/AuxPages.h b/avr/cores/megacommand/MCL/AuxPages.h index c9513f24e..706ba58d1 100644 --- a/avr/cores/megacommand/MCL/AuxPages.h +++ b/avr/cores/megacommand/MCL/AuxPages.h @@ -29,5 +29,6 @@ extern MCLEncoder fx_param3; extern MCLEncoder fx_param4; extern FXPage fx_page_a; +extern FXPage fx_page_b; #endif /* AUXPAGES_H__ */ diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 077b6cc40..b38e0afc3 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -3,7 +3,6 @@ #include "RAMPage.h" #define FX_TYPE 0 #define FX_PARAM 1 -#define NUM_OF_ENCODERS 4 #define INTERPOLATE void FXPage::setup() { DEBUG_PRINT_FN(); } @@ -15,31 +14,29 @@ void FXPage::init() { oled_display.setFont(); #endif md_exploit.off(); - for (uint8_t a = 0; a < num_of_params; a++) { - DEBUG_PRINTLN("params"); - DEBUG_PRINTLN(params[a].type); - DEBUG_PRINTLN(params[a].param); - } - - - for (uint8_t n = 0; n < num_of_params; n++) { + for (uint8_t n = 0; n < GUI_NUM_ENCODERS; n++) { + uint8_t a = ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS) + n; uint8_t fx_param = params[n].param; + + DEBUG_PRINTLN(params[n].param); switch (params[n].type) { case MD_FX_ECHO: DEBUG_PRINTLN("setting delay"); DEBUG_PRINTLN(n); - DEBUG_PRINTLN(fx_param); - encoders[n]->cur = MD.kit.delay[fx_param]; + DEBUG_PRINTLN(fx_param); + encoders[a]->cur = MD.kit.delay[fx_param]; break; case MD_FX_REV: - encoders[n]->cur = MD.kit.reverb[fx_param]; + DEBUG_PRINTLN("setting reverb"); + DEBUG_PRINTLN(n); + encoders[a]->cur = MD.kit.reverb[fx_param]; break; case MD_FX_EQ: - encoders[n]->cur = MD.kit.eq[fx_param]; + encoders[a]->cur = MD.kit.eq[fx_param]; break; case MD_FX_DYN: - encoders[n]->cur = MD.kit.dynamics[fx_param]; + encoders[a]->cur = MD.kit.dynamics[fx_param]; break; } @@ -55,15 +52,15 @@ void FXPage::cleanup() { void FXPage::loop() { - for (uint8_t i = 0; i < NUM_OF_ENCODERS; i++) { - uint8_t n = i + ((page_mode ? 1 : 0) * NUM_OF_ENCODERS); + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { + uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); if (encoders[i]->hasChanged()) { - uint8_t fx_param = params[n].param; - uint8_t fx_type = params[n].type; + uint8_t fx_param = params[n].param; + uint8_t fx_type = params[n].type; uint8_t val; - //Interpolation. + // Interpolation. #ifdef INTERPOLATE for (val = encoders[i]->old; val < encoders[i]->cur; val++) { MD.sendFXParam(fx_param, val, fx_type); @@ -72,7 +69,7 @@ void FXPage::loop() { MD.sendFXParam(fx_param, val, fx_type); } #else - MD.sendFXParam(fx_param, encoders[i]->cur, fx_type); + MD.sendFXParam(fx_param, encoders[i]->cur, fx_type); #endif } } @@ -108,23 +105,22 @@ void FXPage::display() { oled_display.print("FX "); oled_display.print(page_mode ? 1 : 0); oled_display.print(" "); - PGM_P param_name = NULL; - char str[4]; - for (uint8_t i = 0; i < NUM_OF_ENCODERS; i++) { - uint8_t n = i + ((page_mode ? 1 : 0) * NUM_OF_ENCODERS); - - uint8_t fx_param = params[n].param; - uint8_t fx_type = params[n].type; - param_name = fx_param_name(fx_type, encoders[0]->getValue() - 1); + PGM_P param_name = NULL; + char str[4]; + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { + uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); + + uint8_t fx_param = params[n].param; + uint8_t fx_type = params[n].type; + param_name = fx_param_name(fx_type, fx_param); m_strncpy_p(str, param_name, 4); - mcl_gui.draw_light_encoder(10 + 20 * i, 10, encoders[i], str); - } + mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); + } - oled_display.display(); + oled_display.display(); #endif - } void FXPage::onControlChangeCallback_Midi(uint8_t *msg) { @@ -175,10 +171,10 @@ bool FXPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { -// GUI.setPage(&grid_page); + // GUI.setPage(&grid_page); } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { - page_mode = !(page_mode); + page_mode = !(page_mode); } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 70fe25bb8..8fd2b1d20 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -1,5 +1,5 @@ #include "MCL.h" -#define SHOW_VALUE_TIMEOUT 500 +#define SHOW_VALUE_TIMEOUT 2000 bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { text_input_page.init(); @@ -152,14 +152,14 @@ void MCLGUI::draw_infobox(const char *line1, const char *line2, void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { - bool vert_flip = false; bool horiz_flip = false; uint8_t image_w = 11; uint8_t image_h = 11; - //Scale encoder values to 123. encoder animation does not start and stop on 0. - value = (uint8_t) ((float) value * .95); + // Scale encoder values to 123. encoder animation does not start and stop on + // 0. + value = (uint8_t)((float)value * .95); value += 4; @@ -177,7 +177,9 @@ void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { } else { vert_flip = true; horiz_flip = false; - if (value > 122) { value = 122; } + if (value > 122) { + value = 122; + } value = 32 - (value - 96); } @@ -206,32 +208,36 @@ void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { } void MCLGUI::draw_encoder(uint8_t x, uint8_t y, Encoder *encoder) { - draw_encoder(x , y, encoder->cur); + draw_encoder(x, y, encoder->cur); } bool MCLGUI::show_encoder_value(Encoder *encoder) { uint8_t match = 255; - for (uint8_t i = 0; i < GUI_NUM_ENCODERS && match != 255; i++) { - if (((LightPage*) GUI.currentPage())->encoders[i] == encoder) { + for (uint8_t i = 0; i < GUI_NUM_ENCODERS && match == 255; i++) { + if (((LightPage *)GUI.currentPage())->encoders[i] == encoder) { match = i; } } if (match != 255) { - if (clock_diff(((LightPage*) GUI.currentPage())->encoders_used_clock[match], slowclock) > SHOW_VALUE_TIMEOUT) { return true; } + if (clock_diff(((LightPage *)GUI.currentPage())->encoders_used_clock[match], + slowclock) < SHOW_VALUE_TIMEOUT) { + return true; + } } return false; - } -void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name) { +void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, + const char *name) { bool show_value = show_encoder_value(encoder); draw_md_encoder(x, y, encoder->cur, name, show_value); } -void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value) { +void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, + const char *name, bool show_value) { auto oldfont = oled_display.getFont(); @@ -241,7 +247,7 @@ void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *na oled_display.setFont(&TomThumb); oled_display.setTextColor(WHITE); - //Find the encoder number matching the encoder. + // Find the encoder number matching the encoder. if (show_value) { oled_display.setCursor(x, y + image_h + 1 + 2 + 8); oled_display.print(value); @@ -258,30 +264,46 @@ void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *na oled_display.drawPixel(x, y + image_h, WHITE); oled_display.drawPixel(x + image_w - 1, y + image_h + 2, WHITE); - oled_display.setFont(oldfont); } - -void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char*name) { +void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, + const char *name) { bool show_value = show_encoder_value(encoder); draw_light_encoder(x, y, encoder->cur, name, show_value); } +void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, + const char *name, bool show_value) { + auto oldfont = oled_display.getFont(); + oled_display.setFont(&TomThumb); -void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value) { + oled_display.setTextColor(WHITE); + + uint8_t x_offset = x; if (show_value) { - oled_display.setCursor(x, y); + if (value < 10) { + x_offset += 2; + } + if (value < 100) { + x_offset += 2; + } + + oled_display.setCursor(x_offset, y); + oled_display.print(value); + } else { + + if (strlen(name) == 2) { + x_offset += 2; + } + oled_display.setCursor(x_offset, y); + oled_display.print(name); } - else { - oled_display.setCursor(x, y); - oled_display.print(name); - } - y += 6; + y += 2; draw_encoder(x, y, value); - + oled_display.setFont(oldfont); } diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 584aceccf..2862386e8 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -7,8 +7,9 @@ #define RAM_PAGE_B 15 #define WAVD_PAGE 8 #define SOUND 7 -#define FX_PAGE_A 9 -#define LOUDNESS 10 +#define FX_PAGE_A 10 +#define FX_PAGE_B 11 +#define LOUDNESS 9 void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -45,11 +46,16 @@ LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { strncpy(str, "RAM 2", 6); r_page = &ram_page_b; break; - case FX_PAGE_A: + case FX_PAGE_A: if (str) - strncpy(str, "FX CTRL", 8); + strncpy(str, "DELAY", 8); r_page = &fx_page_a; break; + case FX_PAGE_B: + if (str) + strncpy(str, "REVERB", 8); + r_page = &fx_page_b; + break; #ifdef WAV_DESIGNER case WAVD_PAGE: if (str) From 31ca2223593fb9cd02a2788949661ac739870ab3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 22:50:13 +1100 Subject: [PATCH 130/469] draw_md_encoder now works --- avr/cores/megacommand/MCL/MCLGUI.cpp | 31 ++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 8fd2b1d20..0209bf154 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -246,24 +246,37 @@ void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, oled_display.setFont(&TomThumb); oled_display.setTextColor(WHITE); - + uint8_t x_offset = x; // Find the encoder number matching the encoder. - if (show_value) { - oled_display.setCursor(x, y + image_h + 1 + 2 + 8); - oled_display.print(value); + if (strlen(name) == 2) { + x_offset += 2; } - - oled_display.setCursor(x, y); + oled_display.setCursor(x_offset, y); oled_display.print(name); - y += 10; + y += 5; draw_encoder(x, y, value); - oled_display.drawPixel(x + image_w / 2, y - 2, WHITE); - oled_display.drawPixel(x, y + image_h, WHITE); + oled_display.drawPixel(x + image_w / 2, y - 3, WHITE); + oled_display.drawPixel(x, y + image_h + 2, WHITE); oled_display.drawPixel(x + image_w - 1, y + image_h + 2, WHITE); + x_offset = x; + if (show_value) { + if (value < 10) { + x_offset += 2; + } + if (value < 100) { + x_offset += 2; + } + + oled_display.setCursor(x_offset, y + image_h + 1 + 8); + + oled_display.print(value); + } + + oled_display.setFont(oldfont); } From 0c1b85ee145ce1c894760416dabbc21a1e6d1784 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 22:54:39 +1100 Subject: [PATCH 131/469] Add small hack to prevent values from being re-displayed on slow_clock loop --- avr/cores/megacommand/MCL/MCLGUI.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 0209bf154..b54587579 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -225,6 +225,9 @@ bool MCLGUI::show_encoder_value(Encoder *encoder) { slowclock) < SHOW_VALUE_TIMEOUT) { return true; } + else { + ((LightPage *)GUI.currentPage())->encoders_used_clock[match] = slowclock - SHOW_VALUE_TIMEOUT - 1; + } } return false; From 5a356ee10db78e339a67a472403ed402790ba984 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 23:07:27 +1100 Subject: [PATCH 132/469] Fix TomThum numerics, my eyes are healed --- .../Adafruit-GFX-Library/Fonts/TomThumb.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp index 9c0bbacad..4d1787b69 100644 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp @@ -22,14 +22,14 @@ const uint8_t TomThumbBitmaps[] PROGMEM = { 0x20, 0x20, 0x40, 0x80, 0x80, /* 0x2F slash */ 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /* 0x30 zero */ //Edited for aesthetics 0xC0, 0x40, 0x40, 0x40, 0xE0, /* 0x31 one */ //Edited for aesthetics - 0xC0, 0x20, 0x40, 0x80, 0xE0, /* 0x32 two */ - 0xC0, 0x20, 0x40, 0x20, 0xC0, /* 0x33 three */ + 0xE0, 0x20, 0xE0, 0x80, 0xE0, /* 0x32 two */ //Edited for aesthetics + 0xE0, 0x20, 0xE0, 0x20, 0xE0, /* 0x33 three */ //Edited for aesthetics 0xA0, 0xA0, 0xE0, 0x20, 0x20, /* 0x34 four */ - 0xE0, 0x80, 0xC0, 0x20, 0xC0, /* 0x35 five */ - 0x60, 0x80, 0xE0, 0xA0, 0xE0, /* 0x36 six */ - 0xE0, 0x20, 0x40, 0x80, 0x80, /* 0x37 seven */ + 0xE0, 0x80, 0xE0, 0x20, 0xE0, /* 0x35 five */ //Edited for aesthetics + 0xE0, 0x80, 0xE0, 0xA0, 0xE0, /* 0x36 six */ //Edited for aesthetics + 0xE0, 0x20, 0x20, 0x20, 0x20, /* 0x37 seven */ //Edited for aesthetics 0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /* 0x38 eight */ - 0xE0, 0xA0, 0xE0, 0x20, 0xC0, /* 0x39 nine */ + 0xE0, 0xA0, 0xE0, 0x20, 0xE0, /* 0x39 nine */ //Edited for aesthetics 0x80, 0x00, 0x80, /* 0x3A colon */ 0x40, 0x00, 0x40, 0x80, /* 0x3B semicolon */ //edited 0x00, 0x40, 0x80, 0x40, 0x00, /* 0x3C less */ From a69ac37abeaab6f3e51f9b5680f8ae30681321fd Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 23:11:41 +1100 Subject: [PATCH 133/469] Update encoder values when toggling page mode --- avr/cores/megacommand/MCL/FXPage.cpp | 11 ++++++++++- avr/cores/megacommand/MCL/FXPage.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index b38e0afc3..25e4a3817 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -14,6 +14,9 @@ void FXPage::init() { oled_display.setFont(); #endif md_exploit.off(); + update_encoders(); +} +void FXPage::update_encoders() { for (uint8_t n = 0; n < GUI_NUM_ENCODERS; n++) { uint8_t a = ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS) + n; @@ -42,7 +45,9 @@ void FXPage::init() { encoders[n]->old = encoders[n]->cur; } + } + void FXPage::cleanup() { // md_exploit.off(); #ifdef OLED_DISPLAY @@ -115,8 +120,11 @@ void FXPage::display() { param_name = fx_param_name(fx_type, fx_param); m_strncpy_p(str, param_name, 4); - mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); + // mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); + + mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); } + oled_display.display(); @@ -175,6 +183,7 @@ bool FXPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { page_mode = !(page_mode); + update_encoders(); } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { diff --git a/avr/cores/megacommand/MCL/FXPage.h b/avr/cores/megacommand/MCL/FXPage.h index 1f1312259..32cfe6369 100644 --- a/avr/cores/megacommand/MCL/FXPage.h +++ b/avr/cores/megacommand/MCL/FXPage.h @@ -39,6 +39,8 @@ class FXPage : public LightPage, MidiCallback { void loop(); void cleanup(); + void update_encoders(); + void setup_callbacks(); void remove_callbacks(); From de2f360ae13720bfd0799dd5c62af5046eea5701 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 23:13:56 +1100 Subject: [PATCH 134/469] Fix array out of bounds --- avr/cores/megacommand/MCL/FXPage.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 25e4a3817..3e51b5881 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -20,26 +20,26 @@ void FXPage::update_encoders() { for (uint8_t n = 0; n < GUI_NUM_ENCODERS; n++) { uint8_t a = ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS) + n; - uint8_t fx_param = params[n].param; + uint8_t fx_param = params[a].param; DEBUG_PRINTLN(params[n].param); - switch (params[n].type) { + switch (params[a].type) { case MD_FX_ECHO: DEBUG_PRINTLN("setting delay"); DEBUG_PRINTLN(n); DEBUG_PRINTLN(fx_param); - encoders[a]->cur = MD.kit.delay[fx_param]; + encoders[n]->cur = MD.kit.delay[fx_param]; break; case MD_FX_REV: DEBUG_PRINTLN("setting reverb"); DEBUG_PRINTLN(n); - encoders[a]->cur = MD.kit.reverb[fx_param]; + encoders[n]->cur = MD.kit.reverb[fx_param]; break; case MD_FX_EQ: - encoders[a]->cur = MD.kit.eq[fx_param]; + encoders[n]->cur = MD.kit.eq[fx_param]; break; case MD_FX_DYN: - encoders[a]->cur = MD.kit.dynamics[fx_param]; + encoders[n]->cur = MD.kit.dynamics[fx_param]; break; } From ff94dccb2db0b31f85e6c503126850d7054b6852 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 23:17:29 +1100 Subject: [PATCH 135/469] Fix text smudging on PageSelect page for oled --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 2862386e8..5b4b4ae1c 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -119,6 +119,9 @@ void PageSelectPage::loop() { } void PageSelectPage::display() { + #ifdef OLED_DISPLAY + oled_display.clearDisplay(); + #endif GUI.setLine(GUI.LINE1); char str[16]; get_page(page_select, str); From 0c957ab1b1be3cbffdd9320c84b71705d036888f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 24 Oct 2019 23:18:21 +1100 Subject: [PATCH 136/469] Revert FXPage to draw_light_encoder. draw_md_encoder left as example in comment --- avr/cores/megacommand/MCL/FXPage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 3e51b5881..ed0f5efb6 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -120,11 +120,11 @@ void FXPage::display() { param_name = fx_param_name(fx_type, fx_param); m_strncpy_p(str, param_name, 4); - // mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); + mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); - mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); + // mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); } - + oled_display.display(); From 62218a11fc2a7d0050922b1a64501c3cf2447ebd Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 25 Oct 2019 00:28:08 +0800 Subject: [PATCH 137/469] cleanup --- avr/cores/megacommand/GUI/Pages.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/avr/cores/megacommand/GUI/Pages.cpp b/avr/cores/megacommand/GUI/Pages.cpp index 1e12057f4..db02f75da 100644 --- a/avr/cores/megacommand/GUI/Pages.cpp +++ b/avr/cores/megacommand/GUI/Pages.cpp @@ -4,7 +4,6 @@ #include "Pages.hh" #include "WProgram.h" -uint16_t LightPage::encoders_used_clock[GUI_NUM_ENCODERS]; /** * \addtogroup GUI * From f7a796d92297f2453375fc7707c952113c33acf3 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 25 Oct 2019 00:33:44 +0800 Subject: [PATCH 138/469] square trig buttons --- avr/cores/megacommand/MCL/SeqPage.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 56d61a1a3..49f13deb6 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -468,7 +468,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(trig_x, trig_y, seq_w, trig_h + 1, WHITE); + oled_display.fillRect(trig_x - 1, trig_y, seq_w + 2, trig_h + 1, WHITE); } else if (!in_range) { // don't draw } else { @@ -478,10 +478,6 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, } else { oled_display.drawRect(trig_x, trig_y, seq_w, trig_h, WHITE); } - oled_display.drawPixel(trig_x, trig_y, BLACK); - oled_display.drawPixel(trig_x, trig_y + trig_h - 1, BLACK); - oled_display.drawPixel(trig_x + seq_w - 1, trig_y, BLACK); - oled_display.drawPixel(trig_x + seq_w - 1, trig_y + trig_h - 1, BLACK); } trig_x += seq_w + 1; From b5eb412a54d29ad19f436152910814e52f0aa39e Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 25 Oct 2019 00:35:56 +0800 Subject: [PATCH 139/469] GridPage first vertical grid line layout update; remove row indicator --- avr/cores/megacommand/MCL/GridPage.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index fffd22d49..87b26203b 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -407,11 +407,6 @@ void GridPage::display_grid() { } for (uint8_t y = 0; y < MAX_VISIBLE_ROWS; y++) { - if ((y + row_shift == cur_row)) { - oled_display.setCursor(x_offset - 6, y_offset + y * 8); - oled_display.print(">"); - } - auto cur_posx = x_offset; auto cur_posy = y_offset + y * 8; for (uint8_t x = col_shift; x < MAX_VISIBLE_COLS + col_shift; x++) { @@ -480,7 +475,7 @@ void GridPage::display_grid() { // optionally, draw the first separator if ((getCol() - cur_col + col_shift) % 4 == 0) { - mcl_gui.draw_vertical_dashline(x_offset - 2, 3); + mcl_gui.draw_vertical_dashline(x_offset - 3, 3); } #endif } From 990d9f65182b415c4996bcda6b6dfb8f75fd29e7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 25 Oct 2019 10:55:17 +1100 Subject: [PATCH 140/469] Bug Fix. FXEncoders would not transmit current value --- avr/cores/megacommand/MCL/FXPage.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index ed0f5efb6..918c07040 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -73,9 +73,8 @@ void FXPage::loop() { for (val = encoders[i]->old; val > encoders[i]->cur; val--) { MD.sendFXParam(fx_param, val, fx_type); } -#else - MD.sendFXParam(fx_param, encoders[i]->cur, fx_type); #endif + MD.sendFXParam(fx_param, encoders[i]->cur, fx_type); } } } From d055597abd85fb8b6eabcb29d1701a15a93873db Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 25 Oct 2019 13:06:25 +1100 Subject: [PATCH 141/469] Add exponential, tri and sine lfo shapes --- avr/cores/megacommand/MCL/AuxPages.cpp | 3 + avr/cores/megacommand/MCL/AuxPages.h | 3 + avr/cores/megacommand/MCL/LFO.cpp | 56 +++++++ avr/cores/megacommand/MCL/LFO.h | 40 +++++ avr/cores/megacommand/MCL/LFOPage.cpp | 160 +++++++++++++++++++ avr/cores/megacommand/MCL/PageSelectPage.cpp | 6 + 6 files changed, 268 insertions(+) create mode 100644 avr/cores/megacommand/MCL/LFO.cpp create mode 100644 avr/cores/megacommand/MCL/LFO.h create mode 100644 avr/cores/megacommand/MCL/LFOPage.cpp diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index e308ccaa5..e80d0dd4f 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -43,3 +43,6 @@ FXPage fx_page_a(&fx_param1, &fx_param2, &fx_param3, &fx_param4, FXPage fx_page_b(&fx_param1, &fx_param2, &fx_param3, &fx_param4, (fx_param_t*) &fx_reverb_params, 8); + +LFOPage lfo_page(&fx_param1, &fx_param2, &fx_param3, &fx_param4); + diff --git a/avr/cores/megacommand/MCL/AuxPages.h b/avr/cores/megacommand/MCL/AuxPages.h index 706ba58d1..44ad4eace 100644 --- a/avr/cores/megacommand/MCL/AuxPages.h +++ b/avr/cores/megacommand/MCL/AuxPages.h @@ -4,6 +4,7 @@ #define AUXPAGES_H__ #include "MCLEncoder.h" +#include "LFOPage.h" #include "MixerPage.h" #include "RoutePage.h" #include "RAMPage.h" @@ -31,4 +32,6 @@ extern MCLEncoder fx_param4; extern FXPage fx_page_a; extern FXPage fx_page_b; +extern LFOPage lfo_page; + #endif /* AUXPAGES_H__ */ diff --git a/avr/cores/megacommand/MCL/LFO.cpp b/avr/cores/megacommand/MCL/LFO.cpp new file mode 100644 index 000000000..de7901ee5 --- /dev/null +++ b/avr/cores/megacommand/MCL/LFO.cpp @@ -0,0 +1,56 @@ +/* Copyright Justin Mammarella jmamma@gmail.com 2018 */ + +#include "Math.h" +#include "LFO.h" +#define DIV_1_127 (1.00 / MAX) +//Exponential Rise Formua: +//y = M * (1-e^(-x/a)); +//M = Maximum +//a = time constant. +//0.63M = M * (1 - e^(-x/a)) +//0.63 = (1 - e^(-x/a)) +//e^(-x/a) = ( 1 - 0.63) +//e^(-x/a) = 0.37 +// -x/a = ln(0.37) [1] +// +//at t = M, y = M - 1 +// +//(M - 1 ) = M * (1 - ^e(-M/a)) +//M - 1 = M - Me^(-M/a) +//e^(-M/a) = 1/M +//ln(1/M) = -M/a +//a = - M / (ln(1/M) [2] +// +//For M = 127. a = 26 +// +// + +#define MAX 127 + +uint8_t ExpLFO::get_sample(uint8_t sample_number) { + uint8_t y = MAX - (uint8_t) ((float) MAX * powf(M_E, (float) -1 * (float) sample_number * (float)time_constant)); + return y; +} + +uint8_t TriLFO::get_sample(uint8_t sample_number) { + uint8_t y; + if (sample_number > 63) { + y = (127 - sample_number) * 2; + } + else { + y = (sample_number * 2); + } + return y; +} + +uint8_t SinLFO::get_sample(uint8_t sample_number) { + float sample_duration = (float)1 / (float)127; + // float sample_duration = (float) 1 / (float) freq; + return (float) (MAX / 2.0) * cos(2 * PI * 1 * sample_number * sample_duration) + (float) (MAX / 2.0); +} + + +uint8_t LFO::get_sample(uint8_t sample_number) { + return 0; +} + diff --git a/avr/cores/megacommand/MCL/LFO.h b/avr/cores/megacommand/MCL/LFO.h new file mode 100644 index 000000000..89b746675 --- /dev/null +++ b/avr/cores/megacommand/MCL/LFO.h @@ -0,0 +1,40 @@ +/* Copyright Justin Mammarella jmamma@gmail.com 2018 */ + +#ifndef LFOOSC_H__ +#define LFOOSC_H__ + +#include "MCL.h" +#include "Math.h" + +#define EXP_LFO 1 + +class LFO { +public: + virtual uint8_t get_sample(uint8_t sample_number); + +}; + +class ExpLFO : public LFO { + + float time_constant; +public: + ExpLFO(float time_constant_ = 20) { time_constant = 1.00 / time_constant_; } + uint8_t get_sample(uint8_t sample_number); +}; + +class TriLFO : public LFO { + +public: + TriLFO() { } + uint8_t get_sample(uint8_t sample_number); +}; + +class SinLFO : public LFO { + +public: + SinLFO() { } + uint8_t get_sample(uint8_t sample_number); +}; + + +#endif /* LFOOSC_H__ */ diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp new file mode 100644 index 000000000..56dc49879 --- /dev/null +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -0,0 +1,160 @@ +#include "LFOPage.h" +#include "MCL.h" +#include "MCLSeq.h" + +#define LFO_TYPE 0 +#define LFO_PARAM 1 +#define INTERPOLATE +#define DIV_1_127 .0079 +void LFOPage::setup() { DEBUG_PRINT_FN(); } + +void LFOPage::init() { + DEBUG_PRINT_FN(); +#ifdef OLED_DISPLAY + classic_display = false; + oled_display.clearDisplay(); + oled_display.setFont(); +#endif + md_exploit.off(); +} +void LFOPage::cleanup() { + // md_exploit.off(); +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif +} + +void LFOPage::loop() { + +} +void LFOPage::display() { + + if (!classic_display) { +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif + } +#ifndef OLED_DISPLAY + GUI.clearLines(); + GUI.setLine(GUI.LINE1); + uint8_t x; + + GUI.put_string_at(0, "LFO"); + GUI.put_value_at1(4, page_mode ? 1 : 0); + GUI.setLine(GUI.LINE2); + /* + if (mcl_cfg.ram_page_mode == 0) { + GUI.put_string_at(0, "MON"); + } else { + GUI.put_string_at(0, "LNK"); + } + */ + +#endif +#ifdef OLED_DISPLAY + oled_display.setFont(); + oled_display.setCursor(0, 0); + + oled_display.print("LFO "); + oled_display.print(page_mode ? 1 : 0); + oled_display.print(" "); +/* + PGM_P param_name = NULL; + char str[4]; + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { + uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); + + uint8_t fx_param = params[n].param; + uint8_t fx_type = params[n].type; + param_name = fx_param_name(fx_type, fx_param); + m_strncpy_p(str, param_name, 4); + + mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); + + // mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); + } + +*/ + uint8_t x = 0; + uint8_t h = 30; + uint8_t y = 0; + + for (uint8_t n = 0; n < 127; n++) { + oled_display.drawPixel(x + n, (float) 32 - mcl_seq.my_lfo[n] * ((float) h * (float)DIV_1_127), WHITE); + if (n % 2 == 0) { + oled_display.drawPixel(x + n, (h / 2) + y, WHITE); + } + + } + + oled_display.display(); + +#endif +} + +void LFOPage::onControlChangeCallback_Midi(uint8_t *msg) { + uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); + uint8_t param = msg[1]; + uint8_t value = msg[2]; + uint8_t track; + uint8_t track_param; + // If external keyboard controlling MD pitch, send parameter updates + // to all polyphonic tracks + uint8_t param_true = 0; + + MD.parseCC(channel, param, &track, &track_param); +} + +void LFOPage::setup_callbacks() { + if (midi_state) { + return; + } + Midi.addOnControlChangeCallback( + this, (midi_callback_ptr_t)&LFOPage::onControlChangeCallback_Midi); + + midi_state = true; +} + +void LFOPage::remove_callbacks() { + if (!midi_state) { + return; + } + + Midi.removeOnControlChangeCallback( + this, (midi_callback_ptr_t)&LFOPage::onControlChangeCallback_Midi); + + midi_state = false; +} + +bool LFOPage::handleEvent(gui_event_t *event) { + if (note_interface.is_event(event)) { + uint8_t track = event->source - 128; + if (midi_active_peering.get_device(event->port) != DEVICE_MD) { + return true; + } + } + if (event->mask == EVENT_BUTTON_RELEASED) { + return true; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || + EVENT_PRESSED(event, Buttons.ENCODER2) || + EVENT_PRESSED(event, Buttons.ENCODER3) || + EVENT_PRESSED(event, Buttons.ENCODER4)) { + GUI.setPage(&grid_page); + } + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + page_mode = !(page_mode); + } + + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + } + + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + } + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + GUI.setPage(&page_select_page); + return true; + } + + return false; +} diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 5b4b4ae1c..788d63fc0 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -10,6 +10,7 @@ #define FX_PAGE_A 10 #define FX_PAGE_B 11 #define LOUDNESS 9 +#define LFO_PAGE 6 void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -56,6 +57,11 @@ LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { strncpy(str, "REVERB", 8); r_page = &fx_page_b; break; + case LFO_PAGE: + if (str) + strncpy(str, "LFO", 8); + r_page = &lfo_page; + break; #ifdef WAV_DESIGNER case WAVD_PAGE: if (str) From 04c08d21a11e3e2389690f86a29c7f4869b8eaa2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 25 Oct 2019 17:14:37 +1100 Subject: [PATCH 142/469] Created LFOSeqTrack class and associated functionality --- avr/cores/megacommand/MCL/LFO.cpp | 4 +- avr/cores/megacommand/MCL/LFO.h | 1 + avr/cores/megacommand/MCL/LFOPage.cpp | 5 ++- avr/cores/megacommand/MCL/LFOPage.h | 44 +++++++++++++++++++++ avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 43 +++++++++++++++++++++ avr/cores/megacommand/MCL/LFOSeqTrack.h | 47 +++++++++++++++++++++++ avr/cores/megacommand/MCL/MCLMemory.h | 2 +- avr/cores/megacommand/MCL/MCLSeq.cpp | 35 +++++++++++++++++ avr/cores/megacommand/MCL/MCLSeq.h | 15 ++++---- 9 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 avr/cores/megacommand/MCL/LFOPage.h create mode 100644 avr/cores/megacommand/MCL/LFOSeqTrack.cpp create mode 100644 avr/cores/megacommand/MCL/LFOSeqTrack.h diff --git a/avr/cores/megacommand/MCL/LFO.cpp b/avr/cores/megacommand/MCL/LFO.cpp index de7901ee5..c407e937e 100644 --- a/avr/cores/megacommand/MCL/LFO.cpp +++ b/avr/cores/megacommand/MCL/LFO.cpp @@ -34,7 +34,7 @@ uint8_t ExpLFO::get_sample(uint8_t sample_number) { uint8_t TriLFO::get_sample(uint8_t sample_number) { uint8_t y; - if (sample_number > 63) { + if (sample_number > LFO_LENGTH / 2) { y = (127 - sample_number) * 2; } else { @@ -44,7 +44,7 @@ uint8_t TriLFO::get_sample(uint8_t sample_number) { } uint8_t SinLFO::get_sample(uint8_t sample_number) { - float sample_duration = (float)1 / (float)127; + float sample_duration = (float)1 / (float)LFO_LENGTH; // float sample_duration = (float) 1 / (float) freq; return (float) (MAX / 2.0) * cos(2 * PI * 1 * sample_number * sample_duration) + (float) (MAX / 2.0); } diff --git a/avr/cores/megacommand/MCL/LFO.h b/avr/cores/megacommand/MCL/LFO.h index 89b746675..77b44a5e9 100644 --- a/avr/cores/megacommand/MCL/LFO.h +++ b/avr/cores/megacommand/MCL/LFO.h @@ -7,6 +7,7 @@ #include "Math.h" #define EXP_LFO 1 +#define LFO_LENGTH 128 class LFO { public: diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 56dc49879..f16ad766b 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -1,4 +1,5 @@ #include "LFOPage.h" +#include "LFO.h" #include "MCL.h" #include "MCLSeq.h" @@ -79,8 +80,8 @@ void LFOPage::display() { uint8_t h = 30; uint8_t y = 0; - for (uint8_t n = 0; n < 127; n++) { - oled_display.drawPixel(x + n, (float) 32 - mcl_seq.my_lfo[n] * ((float) h * (float)DIV_1_127), WHITE); + for (uint8_t n = 0; n < LFO_LENGTH; n++) { + oled_display.drawPixel(x + n, (float) 32 - mcl_seq.lfo_tracks[0].wav_table[n] * ((float) h * (float)DIV_1_127), WHITE); if (n % 2 == 0) { oled_display.drawPixel(x + n, (h / 2) + y, WHITE); } diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h new file mode 100644 index 000000000..024085b46 --- /dev/null +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -0,0 +1,44 @@ +/* Justin Mammarella jmamma@gmail.com 2018 */ + +#ifndef LFOPAGE_H__ +#define LFOPAGE_H__ + +#include "GUI.h" +#include "MCLEncoder.h" + +#define NUM_LFO_PAGES 2 + +// +class LFOPage : public LightPage, MidiCallback { +public: + LFOPage(Encoder *e1 = NULL, Encoder *e2 = NULL, + Encoder *e3 = NULL, Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) { + } + + bool handleEvent(gui_event_t *event); + bool midi_state = false; + + bool page_mode; + uint8_t page_id; + + void display(); + void setup(); + void init(); + void loop(); + void cleanup(); + + void update_encoders(); + + void setup_callbacks(); + void remove_callbacks(); + + void onControlChangeCallback_Midi(uint8_t *msg); +}; + +extern MCLEncoder lfo_page_param1; +extern MCLEncoder lfo_page_param2; +extern MCLEncoder lfo_page_param3; +extern MCLEncoder lfo_page_param4; + +#endif /* LFOPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp new file mode 100644 index 000000000..a45cd3be5 --- /dev/null +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -0,0 +1,43 @@ +#include "LFOSeqTrack.h" +#include "LFO.h" +#include "MCL.h" + +void LFOSeqTrack::seq() { + + if ((MidiClock.mod12_counter == 0) && (mode != LFO_MODE_FREE) && + IS_BIT_SET64(pattern_mask, step_count)) { + sample_count = 0; + } + if (enable) { + for (uint8_t i = 0; i < NUM_OF_LFO_PARAMS; i++) { + + // MD CC LFO + if (params[i].dest < NUM_MD_TRACKS) { + MD.setTrackParam_inline(params[i].dest, params[i].param, + wav_table[sample_count]); + } + // MD FX LFO + else if (params[i].dest != 255) { + MD.sendFXParam(params[i].param, wav_table[sample_count], params[i].dest); + } + } + } + + sample_count += speed; + if (sample_count > LFO_LENGTH) { + // Free running LFO should reset, oneshot should hold at last value. + if (mode == LFO_MODE_ONE) { + sample_count = LFO_LENGTH; + } else { + sample_count = 0; + } + } + + if (MidiClock.mod12_counter == 11) { + if (step_count == length - 1) { + step_count = 0; + } else { + step_count++; + } + } +} diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h new file mode 100644 index 000000000..b8c3c462e --- /dev/null +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -0,0 +1,47 @@ +/* Copyright Justin Mammarella jmamma@gmail.com 2018 */ + +#ifndef LFOSEQTRACK_H__ +#define LFOSEQTRACK_H__ +#include "WProgram.h" +#include "LFO.h" + +#define NUM_OF_LFO_PARAMS 2 + +// LFO is free running as is never reset +#define LFO_MODE_FREE 0 +// LFO resets on trig but plays continously. +#define LFO_MODE_TRIG 1 +// LFO resets on trig but only plays 1 cycle +#define LFO_MODE_ONE 2 + +typedef struct seq_lfo_params_t { + uint8_t dest; + uint8_t param; + uint8_t depth; +} seq_lfo_params_t; + +class LFOSeqTrack { +public: + uint8_t wav_table[LFO_LENGTH]; + uint8_t sample_count; + uint8_t speed = 1; + uint8_t mode; + + uint8_t length = 16; + uint8_t step_count; + uint64_t pattern_mask; + + bool enable = false; + + seq_lfo_params_t params[NUM_OF_LFO_PARAMS]; + LFOSeqTrack() { init(); }; + + void init() { + for (uint8_t a = 0; a < NUM_OF_LFO_PARAMS; a++) { + params[a].dest = 255; + } + } + ALWAYS_INLINE() void seq(); +}; + +#endif /* LFOSEQTRACK_H__ */ diff --git a/avr/cores/megacommand/MCL/MCLMemory.h b/avr/cores/megacommand/MCL/MCLMemory.h index 382b49e9a..d8f3ca55a 100644 --- a/avr/cores/megacommand/MCL/MCLMemory.h +++ b/avr/cores/megacommand/MCL/MCLMemory.h @@ -18,7 +18,7 @@ #define NUM_EXT_TRACKS NUM_A4_TRACKS -#define NUM_LFO_TRACKS 4UL +#define NUM_LFO_TRACKS 2UL #define NUM_TRACKS (NUM_MD_TRACKS + NUM_A4_TRACKS) #define NUM_FILE_ENTRIES 256UL diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index fda921a6e..9e5980718 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -1,5 +1,7 @@ #include "MCL.h" #include "MCLSeq.h" +#include "LFO.h" + void MCLSeq::setup() { for (uint8_t i = 0; i < NUM_PARAM_PAGES; i++) { @@ -31,6 +33,21 @@ void MCLSeq::setup() { #endif // MidiClock.addOnClockCallback(this, // (midi_clock_callback_ptr_t)&MDSequencer::MDSetup); + + //SinLFO tri_lfo; + ExpLFO exp_lfo(20); + for (uint8_t n = 0; n < LFO_LENGTH; n++) { + lfo_tracks[0].wav_table[n] = exp_lfo.get_sample(n); + } + + lfo_tracks[0].enable = true; + lfo_tracks[0].params[0].dest = MD_FX_ECHO; + lfo_tracks[0].params[0].param = MD_ECHO_LEV; + lfo_tracks[0].mode = LFO_MODE_ONE; + for (uint8_t n = 0; n < 64; n += 4) { + SET_BIT64(lfo_tracks[0].pattern_mask, n); + } + enable(); MidiClock.addOnMidiStopCallback( @@ -84,6 +101,12 @@ void MCLSeq::onMidiStartImmediateCallback() { md_tracks[i].oneshot_mask = 0; } + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + + lfo_tracks[i].step_count = 0; + } + + } void MCLSeq::onMidiStartCallback() { @@ -128,6 +151,18 @@ void MCLSeq::seq() { } #endif // } +#ifdef EXT_TRACKS + for (uint8_t i = 0; i < num_ext_tracks; i++) { + ext_tracks[i].seq(); + } +#endif + + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + lfo_tracks[i].seq(); + } +// if (MidiClock.step_counter == 1) { +// lfo_sample = 0; +// } } #ifdef MEGACOMMAND #pragma GCC pop_options diff --git a/avr/cores/megacommand/MCL/MCLSeq.h b/avr/cores/megacommand/MCL/MCLSeq.h index b34dce8e6..14b795780 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.h +++ b/avr/cores/megacommand/MCL/MCLSeq.h @@ -3,11 +3,12 @@ #ifndef MCLSEQUENCER_H__ #define MCLSEQUENCER_H__ -#include "SeqPages.h" #include "ExtSeqTrack.h" +#include "LFOSeqTrack.h" +#include "MCLMemory.h" #include "MDSeqTrack.h" +#include "SeqPages.h" #include "midi-common.hh" -#include "MCLMemory.h" //#include "MDTrack.h" #define SEQ_MUTE_ON 1 @@ -27,18 +28,19 @@ class MCLSeqMidiEvents : public MidiCallback { void onControlChangeCallback_Midi2(uint8_t *msg); }; - class MCLSeq : public ClockCallback { public: - uint8_t num_md_tracks = NUM_MD_TRACKS; - MDSeqTrack md_tracks[NUM_MD_TRACKS]; + MDSeqTrack md_tracks[NUM_MD_TRACKS]; #ifdef EXT_TRACKS ExtSeqTrack ext_tracks[NUM_EXT_TRACKS]; uint8_t num_ext_tracks = NUM_EXT_TRACKS; #endif + LFOSeqTrack lfo_tracks[NUM_LFO_TRACKS]; + uint8_t num_lfo_tracks = NUM_LFO_TRACKS; + MCLSeqMidiEvents midi_events; bool state = false; @@ -46,16 +48,13 @@ class MCLSeq : public ClockCallback { void enable(); void disable(); - void onMidiStartCallback(); void onMidiStartImmediateCallback(); void onMidiContinueCallback(); void onMidiStopCallback(); void seq(); - }; extern MCLSeq mcl_seq; - #endif /* MCLSEQUENCER_H__ */ From ff45457c1669af67ccbf41b824218f85d685e5f2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 25 Oct 2019 22:05:39 +1100 Subject: [PATCH 143/469] Add sample_hold to control lfo playback speed. --- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 4 +++- avr/cores/megacommand/MCL/LFOSeqTrack.h | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index a45cd3be5..3aced358b 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -23,7 +23,9 @@ void LFOSeqTrack::seq() { } } - sample_count += speed; + sample_hold += 1; + if (sample_hold == speed) { sample_hold = 0; sample_count += 1; } + if (sample_count > LFO_LENGTH) { // Free running LFO should reset, oneshot should hold at last value. if (mode == LFO_MODE_ONE) { diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index b8c3c462e..2b0b552a0 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -3,7 +3,6 @@ #ifndef LFOSEQTRACK_H__ #define LFOSEQTRACK_H__ #include "WProgram.h" -#include "LFO.h" #define NUM_OF_LFO_PARAMS 2 @@ -22,9 +21,11 @@ typedef struct seq_lfo_params_t { class LFOSeqTrack { public: - uint8_t wav_table[LFO_LENGTH]; + uint8_t wav_table[128]; uint8_t sample_count; - uint8_t speed = 1; + uint8_t sample_hold = 0; + + uint8_t speed = 0; uint8_t mode; uint8_t length = 16; From f33e0040a9af816d731ce6a7ff6fd6ba27c294b4 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 26 Oct 2019 00:56:30 +0800 Subject: [PATCH 144/469] extend knob frame to mark sequencer beats --- avr/cores/megacommand/MCL/SeqPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 49f13deb6..d99aa343e 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -738,6 +738,7 @@ void SeqPage::draw_knob_frame() { for (uint8_t x = knob_x0; x <= knob_xend; x += knob_w) { mcl_gui.draw_vertical_dashline(x, 0, knob_y); oled_display.drawPixel(x, knob_y, WHITE); + oled_display.drawPixel(x, knob_y + 1, WHITE); } mcl_gui.draw_horizontal_dashline(knob_y, knob_x0 + 1, knob_xend + 1); } From 7580007093ee1a6386d91763f064afff13fc6b39 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 26 Oct 2019 01:50:28 +0800 Subject: [PATCH 145/469] use draw_light_encoder, minor layout refinement --- avr/cores/megacommand/MCL/SeqPage.cpp | 6 +----- avr/cores/megacommand/MCL/SeqPage.h | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index d99aa343e..741e3ed38 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -757,11 +757,7 @@ void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* title) { uint8_t x = knob_x0 + i * knob_w; - oled_display.setFont(&TomThumb); - oled_display.setTextColor(WHITE); - oled_display.setCursor(x + 4, 7); - oled_display.print(title); - mcl_gui.draw_encoder(x + 7, 8, enc); + mcl_gui.draw_light_encoder(x + 6, 6, enc, title); } void SeqPageMidiEvents::setup_callbacks() { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 4b47c8b10..3fd4ede96 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -88,7 +88,7 @@ class SeqPage : public LightPage { static constexpr uint8_t knob_x0 = 31; static constexpr uint8_t knob_w = 24; static constexpr uint8_t knob_xend = 127; - static constexpr uint8_t knob_y = 19; + static constexpr uint8_t knob_y = 20; }; #endif /* SEQPAGE_H__ */ From 9e4030bd9068b262076260d231ef786db19f3980 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 26 Oct 2019 02:19:24 +0800 Subject: [PATCH 146/469] implement chromatic page --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 69 +++++++++++++++++++++++- avr/cores/megacommand/MCL/SeqPtcPage.h | 14 ++--- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 3f9b6cc46..cfdc82ab2 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -66,10 +66,31 @@ void SeqPtcPage::init() { md_exploit.off(); last_md_track = MD.currentTrack; } + curpage = SEQ_PTC_PAGE; + + config(); +} + +void SeqPtcPage::config() +{ config_encoders(); encoders[1]->cur = 32; encoders[0]->cur = 1; - curpage = SEQ_PTC_PAGE; + + // config info labels + const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); + const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); + + constexpr uint8_t len1 = sizeof(info1); + + char buf[len1] = {'\0'}; + m_strncpy_p(buf, str1, len1); + strncpy(info1, buf, len1); + strncat(info1, ">", len1); + m_strncpy_p(buf, str2, len1); + strncat(info1, buf, len1); + + strcpy(info2, "CHROMAT"); } void ptc_pattern_len_handler(Encoder *enc) { @@ -115,6 +136,8 @@ void SeqPtcPage::loop() { } #endif } + +#ifndef OLED_DISPLAY void SeqPtcPage::display() { uint8_t dev_num; if (!redisplay) { @@ -176,6 +199,50 @@ void SeqPtcPage::display() { GUI.put_value_at2(14, encoders[3]->getValue()); SeqPage::display(); } +#else +void SeqPtcPage::display() { + uint8_t dev_num; + if (!redisplay) { + return; + } + + SeqPage::display(); + + if (midi_device == DEVICE_MD) { + dev_num = last_md_track; + } +#ifdef EXT_TRACKS + else { + dev_num = last_ext_track + 16; + } +#endif + + draw_knob_frame(); + char buf1[4]; + + // draw OCTAVE + itoa(encoders[0]->getValue(), buf1, 10); + draw_knob(0, "OCT", buf1); + + // draw FREQ + if (encoders[1]->getValue() < 32) { + strcpy(buf1, "-"); + itoa(32 - encoders[1]->getValue(), buf1 + 1, 10); + } else if (encoders[1]->getValue() > 32) { + strcpy(buf1, "+"); + itoa(encoders[1]->getValue() - 32, buf1 + 1, 10); + } else { + strcpy(buf1, "0"); + } + draw_knob(1, "DET", buf1); //detune + + // draw SCALE + itoa(encoders[3]->getValue(), buf1, 10); + draw_knob(3, "SCA", buf1); + + oled_display.display(); +} +#endif uint8_t SeqPtcPage::calc_pitch(uint8_t note_num) { uint8_t size = scales[encoders[3]->cur]->size; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index f71b7bde0..4c383853e 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -36,22 +36,24 @@ class SeqPtcPage : public SeqPage { SeqPtcPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : SeqPage(e1, e2, e3, e4) {} - bool handleEvent(gui_event_t *event); uint8_t calc_poly_count(); uint8_t seq_ext_pitch(uint8_t note_num); - void display(); uint8_t get_machine_pitch(uint8_t track, uint8_t pitch); uint8_t get_next_voice(uint8_t pitch); uint8_t calc_pitch(uint8_t note_num); void trig_md(uint8_t note_num); void trig_md_fromext(uint8_t note_num); - void setup(); - void cleanup(); - void loop(); void config_encoders(); void init_poly(); - void init(); + + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void setup(); + virtual void cleanup(); + virtual void loop(); + virtual void init(); + virtual void config(); }; #endif /* SEQPTCPAGE_H__ */ From c708151284eebf68fc238eab52dea70569787159 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 26 Oct 2019 16:12:16 +1100 Subject: [PATCH 147/469] LFOs are working --- avr/cores/megacommand/MCL/LFOPage.cpp | 208 +++++++++++++++++++++- avr/cores/megacommand/MCL/LFOPage.h | 10 +- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 114 ++++++++++-- avr/cores/megacommand/MCL/LFOSeqTrack.h | 31 +++- avr/cores/megacommand/MCL/MCLSeq.cpp | 28 +-- avr/cores/megacommand/MCL/SeqPages.h | 1 - 6 files changed, 349 insertions(+), 43 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index f16ad766b..6527e6d5a 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -7,6 +7,10 @@ #define LFO_PARAM 1 #define INTERPOLATE #define DIV_1_127 .0079 + +#define LFO_DESTINATION 0 +#define LFO_SETTINGS 1 + void LFOPage::setup() { DEBUG_PRINT_FN(); } void LFOPage::init() { @@ -16,17 +20,136 @@ void LFOPage::init() { oled_display.clearDisplay(); oled_display.setFont(); #endif - md_exploit.off(); + md_exploit.on(); + update_encoders(); } void LFOPage::cleanup() { - // md_exploit.off(); + md_exploit.off(); #ifdef OLED_DISPLAY oled_display.clearDisplay(); #endif } +void LFOPage::update_encoders() { + if (page_mode == LFO_DESTINATION) { + encoders[0]->cur = mcl_seq.lfo_tracks[0].params[0].dest; + ((MCLEncoder*)encoders[0])->max = NUM_MD_TRACKS + 4; + encoders[1]->cur = mcl_seq.lfo_tracks[0].params[0].param; + ((MCLEncoder*)encoders[1])->max = 24; + encoders[2]->cur = mcl_seq.lfo_tracks[0].params[1].dest; + ((MCLEncoder*)encoders[2])->max = 16 + 4; + encoders[3]->cur = mcl_seq.lfo_tracks[0].params[1].param; + ((MCLEncoder*)encoders[3])->max = 24; + } + if (page_mode == LFO_SETTINGS) { + encoders[0]->cur = waveform; + ((MCLEncoder*)encoders[0])->max = 2; + encoders[1]->cur = mcl_seq.lfo_tracks[0].mode; + ((MCLEncoder*)encoders[1])->max = 2; + encoders[2]->cur = mcl_seq.lfo_tracks[0].speed; + ((MCLEncoder*)encoders[2])->max = 127; + encoders[3]->cur = depth; + ((MCLEncoder*)encoders[3])->max = 127; + } +} + void LFOPage::loop() { + if (page_mode == LFO_DESTINATION) { + + if (encoders[0]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[0].dest = encoders[0]->cur; + mcl_seq.lfo_tracks[0].params[0].update_offset(); + CLEAR_LOCK(); + if (encoders[0]->cur >= NUM_MD_TRACKS) { + ((MCLEncoder*)encoders[1])->max = 8; + } + + } + if (encoders[1]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[0].param = encoders[1]->cur; + mcl_seq.lfo_tracks[0].params[0].offset = mcl_seq.lfo_tracks[0].params[0].get_param_offset(encoders[0]->cur, encoders[1]->cur); + mcl_seq.lfo_tracks[0].params[0].update_offset(); + CLEAR_LOCK(); + } + + if (encoders[2]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[1].dest = encoders[2]->cur; + mcl_seq.lfo_tracks[0].params[1].update_offset(); + CLEAR_LOCK(); + if (encoders[0]->cur >= NUM_MD_TRACKS) { + ((MCLEncoder*)encoders[3])->max = 8; + } + + } + if (encoders[3]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[1].param = encoders[2]->cur; + mcl_seq.lfo_tracks[0].params[1].offset = mcl_seq.lfo_tracks[0].params[1].get_param_offset(encoders[2]->cur, encoders[3]->cur); + mcl_seq.lfo_tracks[0].params[1].update_offset(); + CLEAR_LOCK(); + } + + + } + if (page_mode == LFO_SETTINGS) { + if (encoders[0]->hasChanged()) { + waveform = encoders[0]->cur; + load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); + } + if (encoders[1]->hasChanged()) { + mcl_seq.lfo_tracks[0].mode = encoders[1]->cur; + } + + if (encoders[2]->hasChanged()) { + mcl_seq.lfo_tracks[0].speed = encoders[2]->cur; + } + + if (encoders[3]->hasChanged()) { + depth = encoders[3]->cur; + mcl_seq.lfo_tracks[0].params[0].depth = depth; + mcl_seq.lfo_tracks[0].params[1].depth = depth; + load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); + } + + } +} + +void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, uint8_t depth) { + SinLFO sin_lfo; + TriLFO tri_lfo; + ExpLFO exp_lfo; + LFO *lfo; + + switch (waveform) { + case SIN_WAV: + lfo = (LFO*) &sin_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; + break; + case TRI_WAV: + lfo = (LFO*) &tri_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; + break; + case EXP_WAV: + lfo = (LFO*) &exp_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_MAX; + break; + } + //ExpLFO exp_lfo(20); + for (uint8_t n = 0; n < LFO_LENGTH; n++) { + lfo_track->wav_table[n] = (float)lfo->get_sample(n) * ((float)depth * (float) (DIV_1_127)); + } } void LFOPage::display() { @@ -59,8 +182,8 @@ void LFOPage::display() { oled_display.print("LFO "); oled_display.print(page_mode ? 1 : 0); oled_display.print(" "); -/* - PGM_P param_name = NULL; + +/* PGM_P param_name = NULL; char str[4]; for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); @@ -74,8 +197,8 @@ void LFOPage::display() { // mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); } - */ + uint8_t x = 0; uint8_t h = 30; uint8_t y = 0; @@ -87,12 +210,61 @@ void LFOPage::display() { } } + uint8_t i = 0; + if (page_mode == LFO_DESTINATION) { + + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); + + } + if (page_mode == LFO_SETTINGS) { + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SHP"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "TYP"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SPD"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP"); + } + + draw_pattern_mask(); oled_display.display(); #endif } +void LFOPage::draw_pattern_mask() { + + uint8_t trig_x = 10; + uint8_t trig_y = 20; + uint8_t seq_w = 5; + uint8_t trig_h = 5; + + uint64_t pattern_mask = mcl_seq.lfo_tracks[0].pattern_mask; + + uint8_t offset = 0; + for (int i = 0; i < 16; i++) { + + uint8_t idx = i + offset; + bool in_range = idx < mcl_seq.lfo_tracks[0].length; + + if (note_interface.notes[i] == 1) { + // TI feedback + oled_display.fillRect(trig_x - 1, trig_y, seq_w + 2, trig_h + 1, WHITE); + } else if (!in_range) { + // don't draw + } else { + if (IS_BIT_SET64(pattern_mask, i + offset)) { + /*If the bit is set, there is a trigger at this position. */ + oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); + } else { + oled_display.drawRect(trig_x, trig_y, seq_w, trig_h, WHITE); + } + } + + trig_x += seq_w + 1; + } +} void LFOPage::onControlChangeCallback_Midi(uint8_t *msg) { uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); uint8_t param = msg[1]; @@ -129,9 +301,28 @@ void LFOPage::remove_callbacks() { bool LFOPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { + uint8_t mask = event->mask; + uint8_t port = event->port; + uint8_t device = midi_active_peering.get_device(port); + uint8_t track = event->source - 128; - if (midi_active_peering.get_device(event->port) != DEVICE_MD) { - return true; + uint8_t page_select = 0; + uint8_t step = track + (page_select * 16); + uint8_t midi_device = device; + if (event->mask == EVENT_BUTTON_PRESSED) { + if (device == DEVICE_A4) { + // GUI.setPage(&seq_extstep_page); + return true; + } + if (!IS_BIT_SET64(mcl_seq.lfo_tracks[0].pattern_mask, step)) { + + SET_BIT64(mcl_seq.lfo_tracks[0].pattern_mask, step); + } else { + DEBUG_PRINTLN("Trying to clear"); + if (clock_diff(note_interface.note_hold,slowclock) < TRIG_HOLD_TIME) { + CLEAR_BIT64(mcl_seq.lfo_tracks[0].pattern_mask, step); + } + } } } if (event->mask == EVENT_BUTTON_RELEASED) { @@ -145,7 +336,8 @@ bool LFOPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { page_mode = !(page_mode); - } + update_encoders(); + } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { } diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h index 024085b46..b5ab4b7fa 100644 --- a/avr/cores/megacommand/MCL/LFOPage.h +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -5,9 +5,13 @@ #include "GUI.h" #include "MCLEncoder.h" +#include "LFOSeqTrack.h" #define NUM_LFO_PAGES 2 +#define SIN_WAV 0 +#define TRI_WAV 1 +#define EXP_WAV 2 // class LFOPage : public LightPage, MidiCallback { public: @@ -22,13 +26,17 @@ class LFOPage : public LightPage, MidiCallback { bool page_mode; uint8_t page_id; + uint8_t waveform; + uint8_t depth; + void display(); void setup(); + void draw_pattern_mask(); void init(); void loop(); void cleanup(); - void update_encoders(); + void load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, uint8_t depth); void setup_callbacks(); void remove_callbacks(); diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index 3aced358b..a9affcc0b 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -1,7 +1,39 @@ -#include "LFOSeqTrack.h" #include "LFO.h" +#include "LFOSeqTrack.h" #include "MCL.h" +uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { + uint8_t offset = params[param].offset; + uint8_t depth = params[param].depth; + int8_t val; + + switch (offset_behaviour) { + case LFO_OFFSET_CENTRE: + val = offset + (wav_table[sample_count] - (depth / 2)); + if (val > 127) { + return 127; + } + if (val < 0) { + return 0; + } else { + return val; + } + break; + case LFO_OFFSET_MAX: + val = offset - depth + wav_table[sample_count]; + if (val > 127) { + return 127; + } + if (val < 0) { + return 0; + } else { + return val; + } + break; + } + return offset; +} + void LFOSeqTrack::seq() { if ((MidiClock.mod12_counter == 0) && (mode != LFO_MODE_FREE) && @@ -9,22 +41,27 @@ void LFOSeqTrack::seq() { sample_count = 0; } if (enable) { - for (uint8_t i = 0; i < NUM_OF_LFO_PARAMS; i++) { + for (uint8_t i = 0; i < NUM_LFO_PARAMS; i++) { - // MD CC LFO - if (params[i].dest < NUM_MD_TRACKS) { - MD.setTrackParam_inline(params[i].dest, params[i].param, - wav_table[sample_count]); - } - // MD FX LFO - else if (params[i].dest != 255) { - MD.sendFXParam(params[i].param, wav_table[sample_count], params[i].dest); + if (params[i].dest > 0) { + // MD CC LFO + if (params[i].dest <= NUM_MD_TRACKS) { + MD.setTrackParam_inline(params[i].dest - 1, params[i].param, + get_wav_value(sample_count, i)); + } + // MD FX LFO + else { + MD.sendFXParam(params[i].param, get_wav_value(sample_count, i), + MD_FX_ECHO + params[i].dest - NUM_MD_TRACKS - 1); + } } } } - sample_hold += 1; - if (sample_hold == speed) { sample_hold = 0; sample_count += 1; } + if (sample_hold >= speed) { + sample_hold = 0; + sample_count += 1; + } if (sample_count > LFO_LENGTH) { // Free running LFO should reset, oneshot should hold at last value. @@ -43,3 +80,56 @@ void LFOSeqTrack::seq() { } } } + +void LFOSeqTrack::check_and_update_params_offset(uint8_t dest, uint8_t value) { + for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { + if (params[n].dest == dest) { + params[n].offset = value; + } + } +} + +void LFOSeqTrack::reset_params_offset() { + for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { + params[n].reset_param_offset(); + } +} + +void LFOSeqTrack::update_params_offset() { + for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { + params[n].update_offset(); + } +} +void LFOSeqParam::update_offset() { offset = get_param_offset(dest, param); } +void LFOSeqParam::reset_param_offset() { reset_param(dest, param, offset); } + +void LFOSeqParam::reset_param(uint8_t dest, uint8_t param, uint8_t value) { + if (dest <= NUM_MD_TRACKS) { + MD.setTrackParam(dest - 1, param, value); + } else { + MD.sendFXParam(param, value, MD_FX_ECHO + dest - NUM_MD_TRACKS - 1); + } +} + +uint8_t LFOSeqParam::get_param_offset(uint8_t dest, uint8_t param) { + if (dest <= NUM_MD_TRACKS) { + return MD.kit.params[dest - 1][param]; + } else { + switch (dest - NUM_MD_TRACKS - 1) { + case MD_FX_ECHO - MD_FX_ECHO: + return MD.kit.delay[param]; + break; + case MD_FX_DYN - MD_FX_ECHO: + return MD.kit.dynamics[param]; + break; + + case MD_FX_REV - MD_FX_ECHO: + return MD.kit.reverb[param]; + break; + case MD_FX_EQ - MD_FX_ECHO: + return MD.kit.eq[param]; + break; + } + } + return 255; +} diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index 2b0b552a0..7659a62a1 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -4,7 +4,7 @@ #define LFOSEQTRACK_H__ #include "WProgram.h" -#define NUM_OF_LFO_PARAMS 2 +#define NUM_LFO_PARAMS 2 // LFO is free running as is never reset #define LFO_MODE_FREE 0 @@ -13,11 +13,24 @@ // LFO resets on trig but only plays 1 cycle #define LFO_MODE_ONE 2 -typedef struct seq_lfo_params_t { +// LFO will symmetrically oscillate around offset value +#define LFO_OFFSET_CENTRE 0 + +// LFO maximum value will be equal to the offset. +#define LFO_OFFSET_MAX 1 + +class LFOSeqParam { +public: uint8_t dest; uint8_t param; uint8_t depth; -} seq_lfo_params_t; + uint8_t offset; + + uint8_t get_param_offset(uint8_t dest, uint8_t param); + void reset_param(uint8_t dest, uint8_t param, uint8_t value); + void reset_param_offset(); + void update_offset(); +}; class LFOSeqTrack { public: @@ -27,18 +40,22 @@ class LFOSeqTrack { uint8_t speed = 0; uint8_t mode; + uint8_t offset_behaviour; uint8_t length = 16; uint8_t step_count; uint64_t pattern_mask; - bool enable = false; + bool enable = true; - seq_lfo_params_t params[NUM_OF_LFO_PARAMS]; + LFOSeqParam params[NUM_LFO_PARAMS]; LFOSeqTrack() { init(); }; - + ALWAYS_INLINE() uint8_t get_wav_value(uint8_t sample_count, uint8_t param); + void update_params_offset(); + void reset_params_offset(); + void check_and_update_params_offset(uint8_t dest, uint8_t value); void init() { - for (uint8_t a = 0; a < NUM_OF_LFO_PARAMS; a++) { + for (uint8_t a = 0; a < NUM_LFO_PARAMS; a++) { params[a].dest = 255; } } diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index 9e5980718..bcca732d2 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -34,20 +34,6 @@ void MCLSeq::setup() { // MidiClock.addOnClockCallback(this, // (midi_clock_callback_ptr_t)&MDSequencer::MDSetup); - //SinLFO tri_lfo; - ExpLFO exp_lfo(20); - for (uint8_t n = 0; n < LFO_LENGTH; n++) { - lfo_tracks[0].wav_table[n] = exp_lfo.get_sample(n); - } - - lfo_tracks[0].enable = true; - lfo_tracks[0].params[0].dest = MD_FX_ECHO; - lfo_tracks[0].params[0].param = MD_ECHO_LEV; - lfo_tracks[0].mode = LFO_MODE_ONE; - for (uint8_t n = 0; n < 64; n += 4) { - SET_BIT64(lfo_tracks[0].pattern_mask, n); - } - enable(); MidiClock.addOnMidiStopCallback( @@ -83,6 +69,9 @@ void MCLSeq::onMidiContinueCallback() { for (uint8_t i = 0; i < num_md_tracks; i++) { md_tracks[i].update_params(); } + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + lfo_tracks[i].update_params_offset(); + } } void MCLSeq::onMidiStartImmediateCallback() { @@ -114,6 +103,10 @@ void MCLSeq::onMidiStartCallback() { md_tracks[i].update_params(); md_tracks[i].mute_state = SEQ_MUTE_OFF; } + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + lfo_tracks[i].update_params_offset(); + } + } void MCLSeq::onMidiStopCallback() { @@ -125,6 +118,10 @@ void MCLSeq::onMidiStopCallback() { for (uint8_t i = 0; i < num_md_tracks; i++) { md_tracks[i].reset_params(); } + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + lfo_tracks[i].reset_params_offset(); + } + } #ifdef MEGACOMMAND @@ -181,6 +178,9 @@ void MCLSeqMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { if (param >= 16) { MD.parseCC(channel, param, &track, &track_param); mcl_seq.md_tracks[track].update_param(track_param, value); + for (uint8_t n = 0; n < mcl_seq.num_lfo_tracks; n++) { + mcl_seq.lfo_tracks[n].check_and_update_params_offset(track_param, value); + } } } diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 4f1d32c6b..be9142702 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -16,7 +16,6 @@ #endif #define NUM_PARAM_PAGES 2 -#define NUM_LFO_PAGES 4 extern MCLEncoder seq_param1; extern MCLEncoder seq_param2; From bbfdb4cdd86d21eccf20d836f1bd565af4c847d7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 26 Oct 2019 18:51:12 +1100 Subject: [PATCH 148/469] Add md_isometric sprite --- art/sprites/md_isometric.png | Bin 0 -> 447 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/md_isometric.png diff --git a/art/sprites/md_isometric.png b/art/sprites/md_isometric.png new file mode 100644 index 0000000000000000000000000000000000000000..82f4bc715b2327708928b3f7d17b06aa9771fff3 GIT binary patch literal 447 zcmV;w0YLtVP)Px$c}YY;R7efIl!2~-Pz*z_iU0r0YLRnk#s(KbNM5?NT{kd!t=;#$?pw9JNr!L@ zxIOVF)eq4MjsgF)b{#u%$Sp^W9CFK-I&;XSUn(BLj~AEFgZRLTN8d#S#E^Xx9Ijhoc88zWEo@+sFuNm^W0d zN4g$(`Y*fY@uB07JbJpEqq^x+#2|V+EP$s+uXywoGJrrYq?Sexy5D_!Jo+ZaK<_yK zBq;WKJo+&D5gMY8i(}7%ibwwmIf7yDtA!O$#%c5;6jm*)_|peu0e+O%TL)%2mvS89 pNPX!d!s7o6ct<~A#P73*>o Date: Sat, 26 Oct 2019 18:54:04 +1100 Subject: [PATCH 149/469] RAMPage playback should not unmute the track --- avr/cores/megacommand/MCL/MCLActions.h | 1 + avr/cores/megacommand/MCL/RAMPage.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLActions.h b/avr/cores/megacommand/MCL/MCLActions.h index 8a4d77a3f..f3aeaab09 100644 --- a/avr/cores/megacommand/MCL/MCLActions.h +++ b/avr/cores/megacommand/MCL/MCLActions.h @@ -14,6 +14,7 @@ #define STORE_IN_PLACE 0 #define STORE_AT_SPECIFIC 254 +#define TRANSITION_NORMAL 0 #define TRANSITION_MUTE 2 #define TRANSITION_UNMUTE 3 diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index efe564c65..b7f768404 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -365,7 +365,7 @@ void RAMPage::setup_ram_play(uint8_t track, uint8_t model, uint8_t pan, // mcl_seq.md_tracks[track].step_count); uint16_t next_step = (MidiClock.div16th_counter / steps) * steps + steps; grid_page.active_slots[track] = 0x7FFF; - // mcl_actions.transition_level[track] = TRANSITION_MUTE; + mcl_actions.transition_level[track] = TRANSITION_NORMAL; mcl_actions.next_transitions[track] = next_step; transition_step = next_step; record_len = (uint8_t)steps; From 4741528b32ad09db4a2d13302e1d514bb438550e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 26 Oct 2019 22:12:27 +1100 Subject: [PATCH 150/469] Add trimetric* sprites --- art/sprites/mc_trimetric.png | Bin 0 -> 210 bytes art/sprites/md_isometric.png | Bin 447 -> 0 bytes art/sprites/md_trimetric.png | Bin 0 -> 408 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/mc_trimetric.png delete mode 100644 art/sprites/md_isometric.png create mode 100644 art/sprites/md_trimetric.png diff --git a/art/sprites/mc_trimetric.png b/art/sprites/mc_trimetric.png new file mode 100644 index 0000000000000000000000000000000000000000..fc26c3d01b7ca7de72287b9025abeb3679e9df18 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&V!3HE>w#^4|7>k44ofy`glX(f`^mw{BhFA#B zoxsS~puofY@qd5V>XMbI6BIi&dv0&M5mh_ayzBk5@O1Ta JS?83{1ORjLQ)>VK literal 0 HcmV?d00001 diff --git a/art/sprites/md_isometric.png b/art/sprites/md_isometric.png deleted file mode 100644 index 82f4bc715b2327708928b3f7d17b06aa9771fff3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 447 zcmV;w0YLtVP)Px$c}YY;R7efIl!2~-Pz*z_iU0r0YLRnk#s(KbNM5?NT{kd!t=;#$?pw9JNr!L@ zxIOVF)eq4MjsgF)b{#u%$Sp^W9CFK-I&;XSUn(BLj~AEFgZRLTN8d#S#E^Xx9Ijhoc88zWEo@+sFuNm^W0d zN4g$(`Y*fY@uB07JbJpEqq^x+#2|V+EP$s+uXywoGJrrYq?Sexy5D_!Jo+ZaK<_yK zBq;WKJo+&D5gMY8i(}7%ibwwmIf7yDtA!O$#%c5;6jm*)_|peu0e+O%TL)%2mvS89 pNPX!d!s7o6ct<~A#P73*>oX1^@s6RQmj^00001b5ch_0Itp) z=>Px$Qb|NXR7efAlM4>QFbqS7#Qm>4v7%N$wY)+h7N0z;lmF2GC5aB2`ASn=p3sxg}@P9bN!o7dpF)Pyoy-K3pNypo+gd zxYg*(_`SDSD`yDD0*>5=3?L-`@9>U3_=w+o58q!LBcYUj7~2B?0000 Date: Sat, 26 Oct 2019 22:55:08 +1100 Subject: [PATCH 151/469] Fix parameter selection bugs --- avr/cores/megacommand/MCL/LFOPage.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 6527e6d5a..b1eec663e 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -35,11 +35,11 @@ void LFOPage::update_encoders() { encoders[0]->cur = mcl_seq.lfo_tracks[0].params[0].dest; ((MCLEncoder*)encoders[0])->max = NUM_MD_TRACKS + 4; encoders[1]->cur = mcl_seq.lfo_tracks[0].params[0].param; - ((MCLEncoder*)encoders[1])->max = 24; + ((MCLEncoder*)encoders[1])->max = 23; encoders[2]->cur = mcl_seq.lfo_tracks[0].params[1].dest; ((MCLEncoder*)encoders[2])->max = 16 + 4; encoders[3]->cur = mcl_seq.lfo_tracks[0].params[1].param; - ((MCLEncoder*)encoders[3])->max = 24; + ((MCLEncoder*)encoders[3])->max = 23; } if (page_mode == LFO_SETTINGS) { encoders[0]->cur = waveform; @@ -64,9 +64,11 @@ void LFOPage::loop() { mcl_seq.lfo_tracks[0].params[0].update_offset(); CLEAR_LOCK(); if (encoders[0]->cur >= NUM_MD_TRACKS) { - ((MCLEncoder*)encoders[1])->max = 8; + ((MCLEncoder*)encoders[1])->max = 7; + } + else { + ((MCLEncoder*)encoders[1])->max = 23; } - } if (encoders[1]->hasChanged()) { USE_LOCK(); @@ -86,7 +88,10 @@ void LFOPage::loop() { mcl_seq.lfo_tracks[0].params[1].update_offset(); CLEAR_LOCK(); if (encoders[0]->cur >= NUM_MD_TRACKS) { - ((MCLEncoder*)encoders[3])->max = 8; + ((MCLEncoder*)encoders[3])->max = 7; + } + else { + ((MCLEncoder*)encoders[3])->max = 23; } } @@ -94,7 +99,7 @@ void LFOPage::loop() { USE_LOCK(); SET_LOCK(); mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[1].param = encoders[2]->cur; + mcl_seq.lfo_tracks[0].params[1].param = encoders[3]->cur; mcl_seq.lfo_tracks[0].params[1].offset = mcl_seq.lfo_tracks[0].params[1].get_param_offset(encoders[2]->cur, encoders[3]->cur); mcl_seq.lfo_tracks[0].params[1].update_offset(); CLEAR_LOCK(); From 22101773c5230db21c3394ca3e8d03f5e7dbe7f9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 26 Oct 2019 23:00:22 +1100 Subject: [PATCH 152/469] Fix another encoder range bug --- avr/cores/megacommand/MCL/LFOPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index b1eec663e..1c62a397d 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -63,7 +63,7 @@ void LFOPage::loop() { mcl_seq.lfo_tracks[0].params[0].dest = encoders[0]->cur; mcl_seq.lfo_tracks[0].params[0].update_offset(); CLEAR_LOCK(); - if (encoders[0]->cur >= NUM_MD_TRACKS) { + if (encoders[0]->cur > NUM_MD_TRACKS) { ((MCLEncoder*)encoders[1])->max = 7; } else { @@ -87,7 +87,7 @@ void LFOPage::loop() { mcl_seq.lfo_tracks[0].params[1].dest = encoders[2]->cur; mcl_seq.lfo_tracks[0].params[1].update_offset(); CLEAR_LOCK(); - if (encoders[0]->cur >= NUM_MD_TRACKS) { + if (encoders[0]->cur > NUM_MD_TRACKS) { ((MCLEncoder*)encoders[3])->max = 7; } else { From fc43cf23ebff500638fd6dcebdf24d0ce7b697e0 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 26 Oct 2019 23:13:14 +1100 Subject: [PATCH 153/469] Write button now enables + disables LFO --- avr/cores/megacommand/MCL/LFOPage.cpp | 283 +++++++++++++------------- 1 file changed, 146 insertions(+), 137 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 1c62a397d..ee8c95602 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -1,5 +1,5 @@ -#include "LFOPage.h" #include "LFO.h" +#include "LFOPage.h" #include "MCL.h" #include "MCLSeq.h" @@ -24,7 +24,7 @@ void LFOPage::init() { update_encoders(); } void LFOPage::cleanup() { - md_exploit.off(); + md_exploit.off(); #ifdef OLED_DISPLAY oled_display.clearDisplay(); #endif @@ -33,127 +33,126 @@ void LFOPage::cleanup() { void LFOPage::update_encoders() { if (page_mode == LFO_DESTINATION) { encoders[0]->cur = mcl_seq.lfo_tracks[0].params[0].dest; - ((MCLEncoder*)encoders[0])->max = NUM_MD_TRACKS + 4; + ((MCLEncoder *)encoders[0])->max = NUM_MD_TRACKS + 4; encoders[1]->cur = mcl_seq.lfo_tracks[0].params[0].param; - ((MCLEncoder*)encoders[1])->max = 23; + ((MCLEncoder *)encoders[1])->max = 23; encoders[2]->cur = mcl_seq.lfo_tracks[0].params[1].dest; - ((MCLEncoder*)encoders[2])->max = 16 + 4; + ((MCLEncoder *)encoders[2])->max = 16 + 4; encoders[3]->cur = mcl_seq.lfo_tracks[0].params[1].param; - ((MCLEncoder*)encoders[3])->max = 23; + ((MCLEncoder *)encoders[3])->max = 23; } - if (page_mode == LFO_SETTINGS) { + if (page_mode == LFO_SETTINGS) { encoders[0]->cur = waveform; - ((MCLEncoder*)encoders[0])->max = 2; + ((MCLEncoder *)encoders[0])->max = 2; encoders[1]->cur = mcl_seq.lfo_tracks[0].mode; - ((MCLEncoder*)encoders[1])->max = 2; + ((MCLEncoder *)encoders[1])->max = 2; encoders[2]->cur = mcl_seq.lfo_tracks[0].speed; - ((MCLEncoder*)encoders[2])->max = 127; + ((MCLEncoder *)encoders[2])->max = 127; encoders[3]->cur = depth; - ((MCLEncoder*)encoders[3])->max = 127; + ((MCLEncoder *)encoders[3])->max = 127; } } void LFOPage::loop() { - if (page_mode == LFO_DESTINATION) { - - if (encoders[0]->hasChanged()) { - USE_LOCK(); - SET_LOCK(); - mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[0].dest = encoders[0]->cur; - mcl_seq.lfo_tracks[0].params[0].update_offset(); - CLEAR_LOCK(); - if (encoders[0]->cur > NUM_MD_TRACKS) { - ((MCLEncoder*)encoders[1])->max = 7; + if (page_mode == LFO_DESTINATION) { + + if (encoders[0]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[0].dest = encoders[0]->cur; + mcl_seq.lfo_tracks[0].params[0].update_offset(); + CLEAR_LOCK(); + if (encoders[0]->cur > NUM_MD_TRACKS) { + ((MCLEncoder *)encoders[1])->max = 7; + } else { + ((MCLEncoder *)encoders[1])->max = 23; + } } - else { - ((MCLEncoder*)encoders[1])->max = 23; + if (encoders[1]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[0].param = encoders[1]->cur; + mcl_seq.lfo_tracks[0].params[0].offset = + mcl_seq.lfo_tracks[0].params[0].get_param_offset(encoders[0]->cur, + encoders[1]->cur); + mcl_seq.lfo_tracks[0].params[0].update_offset(); + CLEAR_LOCK(); } - } - if (encoders[1]->hasChanged()) { - USE_LOCK(); - SET_LOCK(); - mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[0].param = encoders[1]->cur; - mcl_seq.lfo_tracks[0].params[0].offset = mcl_seq.lfo_tracks[0].params[0].get_param_offset(encoders[0]->cur, encoders[1]->cur); - mcl_seq.lfo_tracks[0].params[0].update_offset(); - CLEAR_LOCK(); - } - if (encoders[2]->hasChanged()) { - USE_LOCK(); - SET_LOCK(); - mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[1].dest = encoders[2]->cur; - mcl_seq.lfo_tracks[0].params[1].update_offset(); - CLEAR_LOCK(); - if (encoders[0]->cur > NUM_MD_TRACKS) { - ((MCLEncoder*)encoders[3])->max = 7; + if (encoders[2]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[1].dest = encoders[2]->cur; + mcl_seq.lfo_tracks[0].params[1].update_offset(); + CLEAR_LOCK(); + if (encoders[0]->cur > NUM_MD_TRACKS) { + ((MCLEncoder *)encoders[3])->max = 7; + } else { + ((MCLEncoder *)encoders[3])->max = 23; + } } - else { - ((MCLEncoder*)encoders[3])->max = 23; + if (encoders[3]->hasChanged()) { + USE_LOCK(); + SET_LOCK(); + mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); + mcl_seq.lfo_tracks[0].params[1].param = encoders[3]->cur; + mcl_seq.lfo_tracks[0].params[1].offset = + mcl_seq.lfo_tracks[0].params[1].get_param_offset(encoders[2]->cur, + encoders[3]->cur); + mcl_seq.lfo_tracks[0].params[1].update_offset(); + CLEAR_LOCK(); } - - } - if (encoders[3]->hasChanged()) { - USE_LOCK(); - SET_LOCK(); - mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[1].param = encoders[3]->cur; - mcl_seq.lfo_tracks[0].params[1].offset = mcl_seq.lfo_tracks[0].params[1].get_param_offset(encoders[2]->cur, encoders[3]->cur); - mcl_seq.lfo_tracks[0].params[1].update_offset(); - CLEAR_LOCK(); - } - - - } - if (page_mode == LFO_SETTINGS) { - if (encoders[0]->hasChanged()) { - waveform = encoders[0]->cur; - load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); - } - if (encoders[1]->hasChanged()) { - mcl_seq.lfo_tracks[0].mode = encoders[1]->cur; - } - - if (encoders[2]->hasChanged()) { - mcl_seq.lfo_tracks[0].speed = encoders[2]->cur; - } - - if (encoders[3]->hasChanged()) { - depth = encoders[3]->cur; - mcl_seq.lfo_tracks[0].params[0].depth = depth; - mcl_seq.lfo_tracks[0].params[1].depth = depth; - load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); } + if (page_mode == LFO_SETTINGS) { + if (encoders[0]->hasChanged()) { + waveform = encoders[0]->cur; + load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); + } + if (encoders[1]->hasChanged()) { + mcl_seq.lfo_tracks[0].mode = encoders[1]->cur; + } + if (encoders[2]->hasChanged()) { + mcl_seq.lfo_tracks[0].speed = encoders[2]->cur; + } + if (encoders[3]->hasChanged()) { + depth = encoders[3]->cur; + mcl_seq.lfo_tracks[0].params[0].depth = depth; + mcl_seq.lfo_tracks[0].params[1].depth = depth; + load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); + } } } -void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, uint8_t depth) { +void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, + uint8_t depth) { SinLFO sin_lfo; TriLFO tri_lfo; ExpLFO exp_lfo; LFO *lfo; switch (waveform) { - case SIN_WAV: - lfo = (LFO*) &sin_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; + case SIN_WAV: + lfo = (LFO *)&sin_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; break; - case TRI_WAV: - lfo = (LFO*) &tri_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; + case TRI_WAV: + lfo = (LFO *)&tri_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; break; - case EXP_WAV: - lfo = (LFO*) &exp_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_MAX; + case EXP_WAV: + lfo = (LFO *)&exp_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_MAX; break; } - //ExpLFO exp_lfo(20); + // ExpLFO exp_lfo(20); for (uint8_t n = 0; n < LFO_LENGTH; n++) { - lfo_track->wav_table[n] = (float)lfo->get_sample(n) * ((float)depth * (float) (DIV_1_127)); + lfo_track->wav_table[n] = + (float)lfo->get_sample(n) * ((float)depth * (float)(DIV_1_127)); } } void LFOPage::display() { @@ -185,51 +184,57 @@ void LFOPage::display() { oled_display.setCursor(0, 0); oled_display.print("LFO "); - oled_display.print(page_mode ? 1 : 0); + if (mcl_seq.lfo_tracks[0].enable) { + oled_display.print("ON"); + } else { + oled_display.print("OFF"); + } + // oled_display.print(page_mode ? 1 : 0); oled_display.print(" "); -/* PGM_P param_name = NULL; - char str[4]; - for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { - uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); + /* PGM_P param_name = NULL; + char str[4]; + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { + uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); - uint8_t fx_param = params[n].param; - uint8_t fx_type = params[n].type; - param_name = fx_param_name(fx_type, fx_param); - m_strncpy_p(str, param_name, 4); + uint8_t fx_param = params[n].param; + uint8_t fx_type = params[n].type; + param_name = fx_param_name(fx_type, fx_param); + m_strncpy_p(str, param_name, 4); - mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); + mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); - // mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); - } -*/ + // mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); + } + */ uint8_t x = 0; uint8_t h = 30; uint8_t y = 0; for (uint8_t n = 0; n < LFO_LENGTH; n++) { - oled_display.drawPixel(x + n, (float) 32 - mcl_seq.lfo_tracks[0].wav_table[n] * ((float) h * (float)DIV_1_127), WHITE); + oled_display.drawPixel(x + n, + (float)32 - mcl_seq.lfo_tracks[0].wav_table[n] * + ((float)h * (float)DIV_1_127), + WHITE); if (n % 2 == 0) { oled_display.drawPixel(x + n, (h / 2) + y, WHITE); } - } uint8_t i = 0; if (page_mode == LFO_DESTINATION) { - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); - + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); + } + if (page_mode == LFO_SETTINGS) { + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SHP"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "TYP"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SPD"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP"); } - if (page_mode == LFO_SETTINGS) { - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SHP"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "TYP"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SPD"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP"); - } draw_pattern_mask(); @@ -245,30 +250,30 @@ void LFOPage::draw_pattern_mask() { uint8_t seq_w = 5; uint8_t trig_h = 5; - uint64_t pattern_mask = mcl_seq.lfo_tracks[0].pattern_mask; + uint64_t pattern_mask = mcl_seq.lfo_tracks[0].pattern_mask; - uint8_t offset = 0; - for (int i = 0; i < 16; i++) { + uint8_t offset = 0; + for (int i = 0; i < 16; i++) { - uint8_t idx = i + offset; - bool in_range = idx < mcl_seq.lfo_tracks[0].length; + uint8_t idx = i + offset; + bool in_range = idx < mcl_seq.lfo_tracks[0].length; - if (note_interface.notes[i] == 1) { - // TI feedback - oled_display.fillRect(trig_x - 1, trig_y, seq_w + 2, trig_h + 1, WHITE); - } else if (!in_range) { - // don't draw + if (note_interface.notes[i] == 1) { + // TI feedback + oled_display.fillRect(trig_x - 1, trig_y, seq_w + 2, trig_h + 1, WHITE); + } else if (!in_range) { + // don't draw + } else { + if (IS_BIT_SET64(pattern_mask, i + offset)) { + /*If the bit is set, there is a trigger at this position. */ + oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); } else { - if (IS_BIT_SET64(pattern_mask, i + offset)) { - /*If the bit is set, there is a trigger at this position. */ - oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); - } else { - oled_display.drawRect(trig_x, trig_y, seq_w, trig_h, WHITE); - } + oled_display.drawRect(trig_x, trig_y, seq_w, trig_h, WHITE); } - - trig_x += seq_w + 1; } + + trig_x += seq_w + 1; + } } void LFOPage::onControlChangeCallback_Midi(uint8_t *msg) { uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); @@ -324,10 +329,10 @@ bool LFOPage::handleEvent(gui_event_t *event) { SET_BIT64(mcl_seq.lfo_tracks[0].pattern_mask, step); } else { DEBUG_PRINTLN("Trying to clear"); - if (clock_diff(note_interface.note_hold,slowclock) < TRIG_HOLD_TIME) { + if (clock_diff(note_interface.note_hold, slowclock) < TRIG_HOLD_TIME) { CLEAR_BIT64(mcl_seq.lfo_tracks[0].pattern_mask, step); } - } + } } } if (event->mask == EVENT_BUTTON_RELEASED) { @@ -337,17 +342,21 @@ bool LFOPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { - GUI.setPage(&grid_page); + GUI.setPage(&grid_page); } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { page_mode = !(page_mode); update_encoders(); - } + } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { } if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + mcl_seq.lfo_tracks[0].enable = !(mcl_seq.lfo_tracks[0].enable); + if (!mcl_seq.lfo_tracks[0].enable) { + mcl_seq.lfo_tracks[0].reset_params_offset(); + } } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { GUI.setPage(&page_select_page); From bc51cff7add9e8cf96630201ef3f960de3f4fd45 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 26 Oct 2019 21:39:27 +0800 Subject: [PATCH 154/469] chromatic page: add LEN, add keyboard --- avr/cores/megacommand/MCL/MCLGUI.cpp | 27 ++++++++++-- avr/cores/megacommand/MCL/MCLGUI.h | 1 + avr/cores/megacommand/MCL/SeqPtcPage.cpp | 53 +++++++++++++++--------- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index a39619199..8cb8d7bf3 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -231,9 +231,9 @@ bool MCLGUI::show_encoder_value(Encoder *encoder) { if (clock_diff(((LightPage *)GUI.currentPage())->encoders_used_clock[match], slowclock) < SHOW_VALUE_TIMEOUT) { return true; - } - else { - ((LightPage *)GUI.currentPage())->encoders_used_clock[match] = slowclock - SHOW_VALUE_TIMEOUT - 1; + } else { + ((LightPage *)GUI.currentPage())->encoders_used_clock[match] = + slowclock - SHOW_VALUE_TIMEOUT - 1; } } @@ -286,7 +286,6 @@ void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, oled_display.print(value); } - oled_display.setFont(oldfont); } @@ -330,3 +329,23 @@ void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, oled_display.setFont(oldfont); } + +void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, + uint8_t note_height, uint8_t num_of_notes, + uint64_t note_mask) { + uint16_t chromatic = 0b0000'0101'0100'1010; + uint8_t note_type = 0; + + for (uint8_t n = 0; n < num_of_notes; n++) { + if (IS_BIT_SET16(chromatic, note_type)) { + oled_display.fillRect(x - 1, y + 1, 3, (note_height / 2) - 1, WHITE); + } else { + oled_display.drawRect(x, y, note_width, note_height, WHITE); + x += note_width - 1; + } + note_type++; + if (note_type == 12) { + note_type = 0; + } + } +} diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index b2c2f4676..4c5a5df3d 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -35,6 +35,7 @@ class MCLGUI { void draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); void draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); + void draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask); static constexpr uint8_t s_menu_w = 96; static constexpr uint8_t s_menu_h = 24; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index cfdc82ab2..ea2173710 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "SeqPtcPage.h" +#include "MCL.h" #define MIDI_LOCAL_MODE 0 @@ -56,7 +56,7 @@ void SeqPtcPage::init() { SeqPage::init(); ((MCLEncoder *)encoders[2])->handler = ptc_pattern_len_handler; recording = false; - midi_events.setup_callbacks(); + midi_events.setup_callbacks(); DEBUG_PRINTLN("control mode:"); DEBUG_PRINTLN(mcl_cfg.uart2_ctrl_mode); if (mcl_cfg.uart2_ctrl_mode == MIDI_LOCAL_MODE) { @@ -71,8 +71,7 @@ void SeqPtcPage::init() { config(); } -void SeqPtcPage::config() -{ +void SeqPtcPage::config() { config_encoders(); encoders[1]->cur = 32; encoders[0]->cur = 1; @@ -234,12 +233,28 @@ void SeqPtcPage::display() { } else { strcpy(buf1, "0"); } - draw_knob(1, "DET", buf1); //detune + draw_knob(1, "DET", buf1); // detune + + // draw LEN + if (midi_device == DEVICE_MD) { + draw_knob(2, encoders[2], "LEN"); + } +#ifdef EXT_TRACKS + else { + itoa(encoders[2]->getValue() / + (2 / mcl_seq.ext_tracks[last_ext_track].resolution), + buf1, 10); + draw_knob(2, "LEN", buf1); + } +#endif // draw SCALE itoa(encoders[3]->getValue(), buf1, 10); draw_knob(3, "SCA", buf1); + // draw TI keyboard + mcl_gui.draw_keyboard(32, 23, 6, 8, 24, 0); + oled_display.display(); } #endif @@ -260,7 +275,7 @@ uint8_t SeqPtcPage::get_next_voice(uint8_t pitch) { uint8_t count = 0; if (poly_max == 0) { - return last_md_track; + return last_md_track; } // If track previously played pitch, re-use this track for (uint8_t x = 0; x < 16; x++) { @@ -341,9 +356,9 @@ void SeqPtcPage::trig_md_fromext(uint8_t note_num) { } bool SeqPtcPage::handleEvent(gui_event_t *event) { -// if (SeqPage::handleEvent(event)) { -// return; -// } + // if (SeqPage::handleEvent(event)) { + // return; + // } if (note_interface.is_event(event)) { uint8_t mask = event->mask; @@ -388,18 +403,18 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { } } - #ifdef EXT_TRACKS +#ifdef EXT_TRACKS else { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { mcl_seq.ext_tracks[n].clear_track(); } } - #endif +#endif return true; } if (EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON2)) { - #ifdef EXT_TRACKS +#ifdef EXT_TRACKS if (midi_device != DEVICE_MD) { if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { mcl_seq.ext_tracks[last_ext_track].resolution = 2; @@ -407,7 +422,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { mcl_seq.ext_tracks[last_ext_track].resolution = 1; } } - #endif +#endif redisplay = true; return true; } @@ -428,7 +443,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { else { mcl_seq.md_tracks[last_md_track].clear_track(); } - } + } #ifdef EXT_TRACKS else { mcl_seq.ext_tracks[last_ext_track].clear_track(); @@ -484,7 +499,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { SeqPage::midi_device = midi_active_peering.get_device(UART1_PORT); return; } - #ifdef EXT_TRACKS +#ifdef EXT_TRACKS SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); if (channel >= mcl_seq.num_ext_tracks) { return; @@ -501,7 +516,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { if ((seq_ptc_page.recording) && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteon(pitch, msg[2]); } - #endif +#endif } void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { DEBUG_PRINTLN("note off midi2"); @@ -517,10 +532,10 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { } if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || - (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { + (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { return; } - #ifdef EXT_TRACKS +#ifdef EXT_TRACKS SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); if (channel >= mcl_seq.num_ext_tracks) { return; @@ -533,7 +548,7 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { if (seq_ptc_page.recording && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteoff(pitch, msg[2]); } - #endif +#endif } void SeqPtcMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { From 31e89731a1d206c1cb9e581cd004a823c0b03331 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 00:10:28 +0800 Subject: [PATCH 155/469] use deferred rendering --- avr/cores/megacommand/MCL/MCLGUI.cpp | 42 ++++++++++++++--- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 57 +++++++++++++++--------- avr/cores/megacommand/MCL/SeqPtcPage.h | 6 ++- 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 8cb8d7bf3..9d05d2068 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -333,16 +333,48 @@ void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask) { - uint16_t chromatic = 0b0000'0101'0100'1010; + const uint16_t chromatic = 0b0000010101001010; + const uint8_t half = note_height / 2; + const uint8_t y2 = y + note_height - 1; + const uint8_t wm1 = note_width - 1; + uint8_t note_type = 0; + bool last_black = false; + + // draw first '|' + oled_display.drawFastVLine(x, y, note_height, WHITE); + for (uint8_t n = 0; n < num_of_notes; n++) { - if (IS_BIT_SET16(chromatic, note_type)) { - oled_display.fillRect(x - 1, y + 1, 3, (note_height / 2) - 1, WHITE); + + bool pressed = IS_BIT_SET64(note_mask, n); + bool black = IS_BIT_SET16(chromatic, note_type); + + if (black) { + // previous '|' has already filled the center col. + oled_display.drawRect(x - 1, y + 1, 3, half - 1, WHITE); + if (pressed) { + oled_display.drawFastVLine(x, y + 1, half - 2, BLACK); + } } else { - oled_display.drawRect(x, y, note_width, note_height, WHITE); - x += note_width - 1; + if (pressed && last_black) { + oled_display.fillRect(x + 2, y, note_width - 2, half, WHITE); + oled_display.fillRect(x + 1, y + half, wm1, half + 1, WHITE); + oled_display.drawPixel(x + 1, y, WHITE); + } else if (pressed) { + oled_display.fillRect(x, y, note_width, note_height, WHITE); + } else { + // draw ']' + oled_display.drawFastHLine(x, y, note_width, WHITE); + oled_display.drawFastHLine(x, y2, note_width, WHITE); + oled_display.drawFastVLine(x + wm1, y, note_height, WHITE); + } + + x += wm1; } + + last_black = black; + note_type++; if (note_type == 12) { note_type = 0; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index ea2173710..3955667d1 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -128,12 +128,23 @@ void ptc_pattern_len_handler(Encoder *enc) { } #endif } + void SeqPtcPage::loop() { #ifdef EXT_TRACKS if (encoders[0]->hasChanged() || encoders[3]->hasChanged()) { mcl_seq.ext_tracks[last_ext_track].buffer_notesoff(); } #endif + if (last_midi_state != MidiClock.state) { + last_midi_state = MidiClock.state; + redisplay = true; + } + + if (deferred_timer > 0) { + if (--deferred_timer == 0) { + redisplay = true; + } + } } #ifndef OLED_DISPLAY @@ -253,7 +264,7 @@ void SeqPtcPage::display() { draw_knob(3, "SCA", buf1); // draw TI keyboard - mcl_gui.draw_keyboard(32, 23, 6, 8, 24, 0); + mcl_gui.draw_keyboard(32, 23, 6, 9, 32, note_mask); oled_display.display(); } @@ -264,10 +275,7 @@ uint8_t SeqPtcPage::calc_pitch(uint8_t note_num) { uint8_t oct = note_num / size; note_num = note_num - oct * size; - note_num = scales[encoders[3]->cur]->pitches[note_num]; - - uint8_t pitch = encoders[0]->getValue() * 12 + oct * 12 + note_num; - return pitch; + return scales[encoders[3]->cur]->pitches[note_num] + oct * 12; } uint8_t SeqPtcPage::get_next_voice(uint8_t pitch) { @@ -323,10 +331,11 @@ uint8_t SeqPtcPage::get_machine_pitch(uint8_t track, uint8_t pitch) { pgm_read_byte(&tuning->tuning[pitch]) + encoders[1]->getValue() - 32; return machine_pitch; } -void SeqPtcPage::trig_md(uint8_t note_num) { - uint8_t pitch = calc_pitch(note_num); + +void SeqPtcPage::trig_md(uint8_t note, uint8_t pitch) { uint8_t next_track = get_next_voice(pitch); uint8_t machine_pitch = get_machine_pitch(next_track, pitch); + pitch = encoders[0]->getValue() * 12 + pitch; MD.setTrackParam(next_track, 0, machine_pitch); if (!BUTTON_DOWN(Buttons.BUTTON2)) { MD.triggerTrack(next_track, 127); @@ -334,11 +343,12 @@ void SeqPtcPage::trig_md(uint8_t note_num) { if ((recording) && (MidiClock.state == 2)) { if (!BUTTON_DOWN(Buttons.BUTTON2)) { - mcl_seq.md_tracks[next_track].record_track(note_num, 127); + mcl_seq.md_tracks[next_track].record_track(note, 127); } mcl_seq.md_tracks[next_track].record_track_pitch(machine_pitch); } } + void SeqPtcPage::trig_md_fromext(uint8_t note_num) { uint8_t pitch = seq_ext_pitch(note_num - 32); uint8_t next_track = get_next_voice(pitch); @@ -361,30 +371,34 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { // } if (note_interface.is_event(event)) { + // deferred trigger redraw to update TI keyboard feedback. + deferred_timer = render_defer_time; + uint8_t mask = event->mask; uint8_t port = event->port; uint8_t device = midi_active_peering.get_device(port); - uint8_t track = event->source - 128; + uint8_t note = event->source - 128; + uint8_t pitch = calc_pitch(note); DEBUG_PRINTLN("yep"); // note interface presses are treated as musical notes here - if (event->mask == EVENT_BUTTON_PRESSED) { + if (mask == EVENT_BUTTON_PRESSED) { + + SET_BIT64(note_mask, pitch); + // do not route MD TI events to EXT. if (device != DEVICE_MD) { - return; + return false; } midi_device = device; config_encoders(); - trig_md(track); - return true; + trig_md(note, pitch); + } else if (mask == EVENT_BUTTON_RELEASED) { + CLEAR_BIT(note_mask, pitch); } - if (event->mask == EVENT_BUTTON_RELEASED) { - // draw_notes(0); - - return true; - } return true; - } + } // TI events + if (EVENT_RELEASED(event, Buttons.BUTTON1)) { redisplay = true; recording = !recording; @@ -395,6 +409,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { GUI.setPage(&grid_page); return true; } + if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { if (midi_device == DEVICE_MD) { @@ -439,7 +454,6 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { } } } - else { mcl_seq.md_tracks[last_md_track].clear_track(); } @@ -453,6 +467,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { } if (SeqPage::handleEvent(event)) { + redisplay = true; return true; } @@ -518,6 +533,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { } #endif } + void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { DEBUG_PRINTLN("note off midi2"); uint8_t note_num = msg[1]; @@ -639,3 +655,4 @@ scale_t *scales[16]{ //&minorMaj7Arp9, //&minorMaj7ArpMin9 }; + diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index 4c383853e..42bf1c4b5 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -30,7 +30,11 @@ class SeqPtcPage : public SeqPage { public: uint8_t poly_count = 0; uint8_t poly_max = 0; + uint8_t last_midi_state = 0; int8_t poly_notes[MAX_POLY_NOTES]; + uint64_t note_mask = 0; + uint8_t deferred_timer = 0; + const uint8_t render_defer_time = 200; SeqPtcMidiEvents midi_events; SeqPtcPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, @@ -42,7 +46,7 @@ class SeqPtcPage : public SeqPage { uint8_t get_next_voice(uint8_t pitch); uint8_t calc_pitch(uint8_t note_num); - void trig_md(uint8_t note_num); + void trig_md(uint8_t note_num, uint8_t pitch); void trig_md_fromext(uint8_t note_num); void config_encoders(); void init_poly(); From 8ef90f09dcd387e84c8956eacb8a7fb0e11b0f20 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 00:12:37 +0800 Subject: [PATCH 156/469] cleanup --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 3955667d1..432e506cf 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -333,9 +333,9 @@ uint8_t SeqPtcPage::get_machine_pitch(uint8_t track, uint8_t pitch) { } void SeqPtcPage::trig_md(uint8_t note, uint8_t pitch) { + pitch = encoders[0]->getValue() * 12 + pitch; uint8_t next_track = get_next_voice(pitch); uint8_t machine_pitch = get_machine_pitch(next_track, pitch); - pitch = encoders[0]->getValue() * 12 + pitch; MD.setTrackParam(next_track, 0, machine_pitch); if (!BUTTON_DOWN(Buttons.BUTTON2)) { MD.triggerTrack(next_track, 127); From b2b9c2dcdcf9914cd902819cee54d2594b7a33d7 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 01:11:20 +0800 Subject: [PATCH 157/469] implement ext seq step page --- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 169 ++++++++++++++----- avr/cores/megacommand/MCL/SeqExtStepPage.h | 15 +- avr/cores/megacommand/MCL/SeqStepPage.cpp | 6 +- 3 files changed, 138 insertions(+), 52 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 6138b38c5..9bea76bd4 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -1,11 +1,17 @@ -#include "MCL.h" #include "SeqExtStepPage.h" +#include "MCL.h" void SeqExtStepPage::setup() { SeqPage::setup(); } void SeqExtStepPage::config() { #ifdef EXT_TRACKS - encoders[2]->cur = mcl_seq.ext_tracks[last_ext_track].length; + encoders[2]->cur = mcl_seq.ext_tracks[last_ext_track].length; #endif + // config info labels + constexpr uint8_t len1 = sizeof(info1); + + char buf[len1] = {'\0'}; + + strcpy(info2, "NOTE"); } void SeqExtStepPage::config_encoders() { #ifdef EXT_TRACKS @@ -21,6 +27,7 @@ void SeqExtStepPage::config_encoders() { SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); #endif } + void SeqExtStepPage::init() { DEBUG_PRINTLN("seq extstep init"); curpage = SEQ_EXTSTEP_PAGE; @@ -35,6 +42,7 @@ void SeqExtStepPage::cleanup() { midi_events.remove_callbacks(); } +#ifndef OLED_DISPLAY void SeqExtStepPage::display() { GUI.setLine(GUI.LINE1); @@ -132,48 +140,134 @@ void SeqExtStepPage::display() { GUI.put_value_at1(13, last_ext_track + 1); } draw_pattern_mask((page_select * 16), DEVICE_A4); - #endif +#endif SeqPage::display(); } +#else +void SeqExtStepPage::display() { + SeqPage::display(); + + draw_knob_frame(); + + char K[4]; + if (seq_param1.getValue() == 0) { + strcpy(K, "L1"); + } else if (seq_param1.getValue() <= 8) { + strcpy(K, "L "); + K[1] = seq_param1.getValue() + '0'; + } else if (seq_param1.getValue() <= 13) { + strcpy(K, "P "); + uint8_t prob[5] = {1, 2, 5, 7, 9}; + K[1] = prob[seq_param1.getValue() - 9] + '0'; + } else if (seq_param1.getValue() == 14) { + strcpy(K, "1S"); + } + draw_knob(0, "COND", K); + +#ifdef EXT_TRACKS + auto &active_track = mcl_seq.ext_tracks[last_ext_track]; + strcpy(K, "--"); + K[3] = '\0'; + if (active_track.resolution == 1) { + if (encoders[1]->getValue() == 0) { + } else if ((encoders[1]->getValue() < 6) && + (encoders[1]->getValue() != 0)) { + itoa(6 - encoders[1]->getValue(), K + 1, 10); + } else { + K[0] = '+'; + itoa(encoders[1]->getValue() - 6, K + 1, 10); + } + } else { + if (encoders[1]->getValue() == 0) { + } else if ((encoders[1]->getValue() < 12) && + (encoders[1]->getValue() != 0)) { + itoa(12 - encoders[1]->getValue(), K + 1, 10); + + } else { + K[0] = '+'; + itoa(encoders[1]->getValue() - 12, K + 1, 10); + } + } + draw_knob(1, "UTIM", K); + + MusicalNotes number_to_note; + uint8_t note; + uint8_t notes_held = 0; + uint8_t i; + for (i = 0; i < 16; i++) { + if (note_interface.notes[i] == 1) { + notes_held += 1; + } + } + + itoa(encoders[2]->getValue() / (2 / active_track.resolution), K, 10); + draw_knob(2, "LEN", K); + + if (notes_held > 0) { + uint8_t x = knob_x0 + knob_w * 3 + 4; + auto *oldfont = oled_display.getFont(); + oled_display.setFont(&TomThumb); + for (i = 0; i < 4; i++) { + oled_display.setCursor(x, i * 5 + 5); + note = active_track.notes[i][note_interface.last_note + page_select * 16]; + if (note != 0) { + note = note - 1; + uint8_t oct = note / 12; + uint8_t note = note - 12 * (note / 12); + if (active_track.notes[i][note_interface.last_note + page_select * 16] > + 0) { + oled_display.print(number_to_note.notes_upper[note]); + } else { + oled_display.print(number_to_note.notes_lower[note]); + } + oled_display.print(oct); + } + } + oled_display.setFont(oldfont); + } + + draw_pattern_mask(page_select * 16, DEVICE_A4); + oled_display.display(); +#endif +} +#endif bool SeqExtStepPage::handleEvent(gui_event_t *event) { if (SeqPage::handleEvent(event)) { - return; + return true; } + auto &active_track = mcl_seq.ext_tracks[last_ext_track]; #ifdef EXT_TRACKS if (note_interface.is_event(event)) { uint8_t mask = event->mask; uint8_t port = event->port; uint8_t device = midi_active_peering.get_device(port); - uint8_t track = event->source - 128; if (device == DEVICE_A4) { - uint8_t track = event->source - 128 - 16; + track -= 16; } - if (event->mask == EVENT_BUTTON_PRESSED) { + if (mask == EVENT_BUTTON_PRESSED) { DEBUG_PRINTLN(track); if (device == DEVICE_MD) { - if ((track + (page_select * 16)) >= - mcl_seq.ext_tracks[last_ext_track].length) { + if ((track + (page_select * 16)) >= active_track.length) { DEBUG_PRINTLN("setting to 0"); DEBUG_PRINTLN(last_ext_track); DEBUG_PRINTLN(page_select); note_interface.notes[track] = 0; - return; + return true; } - int8_t utiming = mcl_seq.ext_tracks[last_ext_track] - .timing[(track + (page_select * 16))]; // upper + int8_t utiming = + active_track.timing[(track + (page_select * 16))]; // upper uint8_t condition = - mcl_seq.ext_tracks[last_ext_track] - .conditional[(track + (page_select * 16))]; // lower + active_track.conditional[(track + (page_select * 16))]; // lower encoders[0]->cur = condition; // Micro if (utiming == 0) { - if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { + if (active_track.resolution == 1) { utiming = 6; ((MCLEncoder *)encoders[1])->max = 11; } else { @@ -186,43 +280,34 @@ bool SeqExtStepPage::handleEvent(gui_event_t *event) { note_interface.last_note = track; } } - if (event->mask == EVENT_BUTTON_RELEASED) { + if (mask == EVENT_BUTTON_RELEASED) { if (device == DEVICE_MD) { uint8_t utiming = (encoders[1]->cur + 0); uint8_t condition = encoders[0]->cur; - if ((track + (page_select * 16)) >= - mcl_seq.ext_tracks[last_ext_track].length) { + if ((track + (page_select * 16)) >= active_track.length) { return true; } // timing = 3; // condition = 3; - if (clock_diff(note_interface.note_hold,slowclock) < TRIG_HOLD_TIME) { + if (clock_diff(note_interface.note_hold, slowclock) < TRIG_HOLD_TIME) { for (uint8_t c = 0; c < 4; c++) { - if (mcl_seq.ext_tracks[last_ext_track] - .notes[c][track + page_select * 16] > 0) { + if (active_track.notes[c][track + page_select * 16] > 0) { MidiUart2.sendNoteOff( last_ext_track, - abs(mcl_seq.ext_tracks[last_ext_track] - .notes[c][track + page_select * 16]) - - 1, - 0); + abs(active_track.notes[c][track + page_select * 16]) - 1, 0); } - mcl_seq.ext_tracks[last_ext_track] - .notes[c][track + page_select * 16] = 0; + active_track.notes[c][track + page_select * 16] = 0; } - mcl_seq.ext_tracks[last_ext_track] - .timing[(track + (page_select * 16))] = 0; - mcl_seq.ext_tracks[last_ext_track] - .conditional[(track + (page_select * 16))] = 0; + active_track.timing[(track + (page_select * 16))] = 0; + active_track.conditional[(track + (page_select * 16))] = 0; } else { - mcl_seq.ext_tracks[last_ext_track] - .timing[(track + (page_select * 16))] = condition; // upper - mcl_seq.ext_tracks[last_ext_track] - .timing[(track + (page_select * 16))] = utiming; // upper + active_track.timing[(track + (page_select * 16))] = utiming; // upper + active_track.conditional[(track + (page_select * 16))] = + condition; // upper } } return true; @@ -232,19 +317,19 @@ bool SeqExtStepPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.ENCODER1)) { if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { - md_exploit.off(); - GUI.setPage(&grid_page); + md_exploit.off(); + GUI.setPage(&grid_page); } return true; } if (EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON2)) { - if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { - mcl_seq.ext_tracks[last_ext_track].resolution = 2; + if (active_track.resolution == 1) { + active_track.resolution = 2; init(); } else { - mcl_seq.ext_tracks[last_ext_track].resolution = 1; + active_track.resolution = 1; init(); } @@ -256,13 +341,13 @@ bool SeqExtStepPage::handleEvent(gui_event_t *event) { return true; } if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - mcl_seq.ext_tracks[last_ext_track].clear_track(); + active_track.clear_track(); return true; } if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { - mcl_seq.ext_tracks[last_ext_track].clear_track(); + active_track.clear_track(); } return true; } diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.h b/avr/cores/megacommand/MCL/SeqExtStepPage.h index 0b2c6b22b..f20b17317 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.h +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.h @@ -4,6 +4,7 @@ #define SEQEXTSTEPPAGE_H__ #include "SeqPage.h" +#include "SeqStepPage.h" void ext_pattern_len_handler(Encoder *enc); class SeqExtStepMidiEvents : public MidiCallback { @@ -24,14 +25,14 @@ class SeqExtStepPage : public SeqPage { SeqExtStepPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : SeqPage(e1, e2, e3, e4) {} - bool handleEvent(gui_event_t *event); - void display(); - - void setup(); - void cleanup(); - void init(); - void config(); void config_encoders(); + + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void config(); + virtual void cleanup(); }; #endif /* SEQEXTSTEPPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index fbac713ab..80721e4b8 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -124,7 +124,6 @@ void SeqStepPage::display() { draw_knob_frame(); char K[4]; - if (seq_param1.getValue() == 0) { strcpy(K, "L1"); } else if (seq_param1.getValue() <= 8) { @@ -140,12 +139,13 @@ void SeqStepPage::display() { draw_knob(0, "COND", K); strcpy(K, "--"); + K[3] = '\0'; if (seq_param2.getValue() == 0) { } else if ((seq_param2.getValue() < 12) && (seq_param2.getValue() != 0)) { - K[1] = 12 - seq_param2.getValue() + '0'; + itoa(12 - seq_param2.getValue(), K+1, 10); } else { K[0] = '+'; - K[1] = seq_param2.getValue() - 12 + '0'; + itoa(seq_param2.getValue() - 12, K+1, 10); } draw_knob(1, "UTIM", K); From 71db4c6aa2b6e1782b181de7feae2ac803adba50 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 27 Oct 2019 12:03:34 +1100 Subject: [PATCH 158/469] Reset fx encoder max value, on page init --- avr/cores/megacommand/MCL/FXPage.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 918c07040..b012d8b7c 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -19,20 +19,16 @@ void FXPage::init() { void FXPage::update_encoders() { for (uint8_t n = 0; n < GUI_NUM_ENCODERS; n++) { + ((MCLEncoder*)encoders[n])->max = 127; + uint8_t a = ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS) + n; uint8_t fx_param = params[a].param; - DEBUG_PRINTLN(params[n].param); switch (params[a].type) { case MD_FX_ECHO: - DEBUG_PRINTLN("setting delay"); - DEBUG_PRINTLN(n); - DEBUG_PRINTLN(fx_param); encoders[n]->cur = MD.kit.delay[fx_param]; break; case MD_FX_REV: - DEBUG_PRINTLN("setting reverb"); - DEBUG_PRINTLN(n); encoders[n]->cur = MD.kit.reverb[fx_param]; break; case MD_FX_EQ: From f2defc572c26039bcdb3b5fbcfbaa1b55cc8a4d8 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 27 Oct 2019 13:15:52 +1100 Subject: [PATCH 159/469] Re-add wider MD sprite, think I prefer --- art/sprites/md_wide_trimetric.png | Bin 0 -> 607 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/md_wide_trimetric.png diff --git a/art/sprites/md_wide_trimetric.png b/art/sprites/md_wide_trimetric.png new file mode 100644 index 0000000000000000000000000000000000000000..6219db2338a5847778c874b4b107a7f8abca4759 GIT binary patch literal 607 zcmV-l0-*hgP)Px%8A(JzR7ef2lnIW5Fc1U-;{QL+L0Ma1iCp^j_EFrj=CFGQ4am@q;5MQ z2D2IHP(@%<;HWP4vY<0U#5i}YcDvLG#;2*~XsTp2P7FRPc`|Or^(Q&D6SAmnH~#OK zCB*83OVP&mW5wpctdH{TglOdN@ZJzR%%&vL7bnJ^UxdgZ=gKj?pU-C#AR(3|vA%DK zd2tOKc=Bu?F#E|n&%U&~cSsV+a{zv>D$rP4A0&JA!M0he^Tx3mfU$$E87GN&tT^l9 zYu>t+y4dYYO~OWOzT$$D0N=cQ*7x!`SZuZJEdwZcWNo6x+8aooEzUlzea0iZosh*! zm@z&(FDrfISbIWR@#IhU9fFXph>UZfm^+22w-b~?^aUcq#r?9LdFxkfCkP^++Ap=I tQELtrZ+z8$-JwdD1CLH4g;$PW;t%N?4=O&W+_wM#002ovPDHLkV1g$C8T|kN literal 0 HcmV?d00001 From 8b4f64edcdb316e9f71613da16da8b34ca0e9331 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 27 Oct 2019 17:36:26 +1100 Subject: [PATCH 160/469] Add diagnostic page to show port throughput speeds --- avr/cores/megacommand/MCL/DiagnosticPage.cpp | 110 +++++++++++++++++++ avr/cores/megacommand/MCL/DiagnosticPage.h | 38 +++++++ avr/cores/megacommand/MCL/MCLMenus.cpp | 5 +- avr/cores/megacommand/MCL/MCLMenus.h | 4 +- 4 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 avr/cores/megacommand/MCL/DiagnosticPage.cpp create mode 100644 avr/cores/megacommand/MCL/DiagnosticPage.h diff --git a/avr/cores/megacommand/MCL/DiagnosticPage.cpp b/avr/cores/megacommand/MCL/DiagnosticPage.cpp new file mode 100644 index 000000000..d597d7af6 --- /dev/null +++ b/avr/cores/megacommand/MCL/DiagnosticPage.cpp @@ -0,0 +1,110 @@ +#include "MCL.h" + +void DiagnosticPage::setup() { DEBUG_PRINT_FN(); } + +void DiagnosticPage::init() { + DEBUG_PRINT_FN(); +#ifdef OLED_DISPLAY + classic_display = false; + oled_display.clearDisplay(); + oled_display.setFont(); +#endif +} +void DiagnosticPage::cleanup() { +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif +} + +void DiagnosticPage::loop() { + if (clock_diff(last_clock,slowclock) >= 1000) { + USE_LOCK(); + SET_LOCK(); + if (uart_tx_wr_old > MidiUart.txRb.wr) { uart_tx_wr_old = 0xFFFF - uart_tx_wr_old; } + uart_tx_wr_rate = (MidiUart.txRb.wr - uart_tx_wr_old); + uart_tx_wr_old = MidiUart.txRb.wr; + + if (uart_rx_wr_old > MidiUart.rxRb.wr) { uart_rx_wr_old = 0xFFFF - uart_rx_wr_old; } + uart_rx_wr_rate = (MidiUart.rxRb.wr - uart_rx_wr_old); + uart_rx_wr_old = MidiUart.rxRb.wr; + + last_clock = slowclock; + CLEAR_LOCK(); + } + +} + +void DiagnosticPage::display() { + + if (!classic_display) { +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif + } +#ifndef OLED_DISPLAY + GUI.clearLines(); + GUI.setLine(GUI.LINE1); + uint8_t x; + + GUI.put_string_at(0, "Diagnostic"); + GUI.put_value_at1(4, page_mode ? 1 : 0); + GUI.setLine(GUI.LINE2); + /* + if (mcl_cfg.ram_page_mode == 0) { + GUI.put_string_at(0, "MON"); + } else { + GUI.put_string_at(0, "LNK"); + } + */ + +#endif +#ifdef OLED_DISPLAY + oled_display.setFont(); + oled_display.setCursor(0, 0); + + oled_display.print("DIAG "); + // oled_display.print(page_mode ? 1 : 0); + oled_display.print(" "); + oled_display.print(uart_tx_wr_rate); + oled_display.setCursor(70, 0); + oled_display.print(MidiUart.txRb.size()); + + oled_display.setCursor(0, 15); + oled_display.print(uart_rx_wr_rate); + oled_display.setCursor(70, 15); + oled_display.print(MidiUart.rxRb.size()); + oled_display.display(); + +#endif +} + +bool DiagnosticPage::handleEvent(gui_event_t *event) { + if (note_interface.is_event(event)) { + return true; + } + if (event->mask == EVENT_BUTTON_RELEASED) { + return true; + } + if (EVENT_PRESSED(event, Buttons.ENCODER1) || + EVENT_PRESSED(event, Buttons.ENCODER2) || + EVENT_PRESSED(event, Buttons.ENCODER3) || + EVENT_PRESSED(event, Buttons.ENCODER4)) { + GUI.setPage(&grid_page); + } + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + page_mode = !(page_mode); + } + + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + } + + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + } + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + return true; + } + + return false; +} + +DiagnosticPage diag_page; diff --git a/avr/cores/megacommand/MCL/DiagnosticPage.h b/avr/cores/megacommand/MCL/DiagnosticPage.h new file mode 100644 index 000000000..484a9af96 --- /dev/null +++ b/avr/cores/megacommand/MCL/DiagnosticPage.h @@ -0,0 +1,38 @@ +/* Justin Mammarella jmamma@gmail.com 2018 */ + +#ifndef DIAGNOSTICPAGE_H__ +#define DIAGNOSTICPAGE_H__ + +#include "GUI.h" +#include "MCLEncoder.h" + +class DiagnosticPage : public LightPage, MidiCallback { +public: + DiagnosticPage(Encoder *e1 = NULL, Encoder *e2 = NULL, + Encoder *e3 = NULL, Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) { + } + + bool handleEvent(gui_event_t *event); + + bool page_mode; + uint8_t page_id; + uint16_t last_clock; + uint16_t uart_tx_wr_old; + uint16_t uart_tx_wr_rate; + + uint16_t uart_rx_wr_old; + uint16_t uart_rx_wr_rate; + + + void display(); + void setup(); + void init(); + void loop(); + void cleanup(); + +}; + +extern DiagnosticPage diag_page; + +#endif /* DIAGNOSTICPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index d333113e0..c3ea46710 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -80,11 +80,12 @@ const menu_t<3> chain_menu_layout PROGMEM = { }; -const menu_t<2> mclconfig_menu_layout PROGMEM = { +const menu_t<3> mclconfig_menu_layout PROGMEM = { "SYSTEM", { {"DISPLAY:", 0, 2, 2, (uint8_t *) &mcl_cfg.display_mirror, (Page*) NULL, NULL, {{0, "INT"}, {1, "INT+EXT"}}}, {"SCREENSAVER:", 0, 2, 2, (uint8_t *) &mcl_cfg.screen_saver, (Page*) NULL, NULL, {{0, "OFF"}, {1, "ON"}}}, + {"DIAGNOSTIC:", 0, 0, 0, (uint8_t *) NULL, (Page*) &diag_page, NULL, {}}, }, (&mclsys_apply_config), }; @@ -107,7 +108,7 @@ MenuPage<5> midi_config_page(&midiconfig_menu_layout, &config_param1, &config_param3); MenuPage<5> md_config_page(&mdconfig_menu_layout, &config_param1, &config_param4); MenuPage<3> chain_config_page(&chain_menu_layout, &config_param1, &config_param6); -MenuPage<2> mcl_config_page(&mclconfig_menu_layout, &config_param1, +MenuPage<3> mcl_config_page(&mclconfig_menu_layout, &config_param1, &config_param5); MenuPage<1> ram_config_page(&rampage1_menu_layout, &config_param1, &config_param7); diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index e8547ee60..2462ffb30 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -11,7 +11,7 @@ #include "GridPages.h" #include "TextInputPage.h" #include "SeqPages.h" - +#include "DiagnosticPage.h" #define ENCODER_RES_SYS 2 extern MCLEncoder options_param1; @@ -28,7 +28,7 @@ extern MCLEncoder config_param7; extern MenuPage<7> system_page; extern MenuPage<5> midi_config_page; extern MenuPage<5> md_config_page; -extern MenuPage<2> mcl_config_page; +extern MenuPage<3> mcl_config_page; extern MenuPage<3> chain_config_page; extern MenuPage<1> aux_config_page; extern MenuPage<1> ram_config_page; From e0e3dab151bfdc6df0fce1233bcca3d049a0f4ec Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 27 Oct 2019 12:53:58 +1100 Subject: [PATCH 161/469] Don't allow TrigInterface input from A4 in PtcPage. Take note pitch data from MIDI2 when in ext mode --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 26 +++++++++++++++++------- avr/cores/megacommand/MCL/SeqPtcPage.h | 2 ++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 432e506cf..90c50487a 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -2,6 +2,7 @@ #include "MCL.h" #define MIDI_LOCAL_MODE 0 +#define NUM_KEYS 32 void SeqPtcPage::setup() { SeqPage::setup(); @@ -264,7 +265,7 @@ void SeqPtcPage::display() { draw_knob(3, "SCA", buf1); // draw TI keyboard - mcl_gui.draw_keyboard(32, 23, 6, 9, 32, note_mask); + mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); oled_display.display(); } @@ -348,11 +349,17 @@ void SeqPtcPage::trig_md(uint8_t note, uint8_t pitch) { mcl_seq.md_tracks[next_track].record_track_pitch(machine_pitch); } } - +void SeqPtcPage::clear_trig_fromext(uint8_t note_num) { + uint8_t pitch = seq_ext_pitch(note_num - 32); + uint8_t next_track = get_next_voice(pitch); + uint8_t machine_pitch = get_machine_pitch(next_track, pitch); + CLEAR_BIT64(note_mask, pitch); +} void SeqPtcPage::trig_md_fromext(uint8_t note_num) { uint8_t pitch = seq_ext_pitch(note_num - 32); uint8_t next_track = get_next_voice(pitch); uint8_t machine_pitch = get_machine_pitch(next_track, pitch); + SET_BIT64(note_mask, pitch); MD.setTrackParam(next_track, 0, machine_pitch); if (!BUTTON_DOWN(Buttons.BUTTON2)) { MD.triggerTrack(next_track, 127); @@ -378,6 +385,9 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { uint8_t port = event->port; uint8_t device = midi_active_peering.get_device(port); + // do not route EXT TI events to MD. + if (device != DEVICE_MD) { return false; } + uint8_t note = event->source - 128; uint8_t pitch = calc_pitch(note); DEBUG_PRINTLN("yep"); @@ -385,10 +395,6 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { if (mask == EVENT_BUTTON_PRESSED) { SET_BIT64(note_mask, pitch); - // do not route MD TI events to EXT. - if (device != DEVICE_MD) { - return false; - } midi_device = device; config_encoders(); trig_md(note, pitch); @@ -510,6 +516,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { } if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { + seq_ptc_page.trig_md_fromext(note_num); SeqPage::midi_device = midi_active_peering.get_device(UART1_PORT); return; @@ -527,6 +534,8 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { DEBUG_PRINTLN(mcl_seq.ext_tracks[channel].length); uint8_t pitch = seq_ptc_page.seq_ext_pitch(note_num); + uint8_t scaled_pitch = pitch - (pitch / 24) * 24; + SET_BIT64(seq_ptc_page.note_mask, scaled_pitch); MidiUart2.sendNoteOn(channel, pitch, msg[2]); if ((seq_ptc_page.recording) && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteon(pitch, msg[2]); @@ -549,7 +558,8 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { - return; + seq_ptc_page.clear_trig_fromext(note_num); + return; } #ifdef EXT_TRACKS SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); @@ -560,6 +570,8 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { seq_ptc_page.config_encoders(); uint8_t pitch = seq_ptc_page.seq_ext_pitch(note_num); + uint8_t scaled_pitch = pitch - (pitch / 24) * 24; + CLEAR_BIT64(seq_ptc_page.note_mask, scaled_pitch); MidiUart2.sendNoteOff(channel, pitch, msg[2]); if (seq_ptc_page.recording && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteoff(pitch, msg[2]); diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index 42bf1c4b5..836791a5e 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -48,6 +48,8 @@ class SeqPtcPage : public SeqPage { void trig_md(uint8_t note_num, uint8_t pitch); void trig_md_fromext(uint8_t note_num); + void clear_trig_fromext(uint8_t note_num); + void config_encoders(); void init_poly(); From bbd16459fda019fd778dce6f238de07a5e3569c0 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 15:56:01 +0800 Subject: [PATCH 162/469] add Rlck page implementation --- avr/cores/megacommand/MCL/SeqRlckPage.cpp | 36 +++++++++++++++++++++++ avr/cores/megacommand/MCL/SeqRlckPage.h | 12 ++++---- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 2 +- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqRlckPage.cpp b/avr/cores/megacommand/MCL/SeqRlckPage.cpp index 2dd2e1725..d0573fe08 100644 --- a/avr/cores/megacommand/MCL/SeqRlckPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRlckPage.cpp @@ -3,6 +3,23 @@ void SeqRlckPage::setup() { SeqPage::setup(); } +void SeqRlckPage::config() { + // config info labels + const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); + const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); + + constexpr uint8_t len1 = sizeof(info1); + + char buf[len1] = {'\0'}; + m_strncpy_p(buf, str1, len1); + strncpy(info1, buf, len1); + strncat(info1, ">", len1); + m_strncpy_p(buf, str2, len1); + strncat(info1, buf, len1); + + strcpy(info2, "RLCK"); +} + void SeqRlckPage::init() { SeqPage::init(); if (MidiClock.state == 2) { @@ -10,6 +27,8 @@ void SeqRlckPage::init() { } md_exploit.off(); note_interface.state = false; + recording = true; + config(); ((MCLEncoder *)encoders[0])->max = 4; ((MCLEncoder *)encoders[1])->max = 64; @@ -20,6 +39,7 @@ void SeqRlckPage::init() { curpage = SEQ_RTRK_PAGE; midi_events.setup_callbacks(); } + void SeqRlckPage::cleanup() { SeqPage::cleanup(); if (MidiClock.state == 2) { @@ -27,6 +47,8 @@ void SeqRlckPage::cleanup() { } midi_events.remove_callbacks(); } + +#ifndef OLED_DISPLAY void SeqRlckPage::display() { if ((!redisplay) && (MidiClock.state == 2)) { return; @@ -59,6 +81,20 @@ void SeqRlckPage::display() { draw_lock_mask(page_select * 16, show_current_step); SeqPage::display(); } +#else +void SeqRlckPage::display() { + if ((!redisplay) && (MidiClock.state == 2)) { + return; + } + SeqPage::display(); + bool show_current_step = false; + draw_lock_mask(page_select * 16, show_current_step); + draw_pattern_mask(page_select * 16, DEVICE_MD, show_current_step); + + oled_display.display(); +} +#endif + bool SeqRlckPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { diff --git a/avr/cores/megacommand/MCL/SeqRlckPage.h b/avr/cores/megacommand/MCL/SeqRlckPage.h index 08b01cf9f..ccd735d36 100644 --- a/avr/cores/megacommand/MCL/SeqRlckPage.h +++ b/avr/cores/megacommand/MCL/SeqRlckPage.h @@ -26,11 +26,13 @@ class SeqRlckPage : public SeqPage { SeqRlckPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : SeqPage(e1, e2, e3, e4) {} - bool handleEvent(gui_event_t *event); - void display(); - void setup(); - void init(); - void cleanup(); + + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void config(); + virtual void cleanup(); }; #endif /* SEQRLCKPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 5bdd6b450..eef6b3f98 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -102,7 +102,7 @@ bool SeqRtrkPage::handleEvent(gui_event_t *event) { encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; MD.triggerTrack(track, 127); - if ((MidiClock.state == 2)) { + if (MidiClock.state == 2) { mcl_seq.md_tracks[last_md_track].record_track(track, 127); return true; From 51aec2ea8dc62893816ea2f6ca81d12ced717f8b Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 16:33:20 +0800 Subject: [PATCH 163/469] fix uart2 midi message keyboard feedback --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 73 +++++++++++++----------- avr/cores/megacommand/MCL/SeqPtcPage.h | 2 + 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 90c50487a..00e063fd3 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -334,7 +334,7 @@ uint8_t SeqPtcPage::get_machine_pitch(uint8_t track, uint8_t pitch) { } void SeqPtcPage::trig_md(uint8_t note, uint8_t pitch) { - pitch = encoders[0]->getValue() * 12 + pitch; + pitch = octave_to_pitch() + pitch; uint8_t next_track = get_next_voice(pitch); uint8_t machine_pitch = get_machine_pitch(next_track, pitch); MD.setTrackParam(next_track, 0, machine_pitch); @@ -349,12 +349,12 @@ void SeqPtcPage::trig_md(uint8_t note, uint8_t pitch) { mcl_seq.md_tracks[next_track].record_track_pitch(machine_pitch); } } + void SeqPtcPage::clear_trig_fromext(uint8_t note_num) { uint8_t pitch = seq_ext_pitch(note_num - 32); - uint8_t next_track = get_next_voice(pitch); - uint8_t machine_pitch = get_machine_pitch(next_track, pitch); - CLEAR_BIT64(note_mask, pitch); + CLEAR_BIT64(note_mask, pitch); } + void SeqPtcPage::trig_md_fromext(uint8_t note_num) { uint8_t pitch = seq_ext_pitch(note_num - 32); uint8_t next_track = get_next_voice(pitch); @@ -372,21 +372,24 @@ void SeqPtcPage::trig_md_fromext(uint8_t note_num) { } } +void SeqPtcPage::queue_redraw() { + deferred_timer = render_defer_time; +} + bool SeqPtcPage::handleEvent(gui_event_t *event) { - // if (SeqPage::handleEvent(event)) { - // return; - // } if (note_interface.is_event(event)) { // deferred trigger redraw to update TI keyboard feedback. - deferred_timer = render_defer_time; + queue_redraw(); uint8_t mask = event->mask; uint8_t port = event->port; uint8_t device = midi_active_peering.get_device(port); // do not route EXT TI events to MD. - if (device != DEVICE_MD) { return false; } + if (device != DEVICE_MD) { + return false; + } uint8_t note = event->source - 128; uint8_t pitch = calc_pitch(note); @@ -459,8 +462,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { mcl_seq.md_tracks[c].clear_track(); } } - } - else { + } else { mcl_seq.md_tracks[last_md_track].clear_track(); } } @@ -494,7 +496,7 @@ uint8_t SeqPtcPage::seq_ext_pitch(uint8_t note_num) { // } // if (encoders[4]->getValue() > 0) { - pitch = encoders[0]->getValue() * 12 + + pitch = octave_to_pitch() + scales[encoders[3]->cur]->pitches[pos] + oct * 12; // } @@ -502,10 +504,6 @@ uint8_t SeqPtcPage::seq_ext_pitch(uint8_t note_num) { } void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { - DEBUG_PRINTLN("note on midi2"); - uint8_t note_num = msg[1]; - uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); - DEBUG_PRINTLN(channel); if ((GUI.currentPage() == &seq_step_page) || #ifdef EXT_TRACKS (GUI.currentPage() == &seq_extstep_page) || @@ -514,28 +512,36 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { (GUI.currentPage() == &grid_write_page)) { return; } + + uint8_t note_num = msg[1]; + uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); + DEBUG_PRINT("note on midi2: "); + DEBUG_DUMP(channel); + + uint8_t pitch = seq_ptc_page.calc_pitch(note_num); + uint8_t scaled_pitch = pitch - (pitch / NUM_KEYS) * NUM_KEYS; + SET_BIT64(seq_ptc_page.note_mask, scaled_pitch); + seq_ptc_page.queue_redraw(); + + // matches control channel, or MIDI2 is OMNI? + // then route midi message to MD if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { - - seq_ptc_page.trig_md_fromext(note_num); + seq_ptc_page.trig_md_fromext(pitch); SeqPage::midi_device = midi_active_peering.get_device(UART1_PORT); return; } + #ifdef EXT_TRACKS + // otherwise, translate the message and send it back to MIDI2. SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); if (channel >= mcl_seq.num_ext_tracks) { return; } - if (last_ext_track != channel) { - seq_ptc_page.redisplay = true; - } last_ext_track = channel; seq_ptc_page.config_encoders(); DEBUG_PRINTLN(mcl_seq.ext_tracks[channel].length); - uint8_t pitch = seq_ptc_page.seq_ext_pitch(note_num); - uint8_t scaled_pitch = pitch - (pitch / 24) * 24; - SET_BIT64(seq_ptc_page.note_mask, scaled_pitch); MidiUart2.sendNoteOn(channel, pitch, msg[2]); if ((seq_ptc_page.recording) && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteon(pitch, msg[2]); @@ -544,9 +550,6 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { } void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { - DEBUG_PRINTLN("note off midi2"); - uint8_t note_num = msg[1]; - uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); if ((GUI.currentPage() == &seq_step_page) || #ifdef EXT_TRACKS (GUI.currentPage() == &seq_extstep_page) || @@ -556,10 +559,18 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { return; } + DEBUG_PRINTLN("note off midi2"); + uint8_t note_num = msg[1]; + uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); + uint8_t pitch = seq_ptc_page.calc_pitch(note_num); + uint8_t scaled_pitch = pitch - (pitch / NUM_KEYS) * NUM_KEYS; + CLEAR_BIT64(seq_ptc_page.note_mask, scaled_pitch); + seq_ptc_page.queue_redraw(); + if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { - seq_ptc_page.clear_trig_fromext(note_num); - return; + seq_ptc_page.clear_trig_fromext(pitch); + return; } #ifdef EXT_TRACKS SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); @@ -569,9 +580,6 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { last_ext_track = channel; seq_ptc_page.config_encoders(); - uint8_t pitch = seq_ptc_page.seq_ext_pitch(note_num); - uint8_t scaled_pitch = pitch - (pitch / 24) * 24; - CLEAR_BIT64(seq_ptc_page.note_mask, scaled_pitch); MidiUart2.sendNoteOff(channel, pitch, msg[2]); if (seq_ptc_page.recording && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteoff(pitch, msg[2]); @@ -667,4 +675,3 @@ scale_t *scales[16]{ //&minorMaj7Arp9, //&minorMaj7ArpMin9 }; - diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index 836791a5e..ebac41134 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -50,8 +50,10 @@ class SeqPtcPage : public SeqPage { void trig_md_fromext(uint8_t note_num); void clear_trig_fromext(uint8_t note_num); + inline uint8_t octave_to_pitch() { return encoders[0]->getValue() * 12; } void config_encoders(); void init_poly(); + void queue_redraw(); virtual bool handleEvent(gui_event_t *event); virtual void display(); From e5d0bc9f3a740ce926212ef199edb89cae6a6235 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 27 Oct 2019 20:28:31 +1100 Subject: [PATCH 164/469] Decouple LFOPage from specific LFOTrack. Add second depth parameter (doubles wav_table space) TrigMode is now selectable by pressing button 3. --- avr/cores/megacommand/MCL/AuxPages.cpp | 4 +- avr/cores/megacommand/MCL/LFOPage.cpp | 143 ++++++++++++++-------- avr/cores/megacommand/MCL/LFOPage.h | 8 +- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 4 +- avr/cores/megacommand/MCL/LFOSeqTrack.h | 2 +- 5 files changed, 102 insertions(+), 59 deletions(-) diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index e80d0dd4f..e162b8374 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -1,4 +1,6 @@ #include "AuxPages.h" +#include "MCL.h" +#include "MCLSeq.h" extern MCLEncoder mixer_param1(0, 127); extern MCLEncoder mixer_param2(0, 127); @@ -44,5 +46,5 @@ FXPage fx_page_a(&fx_param1, &fx_param2, &fx_param3, &fx_param4, FXPage fx_page_b(&fx_param1, &fx_param2, &fx_param3, &fx_param4, (fx_param_t*) &fx_reverb_params, 8); -LFOPage lfo_page(&fx_param1, &fx_param2, &fx_param3, &fx_param4); +LFOPage lfo_page(&(mcl_seq.lfo_tracks[0]), &fx_param1, &fx_param2, &fx_param3, &fx_param4); diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index ee8c95602..ddfaadb4c 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -11,7 +11,9 @@ #define LFO_DESTINATION 0 #define LFO_SETTINGS 1 -void LFOPage::setup() { DEBUG_PRINT_FN(); } +void LFOPage::setup() { +// lfo_track = &mcl_seq.lfo_tracks[0]; + DEBUG_PRINT_FN(); } void LFOPage::init() { DEBUG_PRINT_FN(); @@ -20,8 +22,11 @@ void LFOPage::init() { oled_display.clearDisplay(); oled_display.setFont(); #endif - md_exploit.on(); update_encoders(); + + if (lfo_track->mode != LFO_MODE_FREE) { + md_exploit.on(); + } } void LFOPage::cleanup() { md_exploit.off(); @@ -32,23 +37,26 @@ void LFOPage::cleanup() { void LFOPage::update_encoders() { if (page_mode == LFO_DESTINATION) { - encoders[0]->cur = mcl_seq.lfo_tracks[0].params[0].dest; + encoders[0]->cur = lfo_track->params[0].dest; ((MCLEncoder *)encoders[0])->max = NUM_MD_TRACKS + 4; - encoders[1]->cur = mcl_seq.lfo_tracks[0].params[0].param; + encoders[1]->cur = lfo_track->params[0].param; ((MCLEncoder *)encoders[1])->max = 23; - encoders[2]->cur = mcl_seq.lfo_tracks[0].params[1].dest; - ((MCLEncoder *)encoders[2])->max = 16 + 4; - encoders[3]->cur = mcl_seq.lfo_tracks[0].params[1].param; + encoders[2]->cur = lfo_track->params[1].dest; + ((MCLEncoder *)encoders[2])->max = NUM_MD_TRACKS + 4; + encoders[3]->cur = lfo_track->params[1].param; ((MCLEncoder *)encoders[3])->max = 23; } if (page_mode == LFO_SETTINGS) { encoders[0]->cur = waveform; ((MCLEncoder *)encoders[0])->max = 2; - encoders[1]->cur = mcl_seq.lfo_tracks[0].mode; - ((MCLEncoder *)encoders[1])->max = 2; - encoders[2]->cur = mcl_seq.lfo_tracks[0].speed; + + encoders[1]->cur = lfo_track->speed; + ((MCLEncoder *)encoders[1])->max = 127; + + encoders[2]->cur = lfo_track->params[0].depth; ((MCLEncoder *)encoders[2])->max = 127; - encoders[3]->cur = depth; + + encoders[3]->cur = lfo_track->params[1].depth; ((MCLEncoder *)encoders[3])->max = 127; } } @@ -59,9 +67,9 @@ void LFOPage::loop() { if (encoders[0]->hasChanged()) { USE_LOCK(); SET_LOCK(); - mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[0].dest = encoders[0]->cur; - mcl_seq.lfo_tracks[0].params[0].update_offset(); + lfo_track->params[0].reset_param_offset(); + lfo_track->params[0].dest = encoders[0]->cur; + lfo_track->params[0].update_offset(); CLEAR_LOCK(); if (encoders[0]->cur > NUM_MD_TRACKS) { ((MCLEncoder *)encoders[1])->max = 7; @@ -72,23 +80,23 @@ void LFOPage::loop() { if (encoders[1]->hasChanged()) { USE_LOCK(); SET_LOCK(); - mcl_seq.lfo_tracks[0].params[0].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[0].param = encoders[1]->cur; - mcl_seq.lfo_tracks[0].params[0].offset = - mcl_seq.lfo_tracks[0].params[0].get_param_offset(encoders[0]->cur, + lfo_track->params[0].reset_param_offset(); + lfo_track->params[0].param = encoders[1]->cur; + lfo_track->params[0].offset = + lfo_track->params[0].get_param_offset(encoders[0]->cur, encoders[1]->cur); - mcl_seq.lfo_tracks[0].params[0].update_offset(); + lfo_track->params[0].update_offset(); CLEAR_LOCK(); } if (encoders[2]->hasChanged()) { USE_LOCK(); SET_LOCK(); - mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[1].dest = encoders[2]->cur; - mcl_seq.lfo_tracks[0].params[1].update_offset(); + lfo_track->params[1].reset_param_offset(); + lfo_track->params[1].dest = encoders[2]->cur; + lfo_track->params[1].update_offset(); CLEAR_LOCK(); - if (encoders[0]->cur > NUM_MD_TRACKS) { + if (encoders[2]->cur > NUM_MD_TRACKS) { ((MCLEncoder *)encoders[3])->max = 7; } else { ((MCLEncoder *)encoders[3])->max = 23; @@ -97,39 +105,42 @@ void LFOPage::loop() { if (encoders[3]->hasChanged()) { USE_LOCK(); SET_LOCK(); - mcl_seq.lfo_tracks[0].params[1].reset_param_offset(); - mcl_seq.lfo_tracks[0].params[1].param = encoders[3]->cur; - mcl_seq.lfo_tracks[0].params[1].offset = - mcl_seq.lfo_tracks[0].params[1].get_param_offset(encoders[2]->cur, + lfo_track->params[1].reset_param_offset(); + lfo_track->params[1].param = encoders[3]->cur; + lfo_track->params[1].offset = + lfo_track->params[1].get_param_offset(encoders[2]->cur, encoders[3]->cur); - mcl_seq.lfo_tracks[0].params[1].update_offset(); + lfo_track->params[1].update_offset(); CLEAR_LOCK(); } } if (page_mode == LFO_SETTINGS) { if (encoders[0]->hasChanged()) { waveform = encoders[0]->cur; - load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); + load_wavetable(waveform, lfo_track, 0, + lfo_track->params[0].depth); + load_wavetable(waveform, lfo_track, 1, + lfo_track->params[1].depth); } + if (encoders[1]->hasChanged()) { - mcl_seq.lfo_tracks[0].mode = encoders[1]->cur; + lfo_track->speed = encoders[1]->cur; } if (encoders[2]->hasChanged()) { - mcl_seq.lfo_tracks[0].speed = encoders[2]->cur; + lfo_track->params[0].depth = encoders[2]->cur; + load_wavetable(waveform, lfo_track, 0, encoders[2]->cur); } if (encoders[3]->hasChanged()) { - depth = encoders[3]->cur; - mcl_seq.lfo_tracks[0].params[0].depth = depth; - mcl_seq.lfo_tracks[0].params[1].depth = depth; - load_wavetable(waveform, &mcl_seq.lfo_tracks[0], depth); + lfo_track->params[1].depth = encoders[3]->cur; + load_wavetable(waveform, lfo_track, 1, encoders[3]->cur); } } } void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, - uint8_t depth) { + uint8_t param, uint8_t depth) { SinLFO sin_lfo; TriLFO tri_lfo; ExpLFO exp_lfo; @@ -151,7 +162,7 @@ void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, } // ExpLFO exp_lfo(20); for (uint8_t n = 0; n < LFO_LENGTH; n++) { - lfo_track->wav_table[n] = + lfo_track->wav_table[param][n] = (float)lfo->get_sample(n) * ((float)depth * (float)(DIV_1_127)); } } @@ -184,7 +195,7 @@ void LFOPage::display() { oled_display.setCursor(0, 0); oled_display.print("LFO "); - if (mcl_seq.lfo_tracks[0].enable) { + if (lfo_track->enable) { oled_display.print("ON"); } else { oled_display.print("OFF"); @@ -214,7 +225,7 @@ void LFOPage::display() { for (uint8_t n = 0; n < LFO_LENGTH; n++) { oled_display.drawPixel(x + n, - (float)32 - mcl_seq.lfo_tracks[0].wav_table[n] * + (float)32 - lfo_track->wav_table[0][n] * ((float)h * (float)DIV_1_127), WHITE); if (n % 2 == 0) { @@ -231,12 +242,27 @@ void LFOPage::display() { } if (page_mode == LFO_SETTINGS) { mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SHP"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "TYP"); mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SPD"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP1"); + mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP2"); } - draw_pattern_mask(); + // draw_pattern_mask(); + + oled_display.setCursor(0, 20); + switch (lfo_track->mode) { + case LFO_MODE_FREE: + oled_display.print("FREE"); + break; + case LFO_MODE_TRIG: + draw_pattern_mask(); + oled_display.print("TRIG"); + break; + case LFO_MODE_ONE: + draw_pattern_mask(); + oled_display.print("ONE"); + break; + } oled_display.display(); @@ -245,18 +271,19 @@ void LFOPage::display() { void LFOPage::draw_pattern_mask() { - uint8_t trig_x = 10; + uint8_t trig_x = 32; uint8_t trig_y = 20; uint8_t seq_w = 5; uint8_t trig_h = 5; - uint64_t pattern_mask = mcl_seq.lfo_tracks[0].pattern_mask; + uint64_t pattern_mask = lfo_track->pattern_mask; + //uint64_t pattern_mask = mcl_seq.lfo_tracks[0].pattern_mask; uint8_t offset = 0; for (int i = 0; i < 16; i++) { uint8_t idx = i + offset; - bool in_range = idx < mcl_seq.lfo_tracks[0].length; + bool in_range = idx < lfo_track->length; if (note_interface.notes[i] == 1) { // TI feedback @@ -321,16 +348,16 @@ bool LFOPage::handleEvent(gui_event_t *event) { uint8_t midi_device = device; if (event->mask == EVENT_BUTTON_PRESSED) { if (device == DEVICE_A4) { - // GUI.setPage(&seq_extstep_page); + // GUI.setPage(&seq_extstep_page) return true; } - if (!IS_BIT_SET64(mcl_seq.lfo_tracks[0].pattern_mask, step)) { + if (!IS_BIT_SET64(lfo_track->pattern_mask, step)) { - SET_BIT64(mcl_seq.lfo_tracks[0].pattern_mask, step); + SET_BIT64(lfo_track->pattern_mask, step); } else { DEBUG_PRINTLN("Trying to clear"); if (clock_diff(note_interface.note_hold, slowclock) < TRIG_HOLD_TIME) { - CLEAR_BIT64(mcl_seq.lfo_tracks[0].pattern_mask, step); + CLEAR_BIT64(lfo_track->pattern_mask, step); } } } @@ -350,13 +377,23 @@ bool LFOPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + if (lfo_track->mode >= LFO_MODE_ONE) { + lfo_track->mode = 0; + } else { + lfo_track->mode += 1; + } + if (lfo_track->mode == LFO_MODE_FREE) { + md_exploit.off(); + } else { + md_exploit.on(); + } } if (EVENT_PRESSED(event, Buttons.BUTTON4)) { - mcl_seq.lfo_tracks[0].enable = !(mcl_seq.lfo_tracks[0].enable); - if (!mcl_seq.lfo_tracks[0].enable) { - mcl_seq.lfo_tracks[0].reset_params_offset(); - } + lfo_track->enable = !(lfo_track->enable); + if (!lfo_track->enable) { + lfo_track->reset_params_offset(); + } } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { GUI.setPage(&page_select_page); diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h index b5ab4b7fa..4dc58c5da 100644 --- a/avr/cores/megacommand/MCL/LFOPage.h +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -15,9 +15,10 @@ // class LFOPage : public LightPage, MidiCallback { public: - LFOPage(Encoder *e1 = NULL, Encoder *e2 = NULL, + LFOPage(LFOSeqTrack *lfo_track_, Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { + lfo_track = lfo_track_; } bool handleEvent(gui_event_t *event); @@ -26,8 +27,11 @@ class LFOPage : public LightPage, MidiCallback { bool page_mode; uint8_t page_id; + LFOSeqTrack *lfo_track; + uint8_t waveform; uint8_t depth; + uint8_t depth2; void display(); void setup(); @@ -36,7 +40,7 @@ class LFOPage : public LightPage, MidiCallback { void loop(); void cleanup(); void update_encoders(); - void load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, uint8_t depth); + void load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, uint8_t param, uint8_t depth); void setup_callbacks(); void remove_callbacks(); diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index a9affcc0b..6241bef9a 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -9,7 +9,7 @@ uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { switch (offset_behaviour) { case LFO_OFFSET_CENTRE: - val = offset + (wav_table[sample_count] - (depth / 2)); + val = offset + (wav_table[param][sample_count] - (depth / 2)); if (val > 127) { return 127; } @@ -20,7 +20,7 @@ uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { } break; case LFO_OFFSET_MAX: - val = offset - depth + wav_table[sample_count]; + val = offset - depth + wav_table[param][sample_count]; if (val > 127) { return 127; } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index 7659a62a1..744dff058 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -34,7 +34,7 @@ class LFOSeqParam { class LFOSeqTrack { public: - uint8_t wav_table[128]; + uint8_t wav_table[2][128]; uint8_t sample_count; uint8_t sample_hold = 0; From 9201947b2c464c66a4bc3c1484ae1625806b10c7 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 17:35:14 +0800 Subject: [PATCH 165/469] rlck page LEN, base seq page page_count, labels --- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 3 +- avr/cores/megacommand/MCL/SeqPage.cpp | 31 ++++++++++++-------- avr/cores/megacommand/MCL/SeqPage.h | 1 + avr/cores/megacommand/MCL/SeqPtcPage.cpp | 4 +-- avr/cores/megacommand/MCL/SeqRlckPage.cpp | 14 +++++++++ 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 9bea76bd4..2727039c9 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -11,7 +11,7 @@ void SeqExtStepPage::config() { char buf[len1] = {'\0'}; - strcpy(info2, "NOTE"); + strcpy(info2, "EXT"); } void SeqExtStepPage::config_encoders() { #ifdef EXT_TRACKS @@ -29,6 +29,7 @@ void SeqExtStepPage::config_encoders() { } void SeqExtStepPage::init() { + page_count = 8; DEBUG_PRINTLN("seq extstep init"); curpage = SEQ_EXTSTEP_PAGE; md_exploit.on(); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 741e3ed38..14e80ac5c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -9,6 +9,7 @@ uint8_t SeqPage::length = 0; uint8_t SeqPage::resolution = 0; uint8_t SeqPage::apply = 0; uint8_t SeqPage::ignore_button_release = 255; +uint8_t SeqPage::page_count = 4; bool SeqPage::show_track_menu = false; void SeqPage::create_chars_seq() { @@ -26,6 +27,7 @@ void SeqPage::setup() { create_chars_seq(); } void SeqPage::init() { ignore_button_release = 255; + page_count = 4; ((MCLEncoder *)encoders[2])->handler = pattern_len_handler; seqpage_midi_events.setup_callbacks(); #ifdef OLED_DISPLAY @@ -142,13 +144,9 @@ bool SeqPage::handleEvent(gui_event_t *event) { if (EVENT_RELEASED(event, Buttons.BUTTON2)) { if (ignore_button_release != 2) { ignore_button_release = 255; - uint8_t pagemax = 4; page_select += 1; - if (SeqPage::midi_device != DEVICE_MD) { - pagemax = 8; - } - if (page_select >= pagemax) { + if (page_select >= page_count) { page_select = 0; } } @@ -630,7 +628,7 @@ void SeqPage::display() { bool is_md = (midi_device == DEVICE_MD); #ifdef EXT_TRACKS - bool ext_is_a4 = (seq_extstep_page.midi_device == DEVICE_A4); + bool ext_is_a4 = Analog4.connected; #else bool ext_is_a4 = false; #endif @@ -679,7 +677,9 @@ void SeqPage::display() { oled_display.drawPixel(cir_x2, tri_y, BLACK); oled_display.drawPixel(cir_x1, tri_y + 4, BLACK); oled_display.drawPixel(cir_x2, tri_y + 4, BLACK); - } else if (MidiClock.state == 2) { + } + + if (MidiClock.state == 2) { oled_display.drawLine(tri_x, tri_y, tri_x, tri_y + 4, WHITE); oled_display.fillTriangle(tri_x + 1, tri_y, tri_x + 3, tri_y + 2, tri_x + 1, tri_y + 4, WHITE); @@ -689,22 +689,27 @@ void SeqPage::display() { // draw page index uint8_t pidx_x = pidx_x0; - bool blink = !MidiClock.getBlinkHint(true); + bool blink = MidiClock.getBlinkHint(true); uint8_t playing_idx = (MidiClock.bar_counter - 1) % 4; - for (uint8_t i = 0; i < 4; ++i) { - oled_display.drawRect(pidx_x, pidx_y, pidx_w, pidx_h, WHITE); + uint8_t w = pidx_w; + if (page_count == 8) { w = w / 2; } + + for (uint8_t i = 0; i < page_count; ++i) { + oled_display.drawRect(pidx_x, pidx_y, w, pidx_h, WHITE); // highlight page_select if (page_select == i) { - oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, pidx_w - 2, WHITE); + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); } // blink playing_idx if (playing_idx == i && blink) { if (page_select == i) { - oled_display.drawFastHLine(pidx_x + 2, pidx_y + 1, 2, BLACK); + if(page_count == 4) { + oled_display.drawFastHLine(pidx_x + 2, pidx_y + 1, 2, BLACK); + } } else { - oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, pidx_w - 2, WHITE); + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); } } diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 3fd4ede96..2a959e9e8 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -20,6 +20,7 @@ class SeqPage : public LightPage { public: // Static variables shared amongst derived objects static uint8_t page_select; + static uint8_t page_count; static uint8_t midi_device; static uint8_t length; static uint8_t resolution; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 00e063fd3..2cd463bb5 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -519,7 +519,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { DEBUG_DUMP(channel); uint8_t pitch = seq_ptc_page.calc_pitch(note_num); - uint8_t scaled_pitch = pitch - (pitch / NUM_KEYS) * NUM_KEYS; + uint8_t scaled_pitch = pitch - (pitch / 24) * 24; SET_BIT64(seq_ptc_page.note_mask, scaled_pitch); seq_ptc_page.queue_redraw(); @@ -563,7 +563,7 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { uint8_t note_num = msg[1]; uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); uint8_t pitch = seq_ptc_page.calc_pitch(note_num); - uint8_t scaled_pitch = pitch - (pitch / NUM_KEYS) * NUM_KEYS; + uint8_t scaled_pitch = pitch - (pitch / 24) * 24; CLEAR_BIT64(seq_ptc_page.note_mask, scaled_pitch); seq_ptc_page.queue_redraw(); diff --git a/avr/cores/megacommand/MCL/SeqRlckPage.cpp b/avr/cores/megacommand/MCL/SeqRlckPage.cpp index d0573fe08..d0faae528 100644 --- a/avr/cores/megacommand/MCL/SeqRlckPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRlckPage.cpp @@ -87,6 +87,20 @@ void SeqRlckPage::display() { return; } SeqPage::display(); + + draw_knob_frame(); + + uint8_t len = encoders[2]->getValue(); +#ifdef EXT_TRACKS + if (SeqPage::midi_device != DEVICE_MD) { + len = len / (2 / mcl_seq.ext_tracks[last_ext_track].resolution); + } +#endif + + char K[4]; + itoa(len, K, 10); + draw_knob(2, "LEN", K); + bool show_current_step = false; draw_lock_mask(page_select * 16, show_current_step); draw_pattern_mask(page_select * 16, DEVICE_MD, show_current_step); From 356400a91a6cb299f47978b94f6f758b2f71c31b Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 17:38:44 +0800 Subject: [PATCH 166/469] cleanup --- avr/cores/megacommand/MCL/SeqPage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 14e80ac5c..1dd933244 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -677,9 +677,7 @@ void SeqPage::display() { oled_display.drawPixel(cir_x2, tri_y, BLACK); oled_display.drawPixel(cir_x1, tri_y + 4, BLACK); oled_display.drawPixel(cir_x2, tri_y + 4, BLACK); - } - - if (MidiClock.state == 2) { + } else if (MidiClock.state == 2) { oled_display.drawLine(tri_x, tri_y, tri_x, tri_y + 4, WHITE); oled_display.fillTriangle(tri_x + 1, tri_y, tri_x + 3, tri_y + 2, tri_x + 1, tri_y + 4, WHITE); @@ -713,7 +711,7 @@ void SeqPage::display() { } } - pidx_x += pidx_w + 1; + pidx_x += w + 1; } // draw info lines From 18230da86249ae682a9fb72c308faec6dcfa8349 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 27 Oct 2019 21:01:50 +1100 Subject: [PATCH 167/469] ExtSeq was broken. woops --- avr/cores/megacommand/MCL/MCLSeq.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index bcca732d2..a42e47d58 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -130,36 +130,22 @@ void MCLSeq::onMidiStopCallback() { #endif void MCLSeq::seq() { - // if (in_sysex == 0) { - - // for (uint8_t i = 0; i < 1; i++) { - // lfos[i].seq(); - // } for (uint8_t i = 0; i < num_md_tracks; i++) { md_tracks[i].seq(); } - // } - // if (in_sysex2 == 0) { -#ifdef EXT_TRACKS - for (uint8_t i = 0; i < num_ext_tracks; i++) { - ext_tracks[i].seq(); + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + lfo_tracks[i].seq(); } -#endif - // } + + #ifdef EXT_TRACKS for (uint8_t i = 0; i < num_ext_tracks; i++) { ext_tracks[i].seq(); } #endif - for (uint8_t i = 0; i < num_lfo_tracks; i++) { - lfo_tracks[i].seq(); - } -// if (MidiClock.step_counter == 1) { -// lfo_sample = 0; -// } } #ifdef MEGACOMMAND #pragma GCC pop_options From 134a825bd9e3428c5ae695a67fefb9906e0cdb9e Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 18:30:31 +0800 Subject: [PATCH 168/469] fix led blink to align with MD --- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 12 ++++++------ avr/cores/megacommand/MCL/SeqPage.cpp | 10 ++-------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 2727039c9..6a3276e7b 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -151,16 +151,16 @@ void SeqExtStepPage::display() { draw_knob_frame(); char K[4]; - if (seq_param1.getValue() == 0) { + if (encoders[0]->getValue() == 0) { strcpy(K, "L1"); - } else if (seq_param1.getValue() <= 8) { + } else if (encoders[0]->getValue() <= 8) { strcpy(K, "L "); - K[1] = seq_param1.getValue() + '0'; - } else if (seq_param1.getValue() <= 13) { + K[1] = encoders[0]->getValue() + '0'; + } else if (encoders[0]->getValue() <= 13) { strcpy(K, "P "); uint8_t prob[5] = {1, 2, 5, 7, 9}; - K[1] = prob[seq_param1.getValue() - 9] + '0'; - } else if (seq_param1.getValue() == 14) { + K[1] = prob[encoders[0]->getValue() - 9] + '0'; + } else if (encoders[0]->getValue() == 14) { strcpy(K, "1S"); } draw_knob(0, "COND", K); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 1dd933244..2c0185fe1 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -437,12 +437,8 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { } else if (current ^ locked) { // highlight oled_display.fillRect(led_x, led_y, seq_w, led_h, WHITE); - } else if (current && locked) { - // highlight 2 - oled_display.fillRect(led_x, led_y, seq_w, led_h, WHITE); - oled_display.drawPixel(led_x + 2, led_y + 1, BLACK); } else { - // frame only + // (current && locked) or (not current && not locked), frame only oled_display.drawRect(led_x, led_y, seq_w, led_h, WHITE); } @@ -703,9 +699,7 @@ void SeqPage::display() { // blink playing_idx if (playing_idx == i && blink) { if (page_select == i) { - if(page_count == 4) { - oled_display.drawFastHLine(pidx_x + 2, pidx_y + 1, 2, BLACK); - } + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, BLACK); } else { oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); } From 0185ae45f26b5e28a3428bd8ead30d12e1d5f946 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 18:31:31 +0800 Subject: [PATCH 169/469] add LEN for SeqRtrkPage --- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index eef6b3f98..8cd5bda52 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -78,6 +78,19 @@ void SeqRtrkPage::display() { if ((!redisplay) && (MidiClock.state == 2)) { return; } SeqPage::display(); + draw_knob_frame(); + + uint8_t len = encoders[2]->getValue(); +#ifdef EXT_TRACKS + if (SeqPage::midi_device != DEVICE_MD) { + len = len / (2 / mcl_seq.ext_tracks[last_ext_track].resolution); + } +#endif + + char K[4]; + itoa(len, K, 10); + draw_knob(2, "LEN", K); + bool show_current_step = false; draw_lock_mask(page_select * 16, show_current_step); draw_pattern_mask(page_select * 16, DEVICE_MD, show_current_step); From eaa174403cf2b0d9b4858436b497be4bc0c6b78c Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 18:33:33 +0800 Subject: [PATCH 170/469] clean note_mask in SeqPtcPage::init() --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 2cd463bb5..30e07c66d 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -58,6 +58,7 @@ void SeqPtcPage::init() { ((MCLEncoder *)encoders[2])->handler = ptc_pattern_len_handler; recording = false; midi_events.setup_callbacks(); + note_mask = 0; DEBUG_PRINTLN("control mode:"); DEBUG_PRINTLN(mcl_cfg.uart2_ctrl_mode); if (mcl_cfg.uart2_ctrl_mode == MIDI_LOCAL_MODE) { From ac7e15851d9d8aa7075d1c6f00734ed83de86d30 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 19:09:25 +0800 Subject: [PATCH 171/469] page index layout --- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 1 + avr/cores/megacommand/MCL/SeqPage.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 6a3276e7b..40e0d71d9 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -13,6 +13,7 @@ void SeqExtStepPage::config() { strcpy(info2, "EXT"); } + void SeqExtStepPage::config_encoders() { #ifdef EXT_TRACKS if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 2c0185fe1..515d79a1a 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -686,7 +686,10 @@ void SeqPage::display() { bool blink = MidiClock.getBlinkHint(true); uint8_t playing_idx = (MidiClock.bar_counter - 1) % 4; uint8_t w = pidx_w; - if (page_count == 8) { w = w / 2; } + if (page_count == 8) { + w /= 2; + pidx_x -= 1; + } for (uint8_t i = 0; i < page_count; ++i) { oled_display.drawRect(pidx_x, pidx_y, w, pidx_h, WHITE); From 486d61cc8d21da979642107f9c8f4f5b33b179b0 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 27 Oct 2019 21:37:44 +0800 Subject: [PATCH 172/469] ext step editing: limit channel range to 0..3 --- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 40e0d71d9..a7159dee9 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -362,15 +362,18 @@ void SeqExtStepMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { // For each incoming note, check to see if note interface has any steps // selected For selected steps record notes. #ifdef EXT_TRACKS - DEBUG_PRINTLN("note on midi2 ext"); uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); - if (last_ext_track < mcl_seq.num_ext_tracks) { + DEBUG_PRINT("note on midi2 ext, "); + DEBUG_DUMP(channel); + + if (channel < mcl_seq.num_ext_tracks) { last_ext_track = channel; seq_extstep_page.config_encoders(); if (MidiClock.state != 2) { mcl_seq.ext_tracks[channel].note_on(msg[1]); } + for (uint8_t i = 0; i < 16; i++) { if (note_interface.notes[i] == 1) { mcl_seq.ext_tracks[channel].set_ext_track_step( @@ -384,7 +387,7 @@ void SeqExtStepMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { void SeqExtStepMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { #ifdef EXT_TRACKS uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); - if (MidiClock.state != 2) { + if (channel < mcl_seq.num_ext_tracks && MidiClock.state != 2) { mcl_seq.ext_tracks[channel].note_off(msg[1]); } #endif From 0e8b6f744d57ef76cdc2e4b502990b0a12ed17e8 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 28 Oct 2019 00:45:24 +0800 Subject: [PATCH 173/469] fix ext note display --- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 51 +++++++++++++------- avr/cores/megacommand/MCL/SeqPage.cpp | 2 +- avr/cores/megacommand/MCL/Shared.h | 4 +- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index a7159dee9..3cb443c11 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -9,7 +9,13 @@ void SeqExtStepPage::config() { // config info labels constexpr uint8_t len1 = sizeof(info1); - char buf[len1] = {'\0'}; +#ifdef EXT_TRACKS + if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { + strcpy(info1, "HI-RES"); + } else { + strcpy(info1, "LOW-RES"); + } +#endif strcpy(info2, "EXT"); } @@ -193,9 +199,8 @@ void SeqExtStepPage::display() { draw_knob(1, "UTIM", K); MusicalNotes number_to_note; - uint8_t note; uint8_t notes_held = 0; - uint8_t i; + uint8_t i, j; for (i = 0; i < 16; i++) { if (note_interface.notes[i] == 1) { notes_held += 1; @@ -206,23 +211,35 @@ void SeqExtStepPage::display() { draw_knob(2, "LEN", K); if (notes_held > 0) { - uint8_t x = knob_x0 + knob_w * 3 + 4; + uint8_t x = knob_x0 + knob_w * 3 + 2; auto *oldfont = oled_display.getFont(); oled_display.setFont(&TomThumb); - for (i = 0; i < 4; i++) { - oled_display.setCursor(x, i * 5 + 5); - note = active_track.notes[i][note_interface.last_note + page_select * 16]; - if (note != 0) { - note = note - 1; - uint8_t oct = note / 12; - uint8_t note = note - 12 * (note / 12); - if (active_track.notes[i][note_interface.last_note + page_select * 16] > - 0) { - oled_display.print(number_to_note.notes_upper[note]); - } else { - oled_display.print(number_to_note.notes_lower[note]); + uint8_t note_idx = 0; + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + oled_display.setCursor(x + j * 11, 6 + i * 8); + const int8_t &c_note = + active_track + .notes[note_idx][note_interface.last_note + page_select * 16]; + if (c_note != 0) { + uint8_t note = abs(c_note); + DEBUG_DUMP(c_note); + DEBUG_DUMP(note); + note = note - 1; + uint8_t oct = note / 12; + note = note - 12 * oct; + DEBUG_DUMP(note); + DEBUG_DUMP(oct); + if (c_note > 0) { + oled_display.print(number_to_note.notes_upper[note]); + } else { + oled_display.print(number_to_note.notes_lower[note]); + } + + oled_display.print(oct); } - oled_display.print(oct); + + ++note_idx; } } oled_display.setFont(oldfont); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 515d79a1a..12cf5ff9f 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -520,7 +520,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, oled_display.drawFastVLine(trig_x - 1, trig_y, trig_h, WHITE); } - if (right_most) { // right | + if (right_most && note_held) { // right | oled_display.drawFastVLine(trig_x + seq_w, trig_y, trig_h, WHITE); } diff --git a/avr/cores/megacommand/MCL/Shared.h b/avr/cores/megacommand/MCL/Shared.h index 758922f64..0a74b4da7 100644 --- a/avr/cores/megacommand/MCL/Shared.h +++ b/avr/cores/megacommand/MCL/Shared.h @@ -7,9 +7,9 @@ #include "Math.h" struct MusicalNotes { - const char *notes_upper[16] = {"C ", "C#", "D ", "D#", "E ", "F", + const char *notes_upper[16] = {"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "}; - const char *notes_lower[16] = {"c ", "c#", "d ", "d#", "e ", "f", + const char *notes_lower[16] = {"c ", "c#", "d ", "d#", "e ", "f ", "f#", "g ", "g#", "a ", "a#", "b "}; }; From 625b02f2c30d92cc6c5049a108ec45b4a93ec6c2 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 28 Oct 2019 00:47:28 +0800 Subject: [PATCH 174/469] SeqPage: playing_idx fix --- avr/cores/megacommand/MCL/SeqPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 12cf5ff9f..61153acf8 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -684,7 +684,8 @@ void SeqPage::display() { // draw page index uint8_t pidx_x = pidx_x0; bool blink = MidiClock.getBlinkHint(true); - uint8_t playing_idx = (MidiClock.bar_counter - 1) % 4; + // XXX should retrieve true track length + uint8_t playing_idx = (MidiClock.bar_counter - 1) % page_count; uint8_t w = pidx_w; if (page_count == 8) { w /= 2; From aa227bda918c4f1cd37f7807d153397700fa8937 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 28 Oct 2019 21:13:25 +1100 Subject: [PATCH 175/469] experimenting with different clock calcs --- avr/cores/megacommand/Midi/MidiClock.h | 31 ++++++++++++++------------ avr/cores/megacommand/main.cpp | 17 +++++--------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index 0b688da1b..bc11b4e9c 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -46,14 +46,16 @@ class MidiClockClass { volatile uint8_t mod12_counter; volatile uint8_t mod6_counter; volatile uint8_t mod3_counter; - volatile uint8_t mod6_free_counter; + volatile uint8_t mod4_free_counter; + volatile uint16_t div128_counter; + volatile uint16_t div128_time; + volatile uint16_t clock_last_time; - volatile uint16_t div192th_time; - volatile uint16_t last_clock16; - volatile uint16_t last_diff_clock16; - volatile uint16_t diff_clock16; + volatile uint16_t last_diff_clock4; + volatile uint16_t diff_clock4; + volatile uint16_t last_clock4; volatile uint8_t bar_counter; volatile uint8_t beat_counter; @@ -299,9 +301,10 @@ class MidiClockClass { } void calc_tempo() { - if (last_diff_clock16 != diff_clock16) { - tempo = ((float)75000 / ((float)diff_clock16)); - last_diff_clock16 = diff_clock16; + DEBUG_PRINTLN(diff_clock4); + if (last_diff_clock4 != diff_clock4) { + tempo = ((float)50000 / ((float)diff_clock4)); + last_diff_clock4 = diff_clock4; } } @@ -311,12 +314,12 @@ class MidiClockClass { } ALWAYS_INLINE() void MidiClockClass::incrementCounters() { - mod6_free_counter++; - if (mod6_free_counter == 6) { - diff_clock16 = midi_clock_diff(last_clock16, clock); - div192th_time = diff_clock16 * .08333; - mod6_free_counter = 0; - last_clock16 = clock; + mod4_free_counter++; + if (mod4_free_counter == 4) { + diff_clock4 = midi_clock_diff(last_clock4, clock); + last_clock4 = clock; + div128_time = diff_clock4 / 8; + mod4_free_counter = 0; } if (state == STARTED) { div96th_counter++; diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index f57434dd1..5ad6b6c8a 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -195,9 +195,12 @@ ISR(TIMER1_COMPA_vect) { select_bank(0); clock++; - if (MidiClock.state == 2) { - if (clock_diff(MidiClock.clock_last_time, clock) >= MidiClock.div192th_time) { - if (MidiClock.div192th_counter != MidiClock.div192th_counter_last) { + MidiClock.div128_counter++; + + if (MidiClock.div128_counter >= MidiClock.div128_time) { + MidiClock.div128_counter = 0; + if (MidiClock.state == 2) { + if (MidiClock.div192th_counter != MidiClock.div192th_counter_last) { MidiClock.increment192Counter(); MidiClock.div192th_counter_last = MidiClock.div192th_counter; if ((enable_clock_callbacks)) { @@ -205,14 +208,6 @@ ISR(TIMER1_COMPA_vect) { } } } - /* - if (MidiClock.div96th_counter != MidiClock.div96th_counter_last) { - MidiClock.div96th_counter_last = MidiClock.div96th_counter; - if ((enable_clock_callbacks)) { - MidiClock.callCallbacks(); - } - } - */ } } From 78050acb2755f17533786ff5884d8845f7a8fabf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 28 Oct 2019 21:42:25 +1100 Subject: [PATCH 176/469] Flash current step trig when played --- avr/cores/megacommand/MCL/SeqPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 61153acf8..91058594a 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -466,7 +466,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, } else if (!in_range) { // don't draw } else { - if (IS_BIT_SET64(pattern_mask, i + offset)) { + if (IS_BIT_SET64(pattern_mask, i + offset) && (i + offset != active_track.step_count)) { /*If the bit is set, there is a trigger at this position. */ oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); } else { From 4ec3223ce245647ab2336babfe562053886cd4aa Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 28 Oct 2019 22:01:35 +1100 Subject: [PATCH 177/469] Updated global routing would not be transmitted when LFOs running --- avr/cores/megacommand/MCL/RoutePage.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index 880817feb..d846a3f3e 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -106,8 +106,11 @@ void RoutePage::update_globals() { while ((MidiClock.state == 2) && ((MidiClock.mod12_counter > 6) || (MidiClock.mod12_counter == 0))) ; + USE_LOCK(); + SET_LOCK(); MD.global.toSysex(encoder2); - hasChanged = false; + CLEAR_LOCK(); + hasChanged = false; } } From 59f43d5ab14395902b9e79e426bec353c3e0a9ef Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 29 Oct 2019 12:02:26 +1100 Subject: [PATCH 178/469] Improve Trimetric Sprites --- art/sprites/mc_trimetric.png | Bin 210 -> 0 bytes art/sprites/md_wide_trimetric.png | Bin 607 -> 0 bytes art/sprites/trimetric/analog4.png | Bin 0 -> 599 bytes art/sprites/trimetric/mc.png | Bin 0 -> 268 bytes art/sprites/trimetric/md.png | Bin 0 -> 612 bytes .../{md_trimetric.png => trimetric/md_tight.png} | Bin 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 art/sprites/mc_trimetric.png delete mode 100644 art/sprites/md_wide_trimetric.png create mode 100644 art/sprites/trimetric/analog4.png create mode 100644 art/sprites/trimetric/mc.png create mode 100644 art/sprites/trimetric/md.png rename art/sprites/{md_trimetric.png => trimetric/md_tight.png} (100%) diff --git a/art/sprites/mc_trimetric.png b/art/sprites/mc_trimetric.png deleted file mode 100644 index fc26c3d01b7ca7de72287b9025abeb3679e9df18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&V!3HE>w#^4|7>k44ofy`glX(f`^mw{BhFA#B zoxsS~puofY@qd5V>XMbI6BIi&dv0&M5mh_ayzBk5@O1Ta JS?83{1ORjLQ)>VK diff --git a/art/sprites/md_wide_trimetric.png b/art/sprites/md_wide_trimetric.png deleted file mode 100644 index 6219db2338a5847778c874b4b107a7f8abca4759..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 607 zcmV-l0-*hgP)Px%8A(JzR7ef2lnIW5Fc1U-;{QL+L0Ma1iCp^j_EFrj=CFGQ4am@q;5MQ z2D2IHP(@%<;HWP4vY<0U#5i}YcDvLG#;2*~XsTp2P7FRPc`|Or^(Q&D6SAmnH~#OK zCB*83OVP&mW5wpctdH{TglOdN@ZJzR%%&vL7bnJ^UxdgZ=gKj?pU-C#AR(3|vA%DK zd2tOKc=Bu?F#E|n&%U&~cSsV+a{zv>D$rP4A0&JA!M0he^Tx3mfU$$E87GN&tT^l9 zYu>t+y4dYYO~OWOzT$$D0N=cQ*7x!`SZuZJEdwZcWNo6x+8aooEzUlzea0iZosh*! zm@z&(FDrfISbIWR@#IhU9fFXph>UZfm^+22w-b~?^aUcq#r?9LdFxkfCkP^++Ap=I tQELtrZ+z8$-JwdD1CLH4g;$PW;t%N?4=O&W+_wM#002ovPDHLkV1g$C8T|kN diff --git a/art/sprites/trimetric/analog4.png b/art/sprites/trimetric/analog4.png new file mode 100644 index 0000000000000000000000000000000000000000..29f655e0520e7391ac87ced575747192d14fc08b GIT binary patch literal 599 zcmV-d0;v6oP)Px%5lKWrR7ef2l!=mqFbqSNEXzt3I^Cb^??)@8rfKR_ z$+O-SBUvN9999zu&hxw(!OR&`^JNzykT-x44BT$Fb~BoambdVyhA`lx5pRW^_081J z3~7XcqoIjqT(9V|p|eKL-qfKXPLj*q6FS6v#D$Y9xf#njG?ZdCgRSe@Ue@Fw3XjE0 zol6|7#;MVL_6|)U<7HVkp%k;mE{-^{xqA++7&&!5gw9YlIZR9uc27?%4$rt6RLi=W z;_e)}Gem>r9BPBL%pD?To?Nih9&xRAh7j@L`dle4#_sTplbc2=PLEjXG()vWu4|fE zVhWh=)z{ywztGnK>a67OO>Un>ifAM;nld*|F=Gds%M~}bo_M<y9x+_{r)O}lCNi&a3ONvtqFMihC&xayn<2m) z%pgux2P#x?bL)y%oZOn+x^G_@M9HJwCTc82&8>s;R6Og(pZ0b`5~^V44)o>C9^y|% zdXj0yk%002ovPDHLkV1h2c6o3E# literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/mc.png b/art/sprites/trimetric/mc.png new file mode 100644 index 0000000000000000000000000000000000000000..c88514386d0ac150169cf1bf2447ae1678e31dab GIT binary patch literal 268 zcmV+n0rUQeP)Px##z{m$R5%gkk5LYTFbqSx-T%%TuJ$T7O+ZuG*N`CTv*VQ3{^5BYKdJZDCx+g# zAcpG31XI8Miz+k*#`Ci1VFYu=%+{pK>(%wqV-UCsWypPEEJ7dk`RN$o_QPO}Jotq1 ziqVTtkAzG-;|ho~@!XdHso9`2_w|6|BbM literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/md.png b/art/sprites/trimetric/md.png new file mode 100644 index 0000000000000000000000000000000000000000..941abe432f8ae98d3097a6b015361cf304f12bc6 GIT binary patch literal 612 zcmV-q0-ODbP)Px%9!W$&R7ef2l#6bIFc1U_;{X3RNg1nStuKZq0!z-l_VyNIvb^^7bJI$yWmyJR z;;awF2)6JqyHx~)>$)}}i1|d+dRavf}5fp2odAZBhl1P1^HA-gzppPiHOnK3nrJnuh|C3)iX z#TnOr^5(?QLykRs_7(u1JrRv{Fg<5EYI1^QiI?w95D7QO=c;59WnEu#V|&TF>^Emj zy`RvmNRI8lt@o%B6Cd@9#X5L~6^HD0sS}J(Q_ay-$!eS!d{**g+=}~8a`Y3jsBJg? z@0caT>Vr$s#`a^y=D@7a^6i9Z<~H zxCRb9dA7XSPyX?E^w(ssc9YyD8q8rO&pduE@#c-S^+B>%zt7s8R}KSUTn88@iFm96 ztc$OC>ssn!`#&`a8?pI`3r+%j^Y&Rk%I9FQ)wZt;px~Lci5lx@AbGYp`?U5M&ul*- zi Date: Tue, 29 Oct 2019 12:02:26 +1100 Subject: [PATCH 179/469] Improve Trimetric Sprites --- art/sprites/mc_trimetric.png | Bin 210 -> 0 bytes art/sprites/md_wide_trimetric.png | Bin 607 -> 0 bytes art/sprites/trimetric/analog4.png | Bin 0 -> 599 bytes art/sprites/trimetric/mc.png | Bin 0 -> 268 bytes art/sprites/trimetric/md.png | Bin 0 -> 612 bytes .../{md_trimetric.png => trimetric/md_tight.png} | Bin 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 art/sprites/mc_trimetric.png delete mode 100644 art/sprites/md_wide_trimetric.png create mode 100644 art/sprites/trimetric/analog4.png create mode 100644 art/sprites/trimetric/mc.png create mode 100644 art/sprites/trimetric/md.png rename art/sprites/{md_trimetric.png => trimetric/md_tight.png} (100%) diff --git a/art/sprites/mc_trimetric.png b/art/sprites/mc_trimetric.png deleted file mode 100644 index fc26c3d01b7ca7de72287b9025abeb3679e9df18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&V!3HE>w#^4|7>k44ofy`glX(f`^mw{BhFA#B zoxsS~puofY@qd5V>XMbI6BIi&dv0&M5mh_ayzBk5@O1Ta JS?83{1ORjLQ)>VK diff --git a/art/sprites/md_wide_trimetric.png b/art/sprites/md_wide_trimetric.png deleted file mode 100644 index 6219db2338a5847778c874b4b107a7f8abca4759..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 607 zcmV-l0-*hgP)Px%8A(JzR7ef2lnIW5Fc1U-;{QL+L0Ma1iCp^j_EFrj=CFGQ4am@q;5MQ z2D2IHP(@%<;HWP4vY<0U#5i}YcDvLG#;2*~XsTp2P7FRPc`|Or^(Q&D6SAmnH~#OK zCB*83OVP&mW5wpctdH{TglOdN@ZJzR%%&vL7bnJ^UxdgZ=gKj?pU-C#AR(3|vA%DK zd2tOKc=Bu?F#E|n&%U&~cSsV+a{zv>D$rP4A0&JA!M0he^Tx3mfU$$E87GN&tT^l9 zYu>t+y4dYYO~OWOzT$$D0N=cQ*7x!`SZuZJEdwZcWNo6x+8aooEzUlzea0iZosh*! zm@z&(FDrfISbIWR@#IhU9fFXph>UZfm^+22w-b~?^aUcq#r?9LdFxkfCkP^++Ap=I tQELtrZ+z8$-JwdD1CLH4g;$PW;t%N?4=O&W+_wM#002ovPDHLkV1g$C8T|kN diff --git a/art/sprites/trimetric/analog4.png b/art/sprites/trimetric/analog4.png new file mode 100644 index 0000000000000000000000000000000000000000..29f655e0520e7391ac87ced575747192d14fc08b GIT binary patch literal 599 zcmV-d0;v6oP)Px%5lKWrR7ef2l!=mqFbqSNEXzt3I^Cb^??)@8rfKR_ z$+O-SBUvN9999zu&hxw(!OR&`^JNzykT-x44BT$Fb~BoambdVyhA`lx5pRW^_081J z3~7XcqoIjqT(9V|p|eKL-qfKXPLj*q6FS6v#D$Y9xf#njG?ZdCgRSe@Ue@Fw3XjE0 zol6|7#;MVL_6|)U<7HVkp%k;mE{-^{xqA++7&&!5gw9YlIZR9uc27?%4$rt6RLi=W z;_e)}Gem>r9BPBL%pD?To?Nih9&xRAh7j@L`dle4#_sTplbc2=PLEjXG()vWu4|fE zVhWh=)z{ywztGnK>a67OO>Un>ifAM;nld*|F=Gds%M~}bo_M<y9x+_{r)O}lCNi&a3ONvtqFMihC&xayn<2m) z%pgux2P#x?bL)y%oZOn+x^G_@M9HJwCTc82&8>s;R6Og(pZ0b`5~^V44)o>C9^y|% zdXj0yk%002ovPDHLkV1h2c6o3E# literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/mc.png b/art/sprites/trimetric/mc.png new file mode 100644 index 0000000000000000000000000000000000000000..c88514386d0ac150169cf1bf2447ae1678e31dab GIT binary patch literal 268 zcmV+n0rUQeP)Px##z{m$R5%gkk5LYTFbqSx-T%%TuJ$T7O+ZuG*N`CTv*VQ3{^5BYKdJZDCx+g# zAcpG31XI8Miz+k*#`Ci1VFYu=%+{pK>(%wqV-UCsWypPEEJ7dk`RN$o_QPO}Jotq1 ziqVTtkAzG-;|ho~@!XdHso9`2_w|6|BbM literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/md.png b/art/sprites/trimetric/md.png new file mode 100644 index 0000000000000000000000000000000000000000..941abe432f8ae98d3097a6b015361cf304f12bc6 GIT binary patch literal 612 zcmV-q0-ODbP)Px%9!W$&R7ef2l#6bIFc1U_;{X3RNg1nStuKZq0!z-l_VyNIvb^^7bJI$yWmyJR z;;awF2)6JqyHx~)>$)}}i1|d+dRavf}5fp2odAZBhl1P1^HA-gzppPiHOnK3nrJnuh|C3)iX z#TnOr^5(?QLykRs_7(u1JrRv{Fg<5EYI1^QiI?w95D7QO=c;59WnEu#V|&TF>^Emj zy`RvmNRI8lt@o%B6Cd@9#X5L~6^HD0sS}J(Q_ay-$!eS!d{**g+=}~8a`Y3jsBJg? z@0caT>Vr$s#`a^y=D@7a^6i9Z<~H zxCRb9dA7XSPyX?E^w(ssc9YyD8q8rO&pduE@#c-S^+B>%zt7s8R}KSUTn88@iFm96 ztc$OC>ssn!`#&`a8?pI`3r+%j^Y&Rk%I9FQ)wZt;px~Lci5lx@AbGYp`?U5M&ul*- zi Date: Tue, 29 Oct 2019 15:29:48 +1100 Subject: [PATCH 180/469] remove multiplication from MidiClock div128 interpolation calculation. --- avr/cores/megacommand/main.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index 5ad6b6c8a..4dd773fd1 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -195,12 +195,10 @@ ISR(TIMER1_COMPA_vect) { select_bank(0); clock++; - MidiClock.div128_counter++; - if (MidiClock.div128_counter >= MidiClock.div128_time) { - MidiClock.div128_counter = 0; - if (MidiClock.state == 2) { - if (MidiClock.div192th_counter != MidiClock.div192th_counter_last) { + if (MidiClock.state == 2) { + if (clock_diff(MidiClock.clock_last_time, clock) >= MidiClock.div128_time) { + if (MidiClock.div192th_counter != MidiClock.div192th_counter_last) { MidiClock.increment192Counter(); MidiClock.div192th_counter_last = MidiClock.div192th_counter; if ((enable_clock_callbacks)) { From f5a0319efae0a0e0dacdd041d64122ffdad45e9a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 29 Oct 2019 20:34:09 +1100 Subject: [PATCH 181/469] Use uint8_t counter instead of uint16_t diff for midiclock interpolation --- avr/cores/megacommand/Midi/MidiClock.h | 4 ++-- avr/cores/megacommand/main.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index bc11b4e9c..9738d459d 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -50,7 +50,7 @@ class MidiClockClass { volatile uint8_t mod4_free_counter; volatile uint16_t div128_counter; volatile uint16_t div128_time; - + volatile uint8_t div128th_countdown; volatile uint16_t clock_last_time; volatile uint16_t last_diff_clock4; @@ -261,7 +261,7 @@ class MidiClockClass { // } clock_last_time = clock; uint8_t _mod6_counter = mod6_counter; - + div128th_countdown = 0; if (transmit_uart1) { // MidiUart.putc(0xF8); MidiUart.m_putc_immediate(0xF8); diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index 4dd773fd1..010f38844 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -195,11 +195,12 @@ ISR(TIMER1_COMPA_vect) { select_bank(0); clock++; - + MidiClock.div128th_countdown++; if (MidiClock.state == 2) { - if (clock_diff(MidiClock.clock_last_time, clock) >= MidiClock.div128_time) { + if (MidiClock.div128th_countdown >= MidiClock.div128_time) { if (MidiClock.div192th_counter != MidiClock.div192th_counter_last) { MidiClock.increment192Counter(); + MidiClock.div128th_countdown = 0; MidiClock.div192th_counter_last = MidiClock.div192th_counter; if ((enable_clock_callbacks)) { MidiClock.callCallbacks(); From d881efe193e8f4ce4fc5e55434c79789999dc91e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 29 Oct 2019 20:47:32 +1100 Subject: [PATCH 182/469] 4 pulses not stable enough, bumping to 8 --- avr/cores/megacommand/Midi/MidiClock.h | 29 +++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index 9738d459d..1fe19c644 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -1,4 +1,3 @@ - /* Copyright (c) 2009 - http://ruinwesen.com/ */ #ifndef MIDICLOCK_H__ @@ -47,15 +46,15 @@ class MidiClockClass { volatile uint8_t mod6_counter; volatile uint8_t mod3_counter; - volatile uint8_t mod4_free_counter; + volatile uint8_t mod8_free_counter; volatile uint16_t div128_counter; volatile uint16_t div128_time; volatile uint8_t div128th_countdown; volatile uint16_t clock_last_time; - volatile uint16_t last_diff_clock4; - volatile uint16_t diff_clock4; - volatile uint16_t last_clock4; + volatile uint16_t last_diff_clock8; + volatile uint16_t diff_clock8; + volatile uint16_t last_clock8; volatile uint8_t bar_counter; volatile uint8_t beat_counter; @@ -301,10 +300,10 @@ class MidiClockClass { } void calc_tempo() { - DEBUG_PRINTLN(diff_clock4); - if (last_diff_clock4 != diff_clock4) { - tempo = ((float)50000 / ((float)diff_clock4)); - last_diff_clock4 = diff_clock4; + DEBUG_PRINTLN(diff_clock8); + if (last_diff_clock8 != diff_clock8) { + tempo = ((float)100000 / ((float)diff_clock8)); + last_diff_clock8 = diff_clock8; } } @@ -314,12 +313,12 @@ class MidiClockClass { } ALWAYS_INLINE() void MidiClockClass::incrementCounters() { - mod4_free_counter++; - if (mod4_free_counter == 4) { - diff_clock4 = midi_clock_diff(last_clock4, clock); - last_clock4 = clock; - div128_time = diff_clock4 / 8; - mod4_free_counter = 0; + mod8_free_counter++; + if (mod8_free_counter == 8) { + diff_clock8 = midi_clock_diff(last_clock8, clock); + last_clock8 = clock; + div128_time = diff_clock8 / 16; + mod8_free_counter = 0; } if (state == STARTED) { div96th_counter++; From 25fed62b87954d4ed0eb1ad341a4886442c2e6c9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 29 Oct 2019 21:10:55 +1100 Subject: [PATCH 183/469] fix variable names --- avr/cores/megacommand/Midi/MidiClock.h | 10 +++++----- avr/cores/megacommand/main.cpp | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index 1fe19c644..02451c4bf 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -47,9 +47,9 @@ class MidiClockClass { volatile uint8_t mod3_counter; volatile uint8_t mod8_free_counter; - volatile uint16_t div128_counter; - volatile uint16_t div128_time; - volatile uint8_t div128th_countdown; + volatile uint16_t div196_counter; + volatile uint16_t div196_time; + volatile uint8_t div196th_countdown; volatile uint16_t clock_last_time; volatile uint16_t last_diff_clock8; @@ -260,7 +260,7 @@ class MidiClockClass { // } clock_last_time = clock; uint8_t _mod6_counter = mod6_counter; - div128th_countdown = 0; + div196th_countdown = 0; if (transmit_uart1) { // MidiUart.putc(0xF8); MidiUart.m_putc_immediate(0xF8); @@ -317,7 +317,7 @@ class MidiClockClass { if (mod8_free_counter == 8) { diff_clock8 = midi_clock_diff(last_clock8, clock); last_clock8 = clock; - div128_time = diff_clock8 / 16; + div196_time = diff_clock8 / 16; mod8_free_counter = 0; } if (state == STARTED) { diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index 010f38844..8426d8248 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -195,12 +195,12 @@ ISR(TIMER1_COMPA_vect) { select_bank(0); clock++; - MidiClock.div128th_countdown++; + MidiClock.div196th_countdown++; if (MidiClock.state == 2) { - if (MidiClock.div128th_countdown >= MidiClock.div128_time) { + if (MidiClock.div196th_countdown >= MidiClock.div196_time) { if (MidiClock.div192th_counter != MidiClock.div192th_counter_last) { MidiClock.increment192Counter(); - MidiClock.div128th_countdown = 0; + MidiClock.div196th_countdown = 0; MidiClock.div192th_counter_last = MidiClock.div192th_counter; if ((enable_clock_callbacks)) { MidiClock.callCallbacks(); From b186ecf99b14e4b965285647895efd272e90bd0c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 30 Oct 2019 21:23:56 +1100 Subject: [PATCH 184/469] add more lfo shapes, change the way depth is calculated. lfo wave length is now equal to LFO_LENGTH --- avr/cores/megacommand/MCL/LFO.cpp | 83 +++++++++++++++++---------- avr/cores/megacommand/MCL/LFO.h | 28 ++++++++- avr/cores/megacommand/MCL/LFOPage.cpp | 21 ++++++- avr/cores/megacommand/MCL/LFOPage.h | 5 +- 4 files changed, 100 insertions(+), 37 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFO.cpp b/avr/cores/megacommand/MCL/LFO.cpp index c407e937e..e33170990 100644 --- a/avr/cores/megacommand/MCL/LFO.cpp +++ b/avr/cores/megacommand/MCL/LFO.cpp @@ -1,56 +1,77 @@ /* Copyright Justin Mammarella jmamma@gmail.com 2018 */ -#include "Math.h" #include "LFO.h" +#include "Math.h" #define DIV_1_127 (1.00 / MAX) -//Exponential Rise Formua: -//y = M * (1-e^(-x/a)); -//M = Maximum -//a = time constant. -//0.63M = M * (1 - e^(-x/a)) -//0.63 = (1 - e^(-x/a)) -//e^(-x/a) = ( 1 - 0.63) -//e^(-x/a) = 0.37 +// Exponential Rise Formua: +// y = M * (1-e^(-x/a)); +// M = Maximum +// a = time constant. +// 0.63M = M * (1 - e^(-x/a)) +// 0.63 = (1 - e^(-x/a)) +// e^(-x/a) = ( 1 - 0.63) +// e^(-x/a) = 0.37 // -x/a = ln(0.37) [1] // -//at t = M, y = M - 1 +// at t = M, y = M - 1 // //(M - 1 ) = M * (1 - ^e(-M/a)) -//M - 1 = M - Me^(-M/a) -//e^(-M/a) = 1/M -//ln(1/M) = -M/a -//a = - M / (ln(1/M) [2] +// M - 1 = M - Me^(-M/a) +// e^(-M/a) = 1/M +// ln(1/M) = -M/a +// a = - M / (ln(1/M) [2] // -//For M = 127. a = 26 +// For M = 127. a = 26 // // #define MAX 127 uint8_t ExpLFO::get_sample(uint8_t sample_number) { - uint8_t y = MAX - (uint8_t) ((float) MAX * powf(M_E, (float) -1 * (float) sample_number * (float)time_constant)); - return y; + uint8_t y = (uint8_t)((float)amplitude * + powf(M_E, (float)-1 * (float)sample_number * + (float)time_constant)); + return y; +} + + +uint8_t IExpLFO::get_sample(uint8_t sample_number) { + uint8_t y = amplitude - (uint8_t)((float)amplitude * + powf(M_E, (float)-1 * (float)sample_number * + (float)time_constant)); + return y; +} + + +uint8_t RampLFO::get_sample(uint8_t sample_number) { + uint8_t y = ((float)amplitude / (float)(LFO_LENGTH)) * (sample_number); + + return y; +} + +uint8_t IRampLFO::get_sample(uint8_t sample_number) { + uint8_t y = amplitude - ((float)amplitude / (float)(LFO_LENGTH)) * (sample_number); + + return y; } + uint8_t TriLFO::get_sample(uint8_t sample_number) { - uint8_t y; - if (sample_number > LFO_LENGTH / 2) { - y = (127 - sample_number) * 2; - } - else { - y = (sample_number * 2); - } - return y; + uint8_t y; + if (sample_number > LFO_LENGTH / 2) { + y = amplitude - 1 * ( (float) amplitude / (float) (LFO_LENGTH / 2)) * (sample_number - (LFO_LENGTH / 2)); + } else { + y = ((float)amplitude / (float)(LFO_LENGTH / 2)) * (sample_number); + } + return y; } uint8_t SinLFO::get_sample(uint8_t sample_number) { float sample_duration = (float)1 / (float)LFO_LENGTH; // float sample_duration = (float) 1 / (float) freq; - return (float) (MAX / 2.0) * cos(2 * PI * 1 * sample_number * sample_duration) + (float) (MAX / 2.0); -} - - -uint8_t LFO::get_sample(uint8_t sample_number) { - return 0; + return (float)(amplitude / 2.0) * + cos(2 * PI * 1 * sample_number * sample_duration) + + (float)(amplitude / 2.0); } +uint8_t LFO::get_sample(uint8_t sample_number) { return 0; } diff --git a/avr/cores/megacommand/MCL/LFO.h b/avr/cores/megacommand/MCL/LFO.h index 77b44a5e9..6d8c3e2b8 100644 --- a/avr/cores/megacommand/MCL/LFO.h +++ b/avr/cores/megacommand/MCL/LFO.h @@ -7,12 +7,12 @@ #include "Math.h" #define EXP_LFO 1 -#define LFO_LENGTH 128 +#define LFO_LENGTH 48 class LFO { public: + uint8_t amplitude; virtual uint8_t get_sample(uint8_t sample_number); - }; class ExpLFO : public LFO { @@ -23,6 +23,30 @@ class ExpLFO : public LFO { uint8_t get_sample(uint8_t sample_number); }; + +class IExpLFO : public LFO { + + float time_constant; +public: + IExpLFO(float time_constant_ = 20) { time_constant = 1.00 / time_constant_; } + uint8_t get_sample(uint8_t sample_number); +}; + +class RampLFO : public LFO { + +public: + RampLFO() { } + uint8_t get_sample(uint8_t sample_number); +}; + +class IRampLFO : public LFO { + +public: + IRampLFO() { } + uint8_t get_sample(uint8_t sample_number); +}; + + class TriLFO : public LFO { public: diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index ddfaadb4c..17e64a829 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -48,7 +48,7 @@ void LFOPage::update_encoders() { } if (page_mode == LFO_SETTINGS) { encoders[0]->cur = waveform; - ((MCLEncoder *)encoders[0])->max = 2; + ((MCLEncoder *)encoders[0])->max = 5; encoders[1]->cur = lfo_track->speed; ((MCLEncoder *)encoders[1])->max = 127; @@ -143,9 +143,11 @@ void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, uint8_t param, uint8_t depth) { SinLFO sin_lfo; TriLFO tri_lfo; + RampLFO ramp_lfo; + IRampLFO iramp_lfo; ExpLFO exp_lfo; + IExpLFO iexp_lfo; LFO *lfo; - switch (waveform) { case SIN_WAV: lfo = (LFO *)&sin_lfo; @@ -155,15 +157,28 @@ void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, lfo = (LFO *)&tri_lfo; lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; break; + case IRAMP_WAV: + lfo = (LFO *)&iramp_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_MAX; + break; + case RAMP_WAV: + lfo = (LFO *)&ramp_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_MAX; + break; case EXP_WAV: lfo = (LFO *)&exp_lfo; lfo_track->offset_behaviour = LFO_OFFSET_MAX; break; + case IEXP_WAV: + lfo = (LFO *)&iexp_lfo; + lfo_track->offset_behaviour = LFO_OFFSET_MAX; + break; } + lfo->amplitude = depth; // ExpLFO exp_lfo(20); for (uint8_t n = 0; n < LFO_LENGTH; n++) { lfo_track->wav_table[param][n] = - (float)lfo->get_sample(n) * ((float)depth * (float)(DIV_1_127)); + (float)lfo->get_sample(n); } } void LFOPage::display() { diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h index 4dc58c5da..7b7d695f8 100644 --- a/avr/cores/megacommand/MCL/LFOPage.h +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -11,7 +11,10 @@ #define SIN_WAV 0 #define TRI_WAV 1 -#define EXP_WAV 2 +#define RAMP_WAV 2 +#define IRAMP_WAV 3 +#define IEXP_WAV 4 +#define EXP_WAV 5 // class LFOPage : public LightPage, MidiCallback { public: From 63bcf1fd22cf1292e4bc55e460f39c36b83268ea Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 00:47:30 +1100 Subject: [PATCH 185/469] Wavlength now 96 samples, 1 beat. fixed sin and improved exp fit --- avr/cores/megacommand/MCL/LFO.cpp | 11 +++++++---- avr/cores/megacommand/MCL/LFO.h | 6 +++--- avr/cores/megacommand/MCL/LFOPage.cpp | 3 +-- avr/cores/megacommand/MCL/LFOPage.h | 4 ++-- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 15 ++++++++++----- avr/cores/megacommand/MCL/LFOSeqTrack.h | 4 +++- 6 files changed, 26 insertions(+), 17 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFO.cpp b/avr/cores/megacommand/MCL/LFO.cpp index e33170990..c00279bca 100644 --- a/avr/cores/megacommand/MCL/LFO.cpp +++ b/avr/cores/megacommand/MCL/LFO.cpp @@ -67,11 +67,14 @@ uint8_t TriLFO::get_sample(uint8_t sample_number) { } uint8_t SinLFO::get_sample(uint8_t sample_number) { - float sample_duration = (float)1 / (float)LFO_LENGTH; - // float sample_duration = (float) 1 / (float) freq; - return (float)(amplitude / 2.0) * - cos(2 * PI * 1 * sample_number * sample_duration) + + float sample_duration = (float)1.0 / (float)LFO_LENGTH; + + uint8_t y = (float)(amplitude / 2.0) * + sin(2.0 * PI * (float) sample_number * sample_duration) + (float)(amplitude / 2.0); + return y; + } + uint8_t LFO::get_sample(uint8_t sample_number) { return 0; } diff --git a/avr/cores/megacommand/MCL/LFO.h b/avr/cores/megacommand/MCL/LFO.h index 6d8c3e2b8..5d7cc1c4a 100644 --- a/avr/cores/megacommand/MCL/LFO.h +++ b/avr/cores/megacommand/MCL/LFO.h @@ -7,7 +7,7 @@ #include "Math.h" #define EXP_LFO 1 -#define LFO_LENGTH 48 +#define LFO_LENGTH 96 class LFO { public: @@ -19,7 +19,7 @@ class ExpLFO : public LFO { float time_constant; public: - ExpLFO(float time_constant_ = 20) { time_constant = 1.00 / time_constant_; } + ExpLFO(float time_constant_ = 40) { time_constant = 1.00 / time_constant_; } uint8_t get_sample(uint8_t sample_number); }; @@ -28,7 +28,7 @@ class IExpLFO : public LFO { float time_constant; public: - IExpLFO(float time_constant_ = 20) { time_constant = 1.00 / time_constant_; } + IExpLFO(float time_constant_ = 40) { time_constant = 1.00 / time_constant_; } uint8_t get_sample(uint8_t sample_number); }; diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 17e64a829..a2038f0ec 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -240,8 +240,7 @@ void LFOPage::display() { for (uint8_t n = 0; n < LFO_LENGTH; n++) { oled_display.drawPixel(x + n, - (float)32 - lfo_track->wav_table[0][n] * - ((float)h * (float)DIV_1_127), + (float)32 - ((float) lfo_track->wav_table[0][n] / (float)lfo_track->params[0].depth) * 32, WHITE); if (n % 2 == 0) { oled_display.drawPixel(x + n, (h / 2) + y, WHITE); diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h index 7b7d695f8..89ee001e1 100644 --- a/avr/cores/megacommand/MCL/LFOPage.h +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -12,8 +12,8 @@ #define SIN_WAV 0 #define TRI_WAV 1 #define RAMP_WAV 2 -#define IRAMP_WAV 3 -#define IEXP_WAV 4 +#define IEXP_WAV 3 +#define IRAMP_WAV 4 #define EXP_WAV 5 // class LFOPage : public LightPage, MidiCallback { diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index 6241bef9a..08a0aea7d 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -57,12 +57,17 @@ void LFOSeqTrack::seq() { } } } - sample_hold += 1; - if (sample_hold >= speed) { - sample_hold = 0; - sample_count += 1; - } + if (speed == 0) { + sample_count += 2; + } + else { + sample_hold += 1; + if (sample_hold >= speed - 1) { + sample_hold = 0; + sample_count += 1; + } + } if (sample_count > LFO_LENGTH) { // Free running LFO should reset, oneshot should hold at last value. if (mode == LFO_MODE_ONE) { diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index 744dff058..23041adcf 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -19,6 +19,8 @@ // LFO maximum value will be equal to the offset. #define LFO_OFFSET_MAX 1 +#define WAV_LENGTH 96 + class LFOSeqParam { public: uint8_t dest; @@ -34,7 +36,7 @@ class LFOSeqParam { class LFOSeqTrack { public: - uint8_t wav_table[2][128]; + uint8_t wav_table[2][WAV_LENGTH]; uint8_t sample_count; uint8_t sample_hold = 0; From eb54ff2793e2e57aa364378bf7bccf899d64c0b8 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 01:00:15 +1100 Subject: [PATCH 186/469] shift sine by 90 degrees --- avr/cores/megacommand/MCL/LFO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/LFO.cpp b/avr/cores/megacommand/MCL/LFO.cpp index c00279bca..fdaa68d77 100644 --- a/avr/cores/megacommand/MCL/LFO.cpp +++ b/avr/cores/megacommand/MCL/LFO.cpp @@ -70,7 +70,7 @@ uint8_t SinLFO::get_sample(uint8_t sample_number) { float sample_duration = (float)1.0 / (float)LFO_LENGTH; uint8_t y = (float)(amplitude / 2.0) * - sin(2.0 * PI * (float) sample_number * sample_duration) + + sin(2.0 * PI * (float) sample_number * sample_duration - (0.5 * PI)) + (float)(amplitude / 2.0); return y; From ccfed8216fd3d602cc3890afc6b40d03c17e0e83 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 15:56:28 +1100 Subject: [PATCH 187/469] Fix regression, current step would not be displayed when clock stopped --- avr/cores/megacommand/MCL/SeqPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 91058594a..4b4e894c4 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -466,7 +466,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, } else if (!in_range) { // don't draw } else { - if (IS_BIT_SET64(pattern_mask, i + offset) && (i + offset != active_track.step_count)) { + if (IS_BIT_SET64(pattern_mask, i + offset) && ((i + offset != active_track.step_count) || (MidiClock.state != 2))) { /*If the bit is set, there is a trigger at this position. */ oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); } else { From af797f7ce8e4479b4fdc5efc230a431d2861e028 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 15:45:12 +1100 Subject: [PATCH 188/469] Improve LFO drawing, make LFOPage a SeqPage --- avr/cores/megacommand/MCL/LFOPage.cpp | 97 +++++++++------------------ avr/cores/megacommand/MCL/LFOPage.h | 7 +- avr/cores/megacommand/MCL/SeqPage.cpp | 24 +++++-- avr/cores/megacommand/MCL/SeqPage.h | 1 + 4 files changed, 54 insertions(+), 75 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index a2038f0ec..fe7945cc4 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -12,8 +12,9 @@ #define LFO_SETTINGS 1 void LFOPage::setup() { -// lfo_track = &mcl_seq.lfo_tracks[0]; - DEBUG_PRINT_FN(); } + // lfo_track = &mcl_seq.lfo_tracks[0]; + DEBUG_PRINT_FN(); +} void LFOPage::init() { DEBUG_PRINT_FN(); @@ -82,9 +83,8 @@ void LFOPage::loop() { SET_LOCK(); lfo_track->params[0].reset_param_offset(); lfo_track->params[0].param = encoders[1]->cur; - lfo_track->params[0].offset = - lfo_track->params[0].get_param_offset(encoders[0]->cur, - encoders[1]->cur); + lfo_track->params[0].offset = lfo_track->params[0].get_param_offset( + encoders[0]->cur, encoders[1]->cur); lfo_track->params[0].update_offset(); CLEAR_LOCK(); } @@ -107,9 +107,8 @@ void LFOPage::loop() { SET_LOCK(); lfo_track->params[1].reset_param_offset(); lfo_track->params[1].param = encoders[3]->cur; - lfo_track->params[1].offset = - lfo_track->params[1].get_param_offset(encoders[2]->cur, - encoders[3]->cur); + lfo_track->params[1].offset = lfo_track->params[1].get_param_offset( + encoders[2]->cur, encoders[3]->cur); lfo_track->params[1].update_offset(); CLEAR_LOCK(); } @@ -117,10 +116,8 @@ void LFOPage::loop() { if (page_mode == LFO_SETTINGS) { if (encoders[0]->hasChanged()) { waveform = encoders[0]->cur; - load_wavetable(waveform, lfo_track, 0, - lfo_track->params[0].depth); - load_wavetable(waveform, lfo_track, 1, - lfo_track->params[1].depth); + load_wavetable(waveform, lfo_track, 0, lfo_track->params[0].depth); + load_wavetable(waveform, lfo_track, 1, lfo_track->params[1].depth); } if (encoders[1]->hasChanged()) { @@ -177,12 +174,11 @@ void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, lfo->amplitude = depth; // ExpLFO exp_lfo(20); for (uint8_t n = 0; n < LFO_LENGTH; n++) { - lfo_track->wav_table[param][n] = - (float)lfo->get_sample(n); + lfo_track->wav_table[param][n] = (float)lfo->get_sample(n); } } void LFOPage::display() { - + auto oldfont = oled_display.getFont(); if (!classic_display) { #ifdef OLED_DISPLAY oled_display.clearDisplay(); @@ -206,7 +202,7 @@ void LFOPage::display() { #endif #ifdef OLED_DISPLAY - oled_display.setFont(); + oled_display.setFont(&TomThumb); oled_display.setCursor(0, 0); oled_display.print("LFO "); @@ -235,15 +231,21 @@ void LFOPage::display() { */ uint8_t x = 0; - uint8_t h = 30; - uint8_t y = 0; - - for (uint8_t n = 0; n < LFO_LENGTH; n++) { - oled_display.drawPixel(x + n, - (float)32 - ((float) lfo_track->wav_table[0][n] / (float)lfo_track->params[0].depth) * 32, - WHITE); - if (n % 2 == 0) { - oled_display.drawPixel(x + n, (h / 2) + y, WHITE); + uint8_t y = 16; + uint8_t lfo_height = 16; + uint8_t width = 32; + LFOSeqTrack temp_track; + uint8_t inc = LFO_LENGTH / width; + load_wavetable(waveform, &temp_track, 0, lfo_height); + + // mcl_gui.draw_vertical_dashline(x, 0, knob_y); + SeqPage::draw_knob_frame(); + for (uint8_t n = 0; n < LFO_LENGTH; n += inc, x++) { + oled_display.drawPixel( + x, y + lfo_height - temp_track.wav_table[0][n], + WHITE); + if (x % 2 == 0) { + oled_display.drawPixel(x, (lfo_height / 2) + y, WHITE); } } uint8_t i = 0; @@ -260,62 +262,27 @@ void LFOPage::display() { mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP1"); mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP2"); } + // draw_pattern_mask(); - // draw_pattern_mask(); - - oled_display.setCursor(0, 20); + oled_display.setCursor(0, 10); switch (lfo_track->mode) { case LFO_MODE_FREE: oled_display.print("FREE"); break; case LFO_MODE_TRIG: - draw_pattern_mask(); + draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); oled_display.print("TRIG"); break; case LFO_MODE_ONE: - draw_pattern_mask(); + draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); oled_display.print("ONE"); break; } oled_display.display(); - + oled_display.setFont(oldfont); #endif } - -void LFOPage::draw_pattern_mask() { - - uint8_t trig_x = 32; - uint8_t trig_y = 20; - uint8_t seq_w = 5; - uint8_t trig_h = 5; - - uint64_t pattern_mask = lfo_track->pattern_mask; - //uint64_t pattern_mask = mcl_seq.lfo_tracks[0].pattern_mask; - - uint8_t offset = 0; - for (int i = 0; i < 16; i++) { - - uint8_t idx = i + offset; - bool in_range = idx < lfo_track->length; - - if (note_interface.notes[i] == 1) { - // TI feedback - oled_display.fillRect(trig_x - 1, trig_y, seq_w + 2, trig_h + 1, WHITE); - } else if (!in_range) { - // don't draw - } else { - if (IS_BIT_SET64(pattern_mask, i + offset)) { - /*If the bit is set, there is a trigger at this position. */ - oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); - } else { - oled_display.drawRect(trig_x, trig_y, seq_w, trig_h, WHITE); - } - } - - trig_x += seq_w + 1; - } -} void LFOPage::onControlChangeCallback_Midi(uint8_t *msg) { uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); uint8_t param = msg[1]; diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h index 89ee001e1..02c93dcfb 100644 --- a/avr/cores/megacommand/MCL/LFOPage.h +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -6,6 +6,7 @@ #include "GUI.h" #include "MCLEncoder.h" #include "LFOSeqTrack.h" +#include "SeqPage.h" #define NUM_LFO_PAGES 2 @@ -16,11 +17,11 @@ #define IRAMP_WAV 4 #define EXP_WAV 5 // -class LFOPage : public LightPage, MidiCallback { +class LFOPage : public SeqPage, MidiCallback { public: LFOPage(LFOSeqTrack *lfo_track_, Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4) { + : SeqPage(e1, e2, e3, e4) { lfo_track = lfo_track_; } @@ -38,7 +39,7 @@ class LFOPage : public LightPage, MidiCallback { void display(); void setup(); - void draw_pattern_mask(); +// void draw_pattern_mask(); void init(); void loop(); void cleanup(); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 91058594a..6731662c0 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -446,19 +446,15 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { } } -void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, - bool show_current_step) { +void SeqPage::draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step) { uint8_t trig_x = seq_x0; - if (device == DEVICE_MD) { - auto &active_track = mcl_seq.md_tracks[last_md_track]; - uint64_t pattern_mask = active_track.pattern_mask; for (int i = 0; i < 16; i++) { uint8_t idx = i + offset; - bool in_range = idx < active_track.length; + bool in_range = idx < length; if (note_interface.notes[i] == 1) { // TI feedback @@ -466,7 +462,8 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, } else if (!in_range) { // don't draw } else { - if (IS_BIT_SET64(pattern_mask, i + offset) && (i + offset != active_track.step_count)) { + + if (IS_BIT_SET64(pattern_mask, i + offset) && ((i + offset != step_count) || (MidiClock.state != 2))) { /*If the bit is set, there is a trigger at this position. */ oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); } else { @@ -476,6 +473,19 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, trig_x += seq_w + 1; } + +} + +void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, + bool show_current_step) { + + uint8_t trig_x = seq_x0; + + if (device == DEVICE_MD) { + auto &active_track = mcl_seq.md_tracks[last_md_track]; + uint64_t pattern_mask = active_track.pattern_mask; + + draw_pattern_mask(offset, active_track.pattern_mask, active_track.step_count, active_track.length, show_current_step); } #ifdef EXT_TRACKS else { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 2a959e9e8..ee1328333 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -40,6 +40,7 @@ class SeqPage : public LightPage { } void create_chars_seq(); void draw_lock_mask(uint8_t offset, bool show_current_step = true); + void draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step = true); void draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step = true); void draw_knob_frame(); void draw_knob(uint8_t i, const char* title, const char* text); From d91aee959cfafd45e99230e19b8d7915b48383a1 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 19:27:45 +1100 Subject: [PATCH 189/469] standardise LFO GUI to new format, fixed waveform rendering, lots of other fixes --- avr/cores/megacommand/MCL/LFOPage.cpp | 183 +++++++++++++++++------- avr/cores/megacommand/MCL/LFOPage.h | 3 +- avr/cores/megacommand/MCL/LFOSeqTrack.h | 1 + avr/cores/megacommand/MCL/MCLGUI.cpp | 1 - avr/cores/megacommand/MCL/MCLGUI.h | 2 + avr/cores/megacommand/MCL/MCLSeq.cpp | 4 + avr/cores/megacommand/MCL/SeqPage.cpp | 84 ++++++----- avr/cores/megacommand/MCL/SeqPage.h | 2 + 8 files changed, 191 insertions(+), 89 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index fe7945cc4..c9b56c75f 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -1,7 +1,9 @@ #include "LFO.h" #include "LFOPage.h" #include "MCL.h" +#include "MCLGUI.h" #include "MCLSeq.h" +#include "MD.h" #define LFO_TYPE 0 #define LFO_PARAM 1 @@ -60,6 +62,10 @@ void LFOPage::update_encoders() { encoders[3]->cur = lfo_track->params[1].depth; ((MCLEncoder *)encoders[3])->max = 127; } + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { + ((LightPage *)this)->encoders_used_clock[i] = + slowclock - SHOW_VALUE_TIMEOUT - 1; + } } void LFOPage::loop() { @@ -177,6 +183,51 @@ void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, lfo_track->wav_table[param][n] = (float)lfo->get_sample(n); } } + +void LFOPage::draw_param(uint8_t knob, uint8_t dest, uint8_t param) { + + char myName[4] = "-- "; + + PGM_P modelname = NULL; + if (dest != 0) { + if (dest < 17) { + modelname = model_param_name(MD.kit.models[dest - 1], param); + } else { + modelname = fx_param_name(MD_FX_ECHO + dest - 17, param); + } + if (modelname != NULL) { + m_strncpy_p(myName, modelname, 4); + } + } + draw_knob(knob, "PAR", myName); +} + +void LFOPage::draw_dest(uint8_t knob, uint8_t value) { + char K[4]; + switch (value) { + case 0: + strcpy(K, "--"); + break; + case 17: + strcpy(K, "DEL"); + break; + case 18: + strcpy(K, "REV"); + break; + case 19: + strcpy(K, "EQ"); + break; + case 20: + strcpy(K, "DYN"); + break; + default: + // K[0] = 'T'; + itoa(value, K, 10); + break; + } + draw_knob(knob, "DST", K); +} + void LFOPage::display() { auto oldfont = oled_display.getFont(); if (!classic_display) { @@ -202,80 +253,112 @@ void LFOPage::display() { #endif #ifdef OLED_DISPLAY + oled_display.setTextColor(WHITE); + oled_display.setFont(&Elektrothic); + oled_display.setCursor(trackid_x, trackid_y); + uint8_t lfo_track_num = lfo_track->track_number; + if (lfo_track_num < 10) { + oled_display.print('0'); + } + oled_display.print(lfo_track_num); oled_display.setFont(&TomThumb); - oled_display.setCursor(0, 0); - - oled_display.print("LFO "); - if (lfo_track->enable) { + // draw MD/EXT label + + oled_display.setCursor(label_x + 1, label_md_y + 6); + oled_display.print("LFO"); + if ((lfo_track->enable)) { + oled_display.fillRect(label_x, label_md_y + 7, label_w, label_h, WHITE); + oled_display.setCursor(label_x + 1, label_md_y + 13); + oled_display.setTextColor(BLACK); oled_display.print("ON"); + oled_display.setTextColor(WHITE); } else { + oled_display.setCursor(label_x + 1, label_md_y + 13); oled_display.print("OFF"); } - // oled_display.print(page_mode ? 1 : 0); - oled_display.print(" "); - - /* PGM_P param_name = NULL; - char str[4]; - for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { - uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); - - uint8_t fx_param = params[n].param; - uint8_t fx_type = params[n].type; - param_name = fx_param_name(fx_type, fx_param); - m_strncpy_p(str, param_name, 4); + draw_page_index(); - mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); - - // mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); - } - */ + oled_display.setCursor(0, 0); - uint8_t x = 0; - uint8_t y = 16; - uint8_t lfo_height = 16; - uint8_t width = 32; + uint8_t x = knob_x0 + 4; + uint8_t y = 8; + uint8_t lfo_height = 7; + uint8_t width = 13; LFOSeqTrack temp_track; - uint8_t inc = LFO_LENGTH / width; - load_wavetable(waveform, &temp_track, 0, lfo_height); - + // mcl_gui.draw_vertical_dashline(x, 0, knob_y); SeqPage::draw_knob_frame(); - for (uint8_t n = 0; n < LFO_LENGTH; n += inc, x++) { - oled_display.drawPixel( - x, y + lfo_height - temp_track.wav_table[0][n], - WHITE); - if (x % 2 == 0) { - oled_display.drawPixel(x, (lfo_height / 2) + y, WHITE); - } - } - uint8_t i = 0; - if (page_mode == LFO_DESTINATION) { + char K[4]; - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DST"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "PAR"); + if (page_mode == LFO_DESTINATION) { + draw_dest(0, encoders[0]->cur); + draw_param(1, encoders[0]->cur, encoders[1]->cur); + draw_dest(2, encoders[2]->cur); + draw_param(3, encoders[2]->cur, encoders[3]->cur); } if (page_mode == LFO_SETTINGS) { - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SHP"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "SPD"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP1"); - mcl_gui.draw_light_encoder(30 + 20 * i, 5, encoders[i++], "DEP2"); + /* + char K[4]; + switch (encoders[0]->cur) { + case SIN_WAV: + strcpy(K, "SIN"); + break; + case TRI_WAV: + strcpy(K, "TRI"); + break; + case IRAMP_WAV: + strcpy(K, "SAW"); + break; + case RAMP_WAV: + strcpy(K, "RMP"); + break; + case EXP_WAV: + strcpy(K, "DEC"); + break; + case IEXP_WAV: + strcpy(K, "EXP"); + break; + } + strcpy(K, ""); + */ + load_wavetable(waveform, &temp_track, 0, lfo_height); + uint8_t inc = LFO_LENGTH / width; + for (uint8_t n = 0; n < LFO_LENGTH; n += inc, x++) { + if (n < LFO_LENGTH) { + oled_display.drawPixel(x, y + lfo_height - temp_track.wav_table[0][n], + WHITE); + } + } + + x = knob_x0 + 2; + oled_display.setCursor(x + 4, 7); + oled_display.print("WAV"); + + draw_knob(1, encoders[1], "SPD"); + draw_knob(2, encoders[2], "DEP1"); + draw_knob(3, encoders[3], "DEP2"); } // draw_pattern_mask(); + oled_display.setFont(&TomThumb); + // oled_display.setCursor(1, info1_y + 6); + // oled_display.print("MODE: "); - oled_display.setCursor(0, 10); + oled_display.setCursor(1, info2_y + 6); switch (lfo_track->mode) { case LFO_MODE_FREE: oled_display.print("FREE"); break; case LFO_MODE_TRIG: - draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); + draw_lock_mask(0, 0, lfo_track->step_count, lfo_track->length, true); + draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, + lfo_track->length, true); oled_display.print("TRIG"); break; case LFO_MODE_ONE: - draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); - oled_display.print("ONE"); + draw_lock_mask(0, 0, lfo_track->step_count, lfo_track->length, true); + draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, + lfo_track->length, true); + oled_display.print("TRIG ONE"); break; } diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h index 02c93dcfb..a497ff939 100644 --- a/avr/cores/megacommand/MCL/LFOPage.h +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -30,13 +30,14 @@ class LFOPage : public SeqPage, MidiCallback { bool page_mode; uint8_t page_id; - LFOSeqTrack *lfo_track; uint8_t waveform; uint8_t depth; uint8_t depth2; + void draw_dest(uint8_t knob, uint8_t value); + void draw_param(uint8_t knob, uint8_t dest, uint8_t param); void display(); void setup(); // void draw_pattern_mask(); diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index 23041adcf..bb2238cfa 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -36,6 +36,7 @@ class LFOSeqParam { class LFOSeqTrack { public: + uint8_t track_number; uint8_t wav_table[2][WAV_LENGTH]; uint8_t sample_count; uint8_t sample_hold = 0; diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 9d05d2068..e3c934089 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -1,5 +1,4 @@ #include "MCL.h" -#define SHOW_VALUE_TIMEOUT 2000 bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { text_input_page.init(); diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 4c5a5df3d..aa8ba70b3 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -6,6 +6,8 @@ #include "TextInputPage.h" #include "QuestionDialogPage.h" +#define SHOW_VALUE_TIMEOUT 2000 + class MCLGUI { public: // fills dst buffer with input text. ensures that: diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index a42e47d58..f7afec870 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -24,6 +24,10 @@ void MCLSeq::setup() { md_tracks[i].set_length(16); md_tracks[i].resolution = 1; } + + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + lfo_tracks[i].track_number = i; + } #ifdef EXT_TRACKS for (uint8_t i = 0; i < num_ext_tracks; i++) { ext_tracks[i].channel = i; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 6731662c0..c5828970c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -415,19 +415,19 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, GUI.put_string_at(0, mystr); } #else -void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { - auto &active_track = mcl_seq.md_tracks[last_md_track]; - uint8_t step_count = active_track.step_count; + + +void SeqPage::draw_lock_mask(uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step) { uint8_t led_x = seq_x0; for (int i = 0; i < 16; i++) { uint8_t idx = i + offset; - bool in_range = idx < active_track.length; + bool in_range = idx < length; bool current = show_current_step && step_count == idx && MidiClock.state == 2; - bool locked = in_range && IS_BIT_SET64(active_track.lock_mask, i + offset); + bool locked = in_range && IS_BIT_SET64(lock_mask, i + offset); if (note_interface.notes[i] == 1) { // TI feedback @@ -444,6 +444,12 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { led_x += seq_w + 1; } + +} + +void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { + auto &active_track = mcl_seq.md_tracks[last_md_track]; + draw_lock_mask(offset, active_track.lock_mask, active_track.step_count, active_track.length, show_current_step); } void SeqPage::draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step) { @@ -613,6 +619,41 @@ void SeqPage::loop() { } } +void SeqPage::draw_page_index() { + // draw page index + uint8_t pidx_x = pidx_x0; + bool blink = MidiClock.getBlinkHint(true); + // XXX should retrieve true track length + uint8_t playing_idx = (MidiClock.bar_counter - 1) % page_count; + uint8_t w = pidx_w; + if (page_count == 8) { + w /= 2; + pidx_x -= 1; + } + + for (uint8_t i = 0; i < page_count; ++i) { + oled_display.drawRect(pidx_x, pidx_y, w, pidx_h, WHITE); + + // highlight page_select + if (page_select == i) { + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); + } + + // blink playing_idx + if (playing_idx == i && blink) { + if (page_select == i) { + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, BLACK); + } else { + oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); + } + } + + pidx_x += w + 1; + } + + +} + #ifndef OLED_DISPLAY void SeqPage::display() { for (uint8_t i = 0; i < 2; i++) { @@ -690,38 +731,7 @@ void SeqPage::display() { } else { oled_display.fillRect(tri_x, tri_y, 4, 5, WHITE); } - - // draw page index - uint8_t pidx_x = pidx_x0; - bool blink = MidiClock.getBlinkHint(true); - // XXX should retrieve true track length - uint8_t playing_idx = (MidiClock.bar_counter - 1) % page_count; - uint8_t w = pidx_w; - if (page_count == 8) { - w /= 2; - pidx_x -= 1; - } - - for (uint8_t i = 0; i < page_count; ++i) { - oled_display.drawRect(pidx_x, pidx_y, w, pidx_h, WHITE); - - // highlight page_select - if (page_select == i) { - oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); - } - - // blink playing_idx - if (playing_idx == i && blink) { - if (page_select == i) { - oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, BLACK); - } else { - oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); - } - } - - pidx_x += w + 1; - } - + draw_page_index(); // draw info lines oled_display.fillRect(0, info1_y, pane_w, info_h, WHITE); oled_display.setTextColor(BLACK); diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index ee1328333..0d1620258 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -39,12 +39,14 @@ class SeqPage : public LightPage { : LightPage(e1, e2, e3, e4) { } void create_chars_seq(); + void draw_lock_mask(uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step = true); void draw_lock_mask(uint8_t offset, bool show_current_step = true); void draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step = true); void draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step = true); void draw_knob_frame(); void draw_knob(uint8_t i, const char* title, const char* text); void draw_knob(uint8_t i, Encoder* enc, const char* name); + void draw_page_index(); void select_track(uint8_t device, uint8_t track); virtual bool handleEvent(gui_event_t *event); From 678971d87add05c5243b599c810183ebbc616035 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 21:49:30 +1100 Subject: [PATCH 190/469] Initialise LFO settings, fix encoder val display, improve GUI --- avr/cores/megacommand/MCL/LFOPage.cpp | 14 +++++--- avr/cores/megacommand/MCL/LFOSeqTrack.h | 2 +- avr/cores/megacommand/MCL/MCLSeq.cpp | 43 ++++++++++++------------- avr/cores/megacommand/MCL/SeqPage.cpp | 6 ++-- avr/cores/megacommand/MCL/SeqPage.h | 2 +- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index c9b56c75f..d282e2c3f 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -62,10 +62,14 @@ void LFOPage::update_encoders() { encoders[3]->cur = lfo_track->params[1].depth; ((MCLEncoder *)encoders[3])->max = 127; } + loop(); + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { - ((LightPage *)this)->encoders_used_clock[i] = + encoders[i]->old = encoders[i]->cur; + ((LightPage *)this)->encoders_used_clock[i] = slowclock - SHOW_VALUE_TIMEOUT - 1; } + } void LFOPage::loop() { @@ -209,7 +213,7 @@ void LFOPage::draw_dest(uint8_t knob, uint8_t value) { strcpy(K, "--"); break; case 17: - strcpy(K, "DEL"); + strcpy(K, "ECH"); break; case 18: strcpy(K, "REV"); @@ -225,7 +229,7 @@ void LFOPage::draw_dest(uint8_t knob, uint8_t value) { itoa(value, K, 10); break; } - draw_knob(knob, "DST", K); + draw_knob(knob, "DEST", K); } void LFOPage::display() { @@ -276,11 +280,11 @@ void LFOPage::display() { oled_display.setCursor(label_x + 1, label_md_y + 13); oled_display.print("OFF"); } - draw_page_index(); + draw_page_index(false); oled_display.setCursor(0, 0); - uint8_t x = knob_x0 + 4; + uint8_t x = knob_x0 + 5; uint8_t y = 8; uint8_t lfo_height = 7; uint8_t width = 13; diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index bb2238cfa..007cbc4c5 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -49,7 +49,7 @@ class LFOSeqTrack { uint8_t step_count; uint64_t pattern_mask; - bool enable = true; + bool enable = false; LFOSeqParam params[NUM_LFO_PARAMS]; LFOSeqTrack() { init(); }; diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index f7afec870..1bce140a9 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -1,6 +1,6 @@ +#include "LFO.h" #include "MCL.h" #include "MCLSeq.h" -#include "LFO.h" void MCLSeq::setup() { @@ -26,7 +26,13 @@ void MCLSeq::setup() { } for (uint8_t i = 0; i < num_lfo_tracks; i++) { - lfo_tracks[i].track_number = i; + lfo_tracks[i].track_number = i; + if (i == 0) { + lfo_tracks[i].params[0].dest = 17; + lfo_tracks[i].params[1].dest = 18; + lfo_tracks[i].params[0].param = 8; + lfo_tracks[i].params[1].param = 8; + } } #ifdef EXT_TRACKS for (uint8_t i = 0; i < num_ext_tracks; i++) { @@ -50,8 +56,6 @@ void MCLSeq::setup() { MidiClock.addOnMidiContinueCallback( this, (midi_clock_callback_ptr_t)&MCLSeq::onMidiContinueCallback); midi_events.setup_callbacks(); - - }; void MCLSeq::enable() { @@ -81,25 +85,23 @@ void MCLSeq::onMidiContinueCallback() { void MCLSeq::onMidiStartImmediateCallback() { #ifdef EXT_TRACKS for (uint8_t i = 0; i < num_ext_tracks; i++) { - //ext_tracks[i].start_clock32th = 0; + // ext_tracks[i].start_clock32th = 0; ext_tracks[i].step_count = 0; ext_tracks[i].iterations = 1; } #endif - for (uint8_t i = 0; i < num_md_tracks; i++) { + for (uint8_t i = 0; i < num_md_tracks; i++) { - // md_tracks[i].start_clock32th = 0; + // md_tracks[i].start_clock32th = 0; md_tracks[i].step_count = 0; md_tracks[i].iterations = 1; md_tracks[i].oneshot_mask = 0; - } - - for (uint8_t i = 0; i < num_lfo_tracks; i++) { - - lfo_tracks[i].step_count = 0; - } - + } + for (uint8_t i = 0; i < num_lfo_tracks; i++) { + lfo_tracks[i].sample_hold = 0; + lfo_tracks[i].step_count = 0; + } } void MCLSeq::onMidiStartCallback() { @@ -110,7 +112,6 @@ void MCLSeq::onMidiStartCallback() { for (uint8_t i = 0; i < num_lfo_tracks; i++) { lfo_tracks[i].update_params_offset(); } - } void MCLSeq::onMidiStopCallback() { @@ -125,16 +126,14 @@ void MCLSeq::onMidiStopCallback() { for (uint8_t i = 0; i < num_lfo_tracks; i++) { lfo_tracks[i].reset_params_offset(); } - } #ifdef MEGACOMMAND #pragma GCC push_options -#pragma GCC optimize ("unroll-loops") +#pragma GCC optimize("unroll-loops") #endif void MCLSeq::seq() { - for (uint8_t i = 0; i < num_md_tracks; i++) { md_tracks[i].seq(); } @@ -143,13 +142,11 @@ void MCLSeq::seq() { lfo_tracks[i].seq(); } - #ifdef EXT_TRACKS for (uint8_t i = 0; i < num_ext_tracks; i++) { ext_tracks[i].seq(); } #endif - } #ifdef MEGACOMMAND #pragma GCC pop_options @@ -169,7 +166,7 @@ void MCLSeqMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { MD.parseCC(channel, param, &track, &track_param); mcl_seq.md_tracks[track].update_param(track_param, value); for (uint8_t n = 0; n < mcl_seq.num_lfo_tracks; n++) { - mcl_seq.lfo_tracks[n].check_and_update_params_offset(track_param, value); + mcl_seq.lfo_tracks[n].check_and_update_params_offset(track_param, value); } } } @@ -200,11 +197,11 @@ void MCLSeqMidiEvents::setup_callbacks() { Midi.addOnControlChangeCallback( this, (midi_callback_ptr_t)&MCLSeqMidiEvents::onControlChangeCallback_Midi); - #ifdef EXT_TRACKS +#ifdef EXT_TRACKS Midi2.addOnControlChangeCallback( this, (midi_callback_ptr_t)&MCLSeqMidiEvents::onControlChangeCallback_Midi2); - #endif +#endif state = true; } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index c5828970c..84a7dcbee 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -619,7 +619,7 @@ void SeqPage::loop() { } } -void SeqPage::draw_page_index() { +void SeqPage::draw_page_index(bool show_page_index) { // draw page index uint8_t pidx_x = pidx_x0; bool blink = MidiClock.getBlinkHint(true); @@ -635,13 +635,13 @@ void SeqPage::draw_page_index() { oled_display.drawRect(pidx_x, pidx_y, w, pidx_h, WHITE); // highlight page_select - if (page_select == i) { + if ((page_select == i) && (show_page_index)) { oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); } // blink playing_idx if (playing_idx == i && blink) { - if (page_select == i) { + if ((page_select == i) && (show_page_index)) { oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, BLACK); } else { oled_display.drawFastHLine(pidx_x + 1, pidx_y + 1, w - 2, WHITE); diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 0d1620258..67ebdd4c4 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -46,7 +46,7 @@ class SeqPage : public LightPage { void draw_knob_frame(); void draw_knob(uint8_t i, const char* title, const char* text); void draw_knob(uint8_t i, Encoder* enc, const char* name); - void draw_page_index(); + void draw_page_index(bool show_page_index = true); void select_track(uint8_t device, uint8_t track); virtual bool handleEvent(gui_event_t *event); From e06da7736098b34e4e47ce3aa6ef15b58185734c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 22:20:57 +1100 Subject: [PATCH 191/469] Adjust graphics to match SeqPage layout --- avr/cores/megacommand/MCL/LFOPage.cpp | 42 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index d282e2c3f..62274990a 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -63,13 +63,12 @@ void LFOPage::update_encoders() { ((MCLEncoder *)encoders[3])->max = 127; } loop(); - + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { encoders[i]->old = encoders[i]->cur; - ((LightPage *)this)->encoders_used_clock[i] = + ((LightPage *)this)->encoders_used_clock[i] = slowclock - SHOW_VALUE_TIMEOUT - 1; } - } void LFOPage::loop() { @@ -268,17 +267,23 @@ void LFOPage::display() { oled_display.setFont(&TomThumb); // draw MD/EXT label - oled_display.setCursor(label_x + 1, label_md_y + 6); - oled_display.print("LFO"); if ((lfo_track->enable)) { - oled_display.fillRect(label_x, label_md_y + 7, label_w, label_h, WHITE); oled_display.setCursor(label_x + 1, label_md_y + 13); + oled_display.print("OFF"); + oled_display.fillRect(label_x, label_md_y, label_w, label_h, WHITE); + oled_display.setCursor(label_x + 1, label_md_y + 6); oled_display.setTextColor(BLACK); oled_display.print("ON"); oled_display.setTextColor(WHITE); } else { + + oled_display.setCursor(label_x + 1, label_md_y + 6); + oled_display.print("ON"); + oled_display.fillRect(label_x, label_md_y + 7, label_w, label_h, WHITE); oled_display.setCursor(label_x + 1, label_md_y + 13); + oled_display.setTextColor(BLACK); oled_display.print("OFF"); + oled_display.setTextColor(WHITE); } draw_page_index(false); @@ -324,16 +329,16 @@ void LFOPage::display() { break; } strcpy(K, ""); - */ + */ load_wavetable(waveform, &temp_track, 0, lfo_height); uint8_t inc = LFO_LENGTH / width; - for (uint8_t n = 0; n < LFO_LENGTH; n += inc, x++) { + for (uint8_t n = 0; n < LFO_LENGTH; n += inc, x++) { if (n < LFO_LENGTH) { - oled_display.drawPixel(x, y + lfo_height - temp_track.wav_table[0][n], - WHITE); + oled_display.drawPixel(x, y + lfo_height - temp_track.wav_table[0][n], + WHITE); } } - + x = knob_x0 + 2; oled_display.setCursor(x + 4, 7); oled_display.print("WAV"); @@ -348,6 +353,17 @@ void LFOPage::display() { // oled_display.print("MODE: "); oled_display.setCursor(1, info2_y + 6); + + if (page_mode) { + oled_display.print("LFO A"); + } else { + oled_display.print("LFO B"); + } + + oled_display.fillRect(0, info1_y, pane_w, info_h, WHITE); + oled_display.setTextColor(BLACK); + oled_display.setCursor(1, info1_y + 6); + switch (lfo_track->mode) { case LFO_MODE_FREE: oled_display.print("FREE"); @@ -362,10 +378,10 @@ void LFOPage::display() { draw_lock_mask(0, 0, lfo_track->step_count, lfo_track->length, true); draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); - oled_display.print("TRIG ONE"); + oled_display.print("ONE"); break; } - + oled_display.setTextColor(WHITE); oled_display.display(); oled_display.setFont(oldfont); #endif From 0aef28b765ab3ad9c369036c7f2b27ad1dbb0a68 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 31 Oct 2019 23:00:20 +1100 Subject: [PATCH 192/469] Fix bad commit on md+mc sprites? --- art/sprites/trimetric/mc.png | Bin 268 -> 247 bytes art/sprites/trimetric/md.png | Bin 612 -> 610 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/art/sprites/trimetric/mc.png b/art/sprites/trimetric/mc.png index c88514386d0ac150169cf1bf2447ae1678e31dab..4214512c757eeaee9f7568f0a79d390f71191030 100644 GIT binary patch literal 247 zcmVPx#u}MThR5%fpl2I1IAPfY>=l!o-8Ttm-6jAwT60$p?wC(8Y_GPu!hT@a-vGZ3m z^$K)>Xat@H0mdBu1`$xr38Ys+1F|zaQDcqtxv>s^-K{{)#;VB^-4*T30;)#PfOtfJ z8i?jAGg|itM52vf%sF^|9YF#By4-WJwf)vNj-+NjMTPx##z{m$R5%gkk5LYTFbqSx-T%%TuJ$T7O+ZuG*N`CTv*VQ3{^5BYKdJZDCx+g# zAcpG31XI8Miz+k*#`Ci1VFYu=%+{pK>(%wqV-UCsWypPEEJ7dk`RN$o_QPO}Jotq1 ziqVTtkAzG-;|ho~@!XdHso9`2_w|6|BbM diff --git a/art/sprites/trimetric/md.png b/art/sprites/trimetric/md.png index 941abe432f8ae98d3097a6b015361cf304f12bc6..42098c8860d9bea602480310d73e72bae12a9972 100644 GIT binary patch delta 523 zcmV+m0`&dl1mXmcF@GFML_t(Y4Xu<3j)O1|1Owv#Kh8l}TUL7lED>08=IZHbjLGy~ z*W;#@RMRvKti)L#iV-Z~Uv{eq22A)fN>pQoFwA03a~D|=B;a~i|zl^By7a;RK|jn0N=bg>w9@{*2GrZzA}J#* z4pf|?9Qxl$-F89@W;4*CiomA8SzYX9L7xZ_YFFTRfG=H8w5sh^)J!d&;a)M=vm+wsw2{*^* zs$>#nU0-rzd&#@(H)l+}pU|vGj_tp#_oxyRAN7mHI(UW^hwOH#6O2z&&CyiJYMdB+ zR`O)riu+G;^b@kEZ8!e!m?gyOgGm?SyFLb$D-x9cEJ!>5CI%&o@Hk zkbiUMSY8T|5X+KS-><~HxCRb9dA7XSPyX?E^w(ssc9YyD8q8rO&pduE@#c-S^+B>% zzt7s8R}KSUTn88@iFm96tc$OC>ssn!`#&`a8?pI`3r+%j^Y&Rk%I9FQ)wZt;px~Lc zi5lx@AbGYp`?U5M&ul*-i Date: Thu, 31 Oct 2019 23:04:17 +1100 Subject: [PATCH 193/469] fix pixel error on mc.png --- art/sprites/trimetric/mc.png | Bin 247 -> 247 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/art/sprites/trimetric/mc.png b/art/sprites/trimetric/mc.png index 4214512c757eeaee9f7568f0a79d390f71191030..2b13b11309bd5084aa830cd061b3c545251df440 100644 GIT binary patch delta 197 zcmV;$06PEo0rvrrI)Bj;gCGn9OUM8JWy|6$y=IWMJeY*sZ4hl|Uym=VwKf!Y(qreZ zX6hAa38`iV2{7jH56GPvgC`(X(17gBPSjW-J@})`sGf^s)0- zGxZ8|foKGt1_8z#{ss|H%?YGeK?AZgJ5ghe^trJPf8DJ>&Bm(96WtZ<%mS)L(13VE zfEtMAD>GX62SlQcU(7jpejPyq0J_|BvbFuzH;$xcK1GW^Ha>v^f;i3QDck}%NeV0l zWslsx`^0%C6bSw?<6+I$Im`3uYf!Bwr Date: Thu, 31 Oct 2019 23:16:16 +1100 Subject: [PATCH 194/469] enlarge md screen size --- art/sprites/trimetric/md.png | Bin 610 -> 611 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/art/sprites/trimetric/md.png b/art/sprites/trimetric/md.png index 42098c8860d9bea602480310d73e72bae12a9972..ac6bd0eada152c9f9f20c5fdd02cba560c051e05 100644 GIT binary patch delta 510 zcmVBF3S6qNyJWa#u-&?+)`s#AxjWlgnOm>5DlOlw>xOTb89? zR*1-nRS1_lm-)m}msjkxCi?$SEegWs=W9NPN~2}4K31$bIC2yCe36472c^v>@=2b! z^!J8}k<%DFIiBIOw*c^HM>OV8iLo_re~gt=yfyj01d;IJ_*|7tBI{Ull3TNvyeqNf zjPd&k*^tE)Cvn!3Y_A`T%ib-l7-Y9gonU;LYL2E#R^!Crvyvy{R$PCQqo0sPZM*S* z$1EXMA6$wywjV1t2WEXM-%f}|E}Zv<*kLv$k-j)F_WUA54mnqj>8%h6u`G%8fBj0# zi)-M(ljo5)`^i6_&;FY1)h?3TM1wi308=IZHbjFD7A ze`Yh#p^Ct!z^%I2%Yx1b5#!K3(bNwG`BX`S?-S;Uh|$^$CYQbB(id|mD9LOlw=7G) ztPoWkIWBds6j<>YPb;wh4>>f6^z-x0oIbrl)$bE^@(QdN>xklu90W<8R`n7kdE)fN z8P|UD=ETrLjy-(#762YS5sh`Qj&eqcMB^9+3iv%7@ww^qp6bBI5GIFMXH6QYp|=e;3zm`zEfFHVd-zX*{-&Xr?&D?~ypOJaS$e-iWJ z8aVLe+45#T`RDW5Uz5GsMRJ>HFo%^q^Z2>Mn>W_h2gzRjK5KQ}IShbt9blX!;;{;_ zF23fiYpILv|I{RG#PU?ef|CH>yg2K7d2rUmR@=TZfP%NIP1IPD8e1pNuKBF%@7aDr z0xDs~4$R9+A9>cEkXAhTGyD%hPsmn8#yL>TokG+*2udOP0ukZjep%1F^(%G|1d&h8 zYjLPKZN4&dS43p**B#0tehxf34PRsJ|26&qy!s9 Date: Thu, 31 Oct 2019 22:44:22 +0800 Subject: [PATCH 195/469] refactor left panel code to MCLGUI class --- avr/cores/megacommand/MCL/LFOPage.cpp | 83 +++---------------- avr/cores/megacommand/MCL/MCLGUI.cpp | 78 ++++++++++++++--- avr/cores/megacommand/MCL/MCLGUI.h | 46 +++++++--- .../megacommand/MCL/QuestionDialogPage.cpp | 18 ++-- avr/cores/megacommand/MCL/SeqPage.cpp | 55 +++--------- avr/cores/megacommand/MCL/SeqPage.h | 21 ----- 6 files changed, 135 insertions(+), 166 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 62274990a..9db0fb3a4 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -232,7 +232,6 @@ void LFOPage::draw_dest(uint8_t knob, uint8_t value) { } void LFOPage::display() { - auto oldfont = oled_display.getFont(); if (!classic_display) { #ifdef OLED_DISPLAY oled_display.clearDisplay(); @@ -256,39 +255,12 @@ void LFOPage::display() { #endif #ifdef OLED_DISPLAY - oled_display.setTextColor(WHITE); - oled_display.setFont(&Elektrothic); - oled_display.setCursor(trackid_x, trackid_y); + auto oldfont = oled_display.getFont(); uint8_t lfo_track_num = lfo_track->track_number; - if (lfo_track_num < 10) { - oled_display.print('0'); - } - oled_display.print(lfo_track_num); - oled_display.setFont(&TomThumb); - // draw MD/EXT label - - if ((lfo_track->enable)) { - oled_display.setCursor(label_x + 1, label_md_y + 13); - oled_display.print("OFF"); - oled_display.fillRect(label_x, label_md_y, label_w, label_h, WHITE); - oled_display.setCursor(label_x + 1, label_md_y + 6); - oled_display.setTextColor(BLACK); - oled_display.print("ON"); - oled_display.setTextColor(WHITE); - } else { - - oled_display.setCursor(label_x + 1, label_md_y + 6); - oled_display.print("ON"); - oled_display.fillRect(label_x, label_md_y + 7, label_w, label_h, WHITE); - oled_display.setCursor(label_x + 1, label_md_y + 13); - oled_display.setTextColor(BLACK); - oled_display.print("OFF"); - oled_display.setTextColor(WHITE); - } + mcl_gui.draw_panel_number(lfo_track_num); + mcl_gui.draw_panel_toggle("ON", "OFF", lfo_track->enable); draw_page_index(false); - oled_display.setCursor(0, 0); - uint8_t x = knob_x0 + 5; uint8_t y = 8; uint8_t lfo_height = 7; @@ -297,7 +269,6 @@ void LFOPage::display() { // mcl_gui.draw_vertical_dashline(x, 0, knob_y); SeqPage::draw_knob_frame(); - char K[4]; if (page_mode == LFO_DESTINATION) { draw_dest(0, encoders[0]->cur); @@ -306,30 +277,6 @@ void LFOPage::display() { draw_param(3, encoders[2]->cur, encoders[3]->cur); } if (page_mode == LFO_SETTINGS) { - /* - char K[4]; - switch (encoders[0]->cur) { - case SIN_WAV: - strcpy(K, "SIN"); - break; - case TRI_WAV: - strcpy(K, "TRI"); - break; - case IRAMP_WAV: - strcpy(K, "SAW"); - break; - case RAMP_WAV: - strcpy(K, "RMP"); - break; - case EXP_WAV: - strcpy(K, "DEC"); - break; - case IEXP_WAV: - strcpy(K, "EXP"); - break; - } - strcpy(K, ""); - */ load_wavetable(waveform, &temp_track, 0, lfo_height); uint8_t inc = LFO_LENGTH / width; for (uint8_t n = 0; n < LFO_LENGTH; n += inc, x++) { @@ -347,41 +294,35 @@ void LFOPage::display() { draw_knob(2, encoders[2], "DEP1"); draw_knob(3, encoders[3], "DEP2"); } - // draw_pattern_mask(); oled_display.setFont(&TomThumb); - // oled_display.setCursor(1, info1_y + 6); - // oled_display.print("MODE: "); - - oled_display.setCursor(1, info2_y + 6); + const char* info1; + const char* info2; if (page_mode) { - oled_display.print("LFO A"); + info1 = "LFO A"; } else { - oled_display.print("LFO B"); + info1 = "LFO B"; } - oled_display.fillRect(0, info1_y, pane_w, info_h, WHITE); - oled_display.setTextColor(BLACK); - oled_display.setCursor(1, info1_y + 6); - switch (lfo_track->mode) { case LFO_MODE_FREE: - oled_display.print("FREE"); + info2 = "FREE"; break; case LFO_MODE_TRIG: draw_lock_mask(0, 0, lfo_track->step_count, lfo_track->length, true); draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); - oled_display.print("TRIG"); + info2 = "TRIG"; break; case LFO_MODE_ONE: draw_lock_mask(0, 0, lfo_track->step_count, lfo_track->length, true); draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); - oled_display.print("ONE"); + info2 = "ONE"; break; } - oled_display.setTextColor(WHITE); + mcl_gui.draw_panel_labels(info1, info2); + oled_display.display(); oled_display.setFont(oldfont); #endif diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index e3c934089..0f4da0edf 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -131,26 +131,26 @@ void MCLGUI::draw_infobox(const char *line1, const char *line2, const int line2_offset) { auto oldfont = oled_display.getFont(); - oled_display.fillRect(info_x1 - 1, info_y1 - 1, info_w + 3, info_h + 3, + oled_display.fillRect(dlg_info_x1 - 1, dlg_info_y1 - 1, dlg_info_w + 3, dlg_info_h + 3, BLACK); - oled_display.drawRect(info_x1, info_y1, info_w, info_h, WHITE); - oled_display.drawFastHLine(info_x1 + 1, info_y2 + 1, info_w, WHITE); - oled_display.drawFastVLine(info_x2 + 1, info_y1 + 1, info_h - 1, WHITE); - oled_display.fillRect(info_x1 + 1, info_y1 + 1, info_w - 2, 6, WHITE); + oled_display.drawRect(dlg_info_x1, dlg_info_y1, dlg_info_w, dlg_info_h, WHITE); + oled_display.drawFastHLine(dlg_info_x1 + 1, dlg_info_y2 + 1, dlg_info_w, WHITE); + oled_display.drawFastVLine(dlg_info_x2 + 1, dlg_info_y1 + 1, dlg_info_h - 1, WHITE); + oled_display.fillRect(dlg_info_x1 + 1, dlg_info_y1 + 1, dlg_info_w - 2, 6, WHITE); - oled_display.fillCircle(circle_x, circle_y, 6, WHITE); - oled_display.fillRect(circle_x - 1, circle_y - 3, 2, 4, BLACK); - oled_display.fillRect(circle_x - 1, circle_y + 2, 2, 2, BLACK); + oled_display.fillCircle(dlg_circle_x, dlg_circle_y, 6, WHITE); + oled_display.fillRect(dlg_circle_x - 1, dlg_circle_y - 3, 2, 4, BLACK); + oled_display.fillRect(dlg_circle_x - 1, dlg_circle_y + 2, 2, 2, BLACK); oled_display.setFont(&TomThumb); oled_display.setTextColor(BLACK); - oled_display.setCursor(info_x1 + 4, info_y1 + 6); + oled_display.setCursor(dlg_info_x1 + 4, dlg_info_y1 + 6); strcpy(title_buf, line1); m_toupper(title_buf); oled_display.println(title_buf); oled_display.setTextColor(WHITE); - oled_display.setCursor(info_x1 + 23, info_y1 + 17 + line2_offset); + oled_display.setCursor(dlg_info_x1 + 23, dlg_info_y1 + 17 + line2_offset); oled_display.println(line2); oled_display.setFont(oldfont); @@ -380,3 +380,61 @@ void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, } } } + +void MCLGUI::draw_panel_toggle(const char *s1, const char *s2, bool s1_active) { + oled_display.setFont(&TomThumb); + if (s1_active) { + oled_display.fillRect(pane_label_x, pane_label_md_y, pane_label_w, pane_label_h, WHITE); + oled_display.setCursor(pane_label_x + 1, pane_label_md_y + 6); + oled_display.setTextColor(BLACK); + oled_display.print(s1); + oled_display.setTextColor(WHITE); + } else { + oled_display.setCursor(pane_label_x + 1, pane_label_md_y + 6); + oled_display.setTextColor(WHITE); + oled_display.print(s1); + oled_display.fillRect(pane_label_x, pane_label_ex_y, pane_label_w, pane_label_h, WHITE); + oled_display.setTextColor(BLACK); + } + oled_display.setCursor(pane_label_x + 1, pane_label_ex_y + 6); + oled_display.print(s2); + oled_display.setTextColor(WHITE); +} + +void MCLGUI::draw_panel_labels(const char *info1, const char *info2) { + oled_display.setFont(&TomThumb); + oled_display.fillRect(0, pane_info1_y, pane_w, pane_info_h, WHITE); + oled_display.setTextColor(BLACK); + oled_display.setCursor(1, pane_info1_y + 6); + oled_display.print(info1); + oled_display.setTextColor(WHITE); + oled_display.setCursor(1, pane_info2_y + 6); + oled_display.print(info2); +} + +void MCLGUI::draw_panel_status(bool recording, bool playing) { + if (recording) { + oled_display.fillRect(pane_cir_x1, pane_tri_y, 4, 5, WHITE); + oled_display.drawPixel(pane_cir_x1, pane_tri_y, BLACK); + oled_display.drawPixel(pane_cir_x2, pane_tri_y, BLACK); + oled_display.drawPixel(pane_cir_x1, pane_tri_y + 4, BLACK); + oled_display.drawPixel(pane_cir_x2, pane_tri_y + 4, BLACK); + } else if (playing) { + oled_display.drawLine(pane_tri_x, pane_tri_y, pane_tri_x, pane_tri_y + 4, WHITE); + oled_display.fillTriangle(pane_tri_x + 1, pane_tri_y, pane_tri_x + 3, pane_tri_y + 2, pane_tri_x + 1, + pane_tri_y + 4, WHITE); + } else { + oled_display.fillRect(pane_tri_x, pane_tri_y, 4, 5, WHITE); + } +} + +void MCLGUI::draw_panel_number(uint8_t number) +{ + oled_display.setTextColor(WHITE); + oled_display.setFont(&Elektrothic); + oled_display.setCursor(pane_trackid_x, pane_trackid_y); + if (number < 10) { + oled_display.print('0'); + } + oled_display.print(number); +} diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index aa8ba70b3..f2b0ff08a 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -39,6 +39,11 @@ class MCLGUI { void draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); void draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask); + void draw_panel_toggle(const char* s1, const char* s2, bool s1_active); + void draw_panel_labels(const char* info1, const char* info2); + void draw_panel_status(bool recording, bool playing); + void draw_panel_number(uint8_t number); + static constexpr uint8_t s_menu_w = 96; static constexpr uint8_t s_menu_h = 24; static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; @@ -49,16 +54,37 @@ class MCLGUI { static constexpr uint8_t s_rightpane_offset_x = 43; static constexpr uint8_t s_rightpane_offset_y = 8; - static constexpr auto info_y1 = 2; - static constexpr auto info_y2 = 27; - static constexpr auto info_x1 = 12; - static constexpr auto info_x2 = 124; - static constexpr auto circle_x = info_x1 + 10; - static constexpr auto circle_y = info_y1 + 15; - - static constexpr auto info_w = info_x2 - info_x1 + 1; - static constexpr auto info_h = info_y2 - info_y1 + 1; - + static constexpr auto dlg_info_y1 = 2; + static constexpr auto dlg_info_y2 = 27; + static constexpr auto dlg_info_x1 = 12; + static constexpr auto dlg_info_x2 = 124; + static constexpr auto dlg_circle_x = dlg_info_x1 + 10; + static constexpr auto dlg_circle_y = dlg_info_y1 + 15; + + static constexpr auto dlg_info_w = dlg_info_x2 - dlg_info_x1 + 1; + static constexpr auto dlg_info_h = dlg_info_y2 - dlg_info_y1 + 1; + + static constexpr uint8_t pane_label_x = 0; + static constexpr uint8_t pane_label_md_y = 0; + static constexpr uint8_t pane_label_ex_y = 7; + static constexpr uint8_t pane_label_w = 13; + static constexpr uint8_t pane_label_h = 7; + + static constexpr uint8_t pane_info1_y = 19; + static constexpr uint8_t pane_info2_y = 26; + static constexpr uint8_t pane_info_h = 7; + + static constexpr uint8_t pane_x1 = 0; + static constexpr uint8_t pane_x2 = 30; + static constexpr uint8_t pane_w = pane_x2 - pane_x1; + + static constexpr uint8_t pane_cir_x1 = 22; + static constexpr uint8_t pane_cir_x2 = 25; + static constexpr uint8_t pane_tri_x = 16; + static constexpr uint8_t pane_tri_y = 9; + + static constexpr uint8_t pane_trackid_x = 15; + static constexpr uint8_t pane_trackid_y = 8; }; extern MCLGUI mcl_gui; diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp index 6edde3b3d..129a6574d 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -4,24 +4,24 @@ void QuestionDialogPage::init(const char* title, const char* text) { #ifdef OLED_DISPLAY mcl_gui.draw_infobox(title, text, -1); - oled_display.drawFastHLine(MCLGUI::info_x1 + 1, MCLGUI::info_y2, MCLGUI::info_w - 2, BLACK); + oled_display.drawFastHLine(MCLGUI::dlg_info_x1 + 1, MCLGUI::dlg_info_y2, MCLGUI::dlg_info_w - 2, BLACK); auto oldfont = oled_display.getFont(); oled_display.setFont(&TomThumb); oled_display.setTextColor(WHITE); - oled_display.setCursor(MCLGUI::info_x2 - 86, MCLGUI::info_y1 + 24); + oled_display.setCursor(MCLGUI::dlg_info_x2 - 86, MCLGUI::dlg_info_y1 + 24); oled_display.print("2 YES"); - oled_display.setCursor(MCLGUI::info_x2 - 55, MCLGUI::info_y1 + 24); + oled_display.setCursor(MCLGUI::dlg_info_x2 - 55, MCLGUI::dlg_info_y1 + 24); oled_display.print("3 NO"); - oled_display.drawRect(MCLGUI::info_x2 - 88, MCLGUI::info_y1 + 17, 21, 8, WHITE); - oled_display.drawRect(MCLGUI::info_x2 - 57, MCLGUI::info_y1 + 17, 19, 8, WHITE); + oled_display.drawRect(MCLGUI::dlg_info_x2 - 88, MCLGUI::dlg_info_y1 + 17, 21, 8, WHITE); + oled_display.drawRect(MCLGUI::dlg_info_x2 - 57, MCLGUI::dlg_info_y1 + 17, 19, 8, WHITE); - oled_display.fillRect(MCLGUI::info_x2 - 87, MCLGUI::info_y1 + 18, 5, 6, INVERT); - oled_display.fillRect(MCLGUI::info_x2 - 56, MCLGUI::info_y1 + 18, 5, 6, INVERT); + oled_display.fillRect(MCLGUI::dlg_info_x2 - 87, MCLGUI::dlg_info_y1 + 18, 5, 6, INVERT); + oled_display.fillRect(MCLGUI::dlg_info_x2 - 56, MCLGUI::dlg_info_y1 + 18, 5, 6, INVERT); oled_display.setFont(oldfont); oled_display.display(); @@ -36,13 +36,13 @@ bool QuestionDialogPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { - oled_display.fillRect(MCLGUI::info_x2 - 82, MCLGUI::info_y1 + 18, 12, 6, INVERT); + oled_display.fillRect(MCLGUI::dlg_info_x2 - 82, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); return true; } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { - oled_display.fillRect(MCLGUI::info_x2 - 51, MCLGUI::info_y1 + 18, 12, 6, INVERT); + oled_display.fillRect(MCLGUI::dlg_info_x2 - 51, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); return true; } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index a2e438abf..c8fc8a543 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -668,8 +668,10 @@ void SeqPage::display() { GUI.setLine(GUI.LINE1); } #else + // ref: design/Sequencer.png void SeqPage::display() { + auto* oldfont = oled_display.getFont(); oled_display.clearDisplay(); bool is_md = (midi_device == DEVICE_MD); @@ -686,59 +688,21 @@ void SeqPage::display() { track_id += 1; // draw current active track - oled_display.setTextColor(WHITE); - oled_display.setFont(&Elektrothic); - oled_display.setCursor(trackid_x, trackid_y); - if (track_id < 10) { - oled_display.print('0'); - } - oled_display.print(track_id); + mcl_gui.draw_panel_number(track_id); - oled_display.setFont(&TomThumb); // draw MD/EXT label - if (is_md) { - oled_display.fillRect(label_x, label_md_y, label_w, label_h, WHITE); - oled_display.setCursor(label_x + 1, label_md_y + 6); - oled_display.setTextColor(BLACK); - oled_display.print("MD"); - oled_display.setTextColor(WHITE); - } else { - oled_display.setCursor(label_x + 1, label_md_y + 6); - oled_display.setTextColor(WHITE); - oled_display.print("MD"); - oled_display.fillRect(label_x, label_ex_y, label_w, label_h, WHITE); - oled_display.setTextColor(BLACK); - } - oled_display.setCursor(label_x + 1, label_ex_y + 6); + const char* str_ext = "MI"; if (ext_is_a4) { - oled_display.print("A4"); - } else { - oled_display.print("MI"); + str_ext = "A4"; } + mcl_gui.draw_panel_toggle("MD", str_ext, is_md); // draw stop/play/rec state - if (recording) { - oled_display.fillRect(cir_x1, tri_y, 4, 5, WHITE); - oled_display.drawPixel(cir_x1, tri_y, BLACK); - oled_display.drawPixel(cir_x2, tri_y, BLACK); - oled_display.drawPixel(cir_x1, tri_y + 4, BLACK); - oled_display.drawPixel(cir_x2, tri_y + 4, BLACK); - } else if (MidiClock.state == 2) { - oled_display.drawLine(tri_x, tri_y, tri_x, tri_y + 4, WHITE); - oled_display.fillTriangle(tri_x + 1, tri_y, tri_x + 3, tri_y + 2, tri_x + 1, - tri_y + 4, WHITE); - } else { - oled_display.fillRect(tri_x, tri_y, 4, 5, WHITE); - } + mcl_gui.draw_panel_status(recording, MidiClock.state == 2); + draw_page_index(); // draw info lines - oled_display.fillRect(0, info1_y, pane_w, info_h, WHITE); - oled_display.setTextColor(BLACK); - oled_display.setCursor(1, info1_y + 6); - oled_display.print(info1); - oled_display.setTextColor(WHITE); - oled_display.setCursor(1, info2_y + 6); - oled_display.print(info2); + mcl_gui.draw_panel_labels(info1, info2); // if (show_track_menu) { // uint8_t x_offset = 43; @@ -747,6 +711,7 @@ void SeqPage::display() { // oled_display.fillRect(84, 0, 40, 32, BLACK); // track_menu_page.draw_menu(86, y_offset, 39); //} + oled_display.setFont(oldfont); } #endif diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 67ebdd4c4..c726b01b3 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -56,32 +56,11 @@ class SeqPage : public LightPage { virtual void init(); virtual void cleanup(); - static constexpr uint8_t pane_x1 = 0; - static constexpr uint8_t pane_x2 = 30; - static constexpr uint8_t pane_w = pane_x2 - pane_x1; - static constexpr uint8_t label_x = 0; - static constexpr uint8_t label_md_y = 0; - static constexpr uint8_t label_ex_y = 7; - static constexpr uint8_t label_w = 13; - static constexpr uint8_t label_h = 7; - - static constexpr uint8_t trackid_x = 15; - static constexpr uint8_t trackid_y = 8; - - static constexpr uint8_t cir_x1 = 22; - static constexpr uint8_t cir_x2 = 25; - static constexpr uint8_t tri_x = 16; - static constexpr uint8_t tri_y = 9; - static constexpr uint8_t pidx_x0 = 1; static constexpr uint8_t pidx_y = 15; static constexpr uint8_t pidx_w = 6; static constexpr uint8_t pidx_h = 3; - static constexpr uint8_t info1_y = 19; - static constexpr uint8_t info2_y = 26; - static constexpr uint8_t info_h = 7; - static constexpr uint8_t seq_w = 5; static constexpr uint8_t seq_x0 = 32; static constexpr uint8_t led_y = 22; From cb5702d48531725f9fa4efe4c9b560b47c0b21d3 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 00:22:52 +0800 Subject: [PATCH 196/469] extract trigs, leds, ext tracks drawing code to MCLGUI --- avr/cores/megacommand/MCL/MCLGUI.cpp | 144 +++++++++++++++++++++++--- avr/cores/megacommand/MCL/MCLGUI.h | 7 ++ avr/cores/megacommand/MCL/SeqPage.cpp | 111 +------------------- avr/cores/megacommand/MCL/SeqPage.h | 3 - 4 files changed, 141 insertions(+), 124 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 0f4da0edf..a1e2e8cd6 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -131,12 +131,16 @@ void MCLGUI::draw_infobox(const char *line1, const char *line2, const int line2_offset) { auto oldfont = oled_display.getFont(); - oled_display.fillRect(dlg_info_x1 - 1, dlg_info_y1 - 1, dlg_info_w + 3, dlg_info_h + 3, - BLACK); - oled_display.drawRect(dlg_info_x1, dlg_info_y1, dlg_info_w, dlg_info_h, WHITE); - oled_display.drawFastHLine(dlg_info_x1 + 1, dlg_info_y2 + 1, dlg_info_w, WHITE); - oled_display.drawFastVLine(dlg_info_x2 + 1, dlg_info_y1 + 1, dlg_info_h - 1, WHITE); - oled_display.fillRect(dlg_info_x1 + 1, dlg_info_y1 + 1, dlg_info_w - 2, 6, WHITE); + oled_display.fillRect(dlg_info_x1 - 1, dlg_info_y1 - 1, dlg_info_w + 3, + dlg_info_h + 3, BLACK); + oled_display.drawRect(dlg_info_x1, dlg_info_y1, dlg_info_w, dlg_info_h, + WHITE); + oled_display.drawFastHLine(dlg_info_x1 + 1, dlg_info_y2 + 1, dlg_info_w, + WHITE); + oled_display.drawFastVLine(dlg_info_x2 + 1, dlg_info_y1 + 1, dlg_info_h - 1, + WHITE); + oled_display.fillRect(dlg_info_x1 + 1, dlg_info_y1 + 1, dlg_info_w - 2, 6, + WHITE); oled_display.fillCircle(dlg_circle_x, dlg_circle_y, 6, WHITE); oled_display.fillRect(dlg_circle_x - 1, dlg_circle_y - 3, 2, 4, BLACK); @@ -381,10 +385,122 @@ void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, } } +void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, + uint64_t pattern_mask, uint8_t step_count, + uint8_t length) { + for (int i = 0; i < 16; i++) { + + uint8_t idx = i + offset; + bool in_range = idx < length; + + if (note_interface.notes[i] == 1) { + // TI feedback + oled_display.fillRect(x - 1, y, seq_w + 2, trig_h + 1, WHITE); + } else if (!in_range) { + // don't draw + } else { + if (IS_BIT_SET64(pattern_mask, i + offset) && + ((i + offset != step_count) || (MidiClock.state != 2))) { + /*If the bit is set, there is a trigger at this position. */ + oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + } else { + oled_display.drawRect(x, y, seq_w, trig_h, WHITE); + } + } + + x += seq_w + 1; + } +} + +void MCLGUI::draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, + uint8_t ext_trackid, bool show_current_step) { +#ifdef EXT_TRACKS + int8_t note_held = 0; + auto &active_track = mcl_seq.ext_tracks[ext_trackid]; + for (int i = 0; i < active_track.length; i++) { + + uint8_t step_count = active_track.step_count; + uint8_t noteson = 0; + uint8_t notesoff = 0; + bool in_range = (i >= offset) && (i < offset + 16); + bool right_most = (i == active_track.length - 1); + + for (uint8_t a = 0; a < 4; a++) { + if (active_track.notes[a][i] > 0) { + noteson++; + } + if (active_track.notes[a][i] < 0) { + notesoff++; + } + } + + note_held += noteson; + note_held -= notesoff; + + if (!in_range) { + continue; + } + + if (note_interface.notes[i - offset] == 1) { + oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + } else if (!note_held) { // -- + oled_display.drawFastHLine(x - 1, y + 2, seq_w + 2, WHITE); + } else { // draw top, bottom + oled_display.drawFastHLine(x - 1, y, seq_w + 2, WHITE); + oled_display.drawFastHLine(x - 1, y + trig_h - 1, seq_w + 2, + WHITE); + } + + if (noteson > 0 || notesoff > 0) { // left | + oled_display.drawFastVLine(x - 1, y, trig_h, WHITE); + } + + if (right_most && note_held) { // right | + oled_display.drawFastVLine(x + seq_w, y, trig_h, WHITE); + } + + if ((step_count == i) && (MidiClock.state == 2) && show_current_step) { + oled_display.fillRect(x, y, seq_w, trig_h, INVERT); + } + + x += seq_w + 1; + } +#endif // EXT_TRACKS +} + +void MCLGUI::draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, + uint8_t step_count, uint8_t length, + bool show_current_step) { + for (int i = 0; i < 16; i++) { + + uint8_t idx = i + offset; + bool in_range = idx < length; + bool current = + show_current_step && step_count == idx && MidiClock.state == 2; + bool locked = in_range && IS_BIT_SET64(lock_mask, i + offset); + + if (note_interface.notes[i] == 1) { + // TI feedback + oled_display.fillRect(x - 1, y - 1, seq_w + 2, led_h + 1, WHITE); + } else if (!in_range) { + // don't draw + } else if (current ^ locked) { + // highlight + oled_display.fillRect(x, y, seq_w, led_h, WHITE); + } else { + // (current && locked) or (not current && not locked), frame only + oled_display.drawRect(x, y, seq_w, led_h, WHITE); + } + + x += seq_w + 1; + } +} + void MCLGUI::draw_panel_toggle(const char *s1, const char *s2, bool s1_active) { oled_display.setFont(&TomThumb); if (s1_active) { - oled_display.fillRect(pane_label_x, pane_label_md_y, pane_label_w, pane_label_h, WHITE); + oled_display.fillRect(pane_label_x, pane_label_md_y, pane_label_w, + pane_label_h, WHITE); oled_display.setCursor(pane_label_x + 1, pane_label_md_y + 6); oled_display.setTextColor(BLACK); oled_display.print(s1); @@ -393,7 +509,8 @@ void MCLGUI::draw_panel_toggle(const char *s1, const char *s2, bool s1_active) { oled_display.setCursor(pane_label_x + 1, pane_label_md_y + 6); oled_display.setTextColor(WHITE); oled_display.print(s1); - oled_display.fillRect(pane_label_x, pane_label_ex_y, pane_label_w, pane_label_h, WHITE); + oled_display.fillRect(pane_label_x, pane_label_ex_y, pane_label_w, + pane_label_h, WHITE); oled_display.setTextColor(BLACK); } oled_display.setCursor(pane_label_x + 1, pane_label_ex_y + 6); @@ -420,16 +537,17 @@ void MCLGUI::draw_panel_status(bool recording, bool playing) { oled_display.drawPixel(pane_cir_x1, pane_tri_y + 4, BLACK); oled_display.drawPixel(pane_cir_x2, pane_tri_y + 4, BLACK); } else if (playing) { - oled_display.drawLine(pane_tri_x, pane_tri_y, pane_tri_x, pane_tri_y + 4, WHITE); - oled_display.fillTriangle(pane_tri_x + 1, pane_tri_y, pane_tri_x + 3, pane_tri_y + 2, pane_tri_x + 1, - pane_tri_y + 4, WHITE); + oled_display.drawLine(pane_tri_x, pane_tri_y, pane_tri_x, pane_tri_y + 4, + WHITE); + oled_display.fillTriangle(pane_tri_x + 1, pane_tri_y, pane_tri_x + 3, + pane_tri_y + 2, pane_tri_x + 1, pane_tri_y + 4, + WHITE); } else { oled_display.fillRect(pane_tri_x, pane_tri_y, 4, 5, WHITE); } } -void MCLGUI::draw_panel_number(uint8_t number) -{ +void MCLGUI::draw_panel_number(uint8_t number) { oled_display.setTextColor(WHITE); oled_display.setFont(&Elektrothic); oled_display.setCursor(pane_trackid_x, pane_trackid_y); diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index f2b0ff08a..79f66f946 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -38,12 +38,19 @@ class MCLGUI { void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); void draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); void draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask); + void draw_trigs(uint8_t x, uint8_t y, uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length); + void draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, uint8_t ext_trackid, bool show_current_step); + void draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step); void draw_panel_toggle(const char* s1, const char* s2, bool s1_active); void draw_panel_labels(const char* info1, const char* info2); void draw_panel_status(bool recording, bool playing); void draw_panel_number(uint8_t number); + static constexpr uint8_t seq_w = 5; + static constexpr uint8_t led_h = 3; + static constexpr uint8_t trig_h = 5; + static constexpr uint8_t s_menu_w = 96; static constexpr uint8_t s_menu_h = 24; static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index c8fc8a543..ee3672531 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -419,32 +419,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, void SeqPage::draw_lock_mask(uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step) { - uint8_t led_x = seq_x0; - - for (int i = 0; i < 16; i++) { - - uint8_t idx = i + offset; - bool in_range = idx < length; - bool current = - show_current_step && step_count == idx && MidiClock.state == 2; - bool locked = in_range && IS_BIT_SET64(lock_mask, i + offset); - - if (note_interface.notes[i] == 1) { - // TI feedback - oled_display.fillRect(led_x - 1, led_y - 1, seq_w + 2, led_h + 1, WHITE); - } else if (!in_range) { - // don't draw - } else if (current ^ locked) { - // highlight - oled_display.fillRect(led_x, led_y, seq_w, led_h, WHITE); - } else { - // (current && locked) or (not current && not locked), frame only - oled_display.drawRect(led_x, led_y, seq_w, led_h, WHITE); - } - - led_x += seq_w + 1; - } - + mcl_gui.draw_leds(seq_x0, led_y, offset, lock_mask, step_count, length, show_current_step); } void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { @@ -453,98 +428,18 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { } void SeqPage::draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step) { - - uint8_t trig_x = seq_x0; - - - for (int i = 0; i < 16; i++) { - - uint8_t idx = i + offset; - bool in_range = idx < length; - - if (note_interface.notes[i] == 1) { - // TI feedback - oled_display.fillRect(trig_x - 1, trig_y, seq_w + 2, trig_h + 1, WHITE); - } else if (!in_range) { - // don't draw - } else { - if (IS_BIT_SET64(pattern_mask, i + offset) && ((i + offset != step_count) || (MidiClock.state != 2))) { - /*If the bit is set, there is a trigger at this position. */ - oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); - } else { - oled_display.drawRect(trig_x, trig_y, seq_w, trig_h, WHITE); - } - } - - trig_x += seq_w + 1; - } - + mcl_gui.draw_trigs(seq_x0, trig_y, offset, pattern_mask, step_count, length); } void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step) { - - uint8_t trig_x = seq_x0; - if (device == DEVICE_MD) { auto &active_track = mcl_seq.md_tracks[last_md_track]; - uint64_t pattern_mask = active_track.pattern_mask; - draw_pattern_mask(offset, active_track.pattern_mask, active_track.step_count, active_track.length, show_current_step); } #ifdef EXT_TRACKS else { - - int8_t note_held = 0; - auto &active_track = mcl_seq.ext_tracks[last_ext_track]; - for (int i = 0; i < active_track.length; i++) { - - uint8_t step_count = active_track.step_count; - uint8_t noteson = 0; - uint8_t notesoff = 0; - bool in_range = (i >= offset) && (i < offset + 16); - bool right_most = (i == active_track.length - 1); - - for (uint8_t a = 0; a < 4; a++) { - if (active_track.notes[a][i] > 0) { - noteson++; - } - if (active_track.notes[a][i] < 0) { - notesoff++; - } - } - - note_held += noteson; - note_held -= notesoff; - - if (!in_range) { - continue; - } - - if (note_interface.notes[i - offset] == 1) { - oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, WHITE); - } else if (!note_held) { // -- - oled_display.drawFastHLine(trig_x - 1, trig_y + 2, seq_w + 2, WHITE); - } else { // draw top, bottom - oled_display.drawFastHLine(trig_x - 1, trig_y, seq_w + 2, WHITE); - oled_display.drawFastHLine(trig_x - 1, trig_y + trig_h - 1, seq_w + 2, - WHITE); - } - - if (noteson > 0 || notesoff > 0) { // left | - oled_display.drawFastVLine(trig_x - 1, trig_y, trig_h, WHITE); - } - - if (right_most && note_held) { // right | - oled_display.drawFastVLine(trig_x + seq_w, trig_y, trig_h, WHITE); - } - - if ((step_count == i) && (MidiClock.state == 2) && show_current_step) { - oled_display.fillRect(trig_x, trig_y, seq_w, trig_h, INVERT); - } - - trig_x += seq_w + 1; - } + mcl_gui.draw_ext_track(seq_x0, trig_y, offset, last_ext_track, show_current_step); } #endif } diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index c726b01b3..e52cc77b5 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -61,12 +61,9 @@ class SeqPage : public LightPage { static constexpr uint8_t pidx_w = 6; static constexpr uint8_t pidx_h = 3; - static constexpr uint8_t seq_w = 5; static constexpr uint8_t seq_x0 = 32; static constexpr uint8_t led_y = 22; static constexpr uint8_t trig_y = 26; - static constexpr uint8_t led_h = 3; - static constexpr uint8_t trig_h = 5; static constexpr uint8_t knob_x0 = 31; static constexpr uint8_t knob_w = 24; From 36df8f568822155fd73c05240666f3e8f00bdc8f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 01:07:06 +0800 Subject: [PATCH 197/469] save page on a dialog --- avr/cores/megacommand/MCL/GridSavePage.cpp | 35 ++++++++++++++++++---- avr/cores/megacommand/MCL/MCLGUI.cpp | 21 +++++++++++-- avr/cores/megacommand/MCL/MCLGUI.h | 5 ++-- avr/cores/megacommand/MCL/SeqPage.cpp | 9 +----- avr/cores/megacommand/MCL/SeqPage.h | 1 + 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index e4679bce3..5127443b7 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -12,14 +12,15 @@ void GridSavePage::setup() { grid_page.reload_slot_models = false; } -void GridSavePage::init() { +void GridSavePage::init() { #ifdef OLED_DISPLAY - oled_display.clearDisplay(); + mcl_gui.draw_popup("SAVE", true, 28); #endif } void GridSavePage::cleanup() {} +#ifndef OLED_DISPLAY void GridSavePage::display() { GUI.setLine(GUI.LINE1); char strn[17] = "----------------"; @@ -28,11 +29,7 @@ void GridSavePage::display() { if (note_interface.notes[i] != 0) { -#ifdef OLED_DISPLAY - strn[i] = (char)2; -#else strn[i] = (char)219; -#endif } } @@ -53,6 +50,32 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); GUI.put_value_at2(14, step_count); } +#else +void GridSavePage::display() { + + oled_display.fillRect(MCLGUI::s_menu_x + 3, MCLGUI::s_menu_y + 20, 98, 7, BLACK); + mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); + + const char* merge = "NO"; + if ((MidiClock.state != 2) && (encoders[0]->cur == 1)) { + merge = "YES"; + } + + oled_display.fillRect(MCLGUI::s_menu_x + 8, MCLGUI::s_menu_y + 5, 18, 16, BLACK); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 5, "MERGE", merge); + + char step[4] = {'\0'}; + uint8_t step_count = + (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - + (64 * + ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); + itoa(step_count, step, 10); + + oled_display.fillRect(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, 16, 16, BLACK); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, "STEP", step); + oled_display.display(); +} +#endif bool GridSavePage::handleEvent(gui_event_t *event) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index a1e2e8cd6..33d5a8a8b 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -56,17 +56,20 @@ void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, static char title_buf[16]; // ref: Design/popup_menu.png -void MCLGUI::draw_popup(const char *title, bool deferred_display) { +void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { strcpy(title_buf, title); m_toupper(title_buf); + if (h == 0) { + h = s_menu_h; + } oled_display.setFont(&TomThumb); // draw menu body - oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, s_menu_h + 2, + oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, BLACK); - oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, s_menu_h, WHITE); + oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, h, WHITE); oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 4, WHITE); // draw the title '____/**********\____' part @@ -243,6 +246,18 @@ bool MCLGUI::show_encoder_value(Encoder *encoder) { return false; } +void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char* value) +{ + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + oled_display.setCursor(x + 4, y + 6); + oled_display.print(name); + + oled_display.setFont(); + oled_display.setCursor(x + 4, y + 8); + oled_display.print(value); +} + void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name) { bool show_value = show_encoder_value(encoder); diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 79f66f946..98af9d5e6 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -22,7 +22,7 @@ class MCLGUI { void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); /// Clear the content area of a popup void clear_popup(); - void draw_popup(const char* title, bool deferred_display = false); + void draw_popup(const char* title, bool deferred_display = false, uint8_t h = 0); void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); void clear_leftpane(); @@ -33,6 +33,7 @@ class MCLGUI { bool show_encoder_value(Encoder *encoder); + void draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char* value); void draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); void draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); @@ -51,7 +52,7 @@ class MCLGUI { static constexpr uint8_t led_h = 3; static constexpr uint8_t trig_h = 5; - static constexpr uint8_t s_menu_w = 96; + static constexpr uint8_t s_menu_w = 104; static constexpr uint8_t s_menu_h = 24; static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; static constexpr uint8_t s_menu_y = (32 - s_menu_h) / 2; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index ee3672531..3e2b095d3 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -625,14 +625,7 @@ void SeqPage::draw_knob_frame() { void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { uint8_t x = knob_x0 + i * knob_w; - oled_display.setFont(&TomThumb); - oled_display.setTextColor(WHITE); - oled_display.setCursor(x + 4, 7); - oled_display.print(title); - - oled_display.setFont(); - oled_display.setCursor(x + 4, 9); - oled_display.print(text); + mcl_gui.draw_text_encoder(x, knob_y0, title, text); } void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* title) { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index e52cc77b5..a47b72332 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -68,6 +68,7 @@ class SeqPage : public LightPage { static constexpr uint8_t knob_x0 = 31; static constexpr uint8_t knob_w = 24; static constexpr uint8_t knob_xend = 127; + static constexpr uint8_t knob_y0 = 1; static constexpr uint8_t knob_y = 20; }; From 7c58e3f40448655e13a679439d52446fde03b4fc Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 14:36:03 +0800 Subject: [PATCH 198/469] improve ssd1305 library drawing performance --- .../Adafruit_SSD1305.cpp | 542 ++++++++++++------ .../Adafruit_SSD1305.h | 4 + 2 files changed, 370 insertions(+), 176 deletions(-) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index c0567f0cf..2ef717b25 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -6,30 +6,30 @@ This is a library for our Monochrome OLEDs based on SSD1305 drivers These displays use I2C or SPI to communicate -Adafruit invests time and resources providing this open source code, -please support Adafruit and open-source hardware by purchasing +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing products from Adafruit! -Written by Limor Fried/Ladyada for Adafruit Industries. +Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, check license.txt for more information -All text above, and the splash screen below must be included in any redistribution +All text above, and the splash screen below must be included in any +redistribution *********************************************************************/ - #ifdef __AVR__ - #include - #include +#include +#include #elif defined(ESP8266) - #include +#include #else - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) #endif -#include -#include -#include #include "Adafruit_GFX.h" #include "Adafruit_SSD1305.h" +#include +#include +#include #include "glcdfont.c" @@ -48,77 +48,97 @@ extern const uint8_t PROGMEM font[]; // the memory buffer for the LCD -static uint8_t buffer[SSD1305_LCDHEIGHT * SSD1305_LCDWIDTH / 8] = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xC0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x0C, 0x3E, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xF8, 0xF0, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xE0, 0xE0, 0xE0, -0xE0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x1F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFD, -0xFB, 0x7B, 0xBF, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x3F, 0x1F, 0x0F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xE0, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +static uint8_t buffer[SSD1305_LCDHEIGHT * SSD1305_LCDWIDTH / 8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x3E, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xF8, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x1F, 0xBF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFD, 0xFD, 0xFB, 0x7B, 0xBF, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, + 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x7F, 0x3F, 0x3F, 0x1F, 0x0F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, #if (SSD1305_LCDHEIGHT == 64) -0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, -0xFE, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF1, -0xC1, 0x01, 0x00, 0x00, 0x70, 0x78, 0x7C, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xF8, -0xF0, 0x00, 0x00, 0xF0, 0xF8, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x70, 0x78, 0x7C, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xF8, 0xF0, 0x00, -0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0x3D, 0x3D, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x70, 0x38, -0x3C, 0x3C, 0x3C, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0xFC, -0xFC, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, -0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03, -0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, -0xFF, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF, -0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, -0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, -0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9, -0xF9, 0xF8, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, -0xF8, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, -0xF8, 0xF9, 0x09, 0xF9, 0x09, 0xD8, 0xB8, 0x78, 0x08, 0xF8, 0x09, 0xE9, 0xE9, 0x19, 0xF8, 0x08, -0xF8, 0xF8, 0x08, 0xF8, 0xD8, 0xA8, 0xA9, 0x69, 0xF9, 0xE9, 0x09, 0xE9, 0xF8, 0x09, 0xA9, 0x29, -0xD9, 0xF8, 0x08, 0xF9, 0x09, 0xA9, 0xA9, 0xF8, 0xD8, 0xA8, 0xA9, 0x69, 0xF9, 0xF9, 0xF9, 0xF9, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, -0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, -0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, + 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF1, 0xC1, 0x01, 0x00, 0x00, + 0x70, 0x78, 0x7C, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xF8, + 0xF0, 0x00, 0x00, 0xF0, 0xF8, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x78, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x70, 0x78, 0x7C, 0x7C, 0x3C, 0x3C, + 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xF8, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x3D, 0x3D, 0x3D, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x70, 0x38, + 0x3C, 0x3C, 0x3C, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03, + 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x3F, 0x7F, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, + 0xE7, 0xE7, 0xE7, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xF7, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, + 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, + 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF8, 0xF8, 0xF9, + 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, + 0xF8, 0xF9, 0x09, 0xF9, 0x09, 0xD8, 0xB8, 0x78, 0x08, 0xF8, 0x09, 0xE9, + 0xE9, 0x19, 0xF8, 0x08, 0xF8, 0xF8, 0x08, 0xF8, 0xD8, 0xA8, 0xA9, 0x69, + 0xF9, 0xE9, 0x09, 0xE9, 0xF8, 0x09, 0xA9, 0x29, 0xD9, 0xF8, 0x08, 0xF9, + 0x09, 0xA9, 0xA9, 0xF8, 0xD8, 0xA8, 0xA9, 0x69, 0xF9, 0xF9, 0xF9, 0xF9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01 #endif }; - - // the most basic function, set a single pixel void Adafruit_SSD1305::drawPixel(int16_t x, int16_t y, uint16_t color) { if ((x >= width()) || (y >= height()) || (x < 0) || (y < 0)) @@ -131,26 +151,194 @@ void Adafruit_SSD1305::drawPixel(int16_t x, int16_t y, uint16_t color) { x = WIDTH - x - 1; break; case 2: - // x = WIDTH - x - 1; + // x = WIDTH - x - 1; y = HEIGHT - y - 1; break; case 3: adagfx_swap(x, y); y = HEIGHT - y - 1; break; - } + } draw_pixel: // x is which column - if (color == WHITE) - buffer[x+ (y/8)*SSD1305_LCDWIDTH] |= _BV((y%8)); - else if (color == INVERT) - { - color = (buffer[x+ (y/8)*SSD1305_LCDWIDTH] & _BV((y%8))) ? BLACK : WHITE; + if (color == WHITE) + buffer[x + (y / 8) * SSD1305_LCDWIDTH] |= _BV((y % 8)); + else if (color == INVERT) { + color = + (buffer[x + (y / 8) * SSD1305_LCDWIDTH] & _BV((y % 8))) ? BLACK : WHITE; goto draw_pixel; + } else // BLACK + buffer[x + (y / 8) * SSD1305_LCDWIDTH] &= ~_BV((y % 8)); +} + +void Adafruit_SSD1305::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if (x < 0) { + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + if (y + h > SSD1305_LCDHEIGHT) { + h = SSD1305_LCDHEIGHT - y; + } + + // check rotation, move pixel around if necessary* + // MegaCommand rotation + y = SSD1305_LCDHEIGHT - y - h; + + // initial pointer position + uint8_t *p = buffer + x + (y / 8) * SSD1305_LCDWIDTH; + + // 1. upper part + uint8_t h_ = 8 - (y & 0x07); + + if (h_ != 8) { + // higher bits ON + uint8_t mask = ~(0xFF >> h_); + + if (h_ > h) { + mask = mask & (0xFF >> (h_ - h)); + h_ = h; + } + + if (color == WHITE) { + *p |= mask; + } else if (color == BLACK) { + *p &= ~mask; + } else { // INVERT + *p ^= mask; + } + p += SSD1305_LCDWIDTH; + h -= h_; + } + + // 2. center fast part + while (h >= 8) { + if (color == WHITE) { + *p = 0xFF; + } else if (color == BLACK) { + *p = 0x00; + } else { // INVERT + *p = ~*p; + } + h -= 8; + p += SSD1305_LCDWIDTH; + } + + // 3. lower part + if (h != 0) { + uint8_t mask = ((1 << h) - 1); + if (color == WHITE) { + *p |= mask; + } else if (color == BLACK) { + *p &= ~mask; + } else { // INVERT + *p ^= mask; + } + } +} + +void Adafruit_SSD1305::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + //for (int16_t x2 = x + w; x < x2; ++x) + //{ + //drawFastVLine(x, y, h, color); + //} + //return; + if (x < 0) { + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + if (y + h > SSD1305_LCDHEIGHT) { + h = SSD1305_LCDHEIGHT - y; + } + + // check rotation, move pixel around if necessary* + // MegaCommand rotation + y = SSD1305_LCDHEIGHT - y - h; + + // initial pointer position + uint8_t *p = buffer + x + (y / 8) * SSD1305_LCDWIDTH; + const int16_t xend = x + w; + + // 1. upper part + int16_t h_ = 8 - (y & 0x07); + if (h_ != 8) { + // higher bits ON + uint8_t mask = ~(0xFF >> h_); + + if (h_ > h) { + mask = mask & (0xFF >> (h_ - h)); + h_ = h; + } + + uint8_t *px = p; + for (uint8_t x_ = x; x_ < xend; ++x_) { + if (color == WHITE) { + *px |= mask; + } else if (color == BLACK) { + *px &= ~mask; + } else { // INVERT + *px ^= mask; + } + ++px; + } + p += SSD1305_LCDWIDTH; + h -= h_; + } + + // 2. center fast part + while (h >= 8) { + uint8_t *px = p; + for (uint8_t x_ = x; x_ < xend; ++x_) { + if (color == WHITE) { + *px = 0xFF; + } else if (color == BLACK) { + *px = 0x00; + } else { // INVERT + *px = ~*px; + } + ++px; + } + h -= 8; + p += SSD1305_LCDWIDTH; + } + + // 3. lower part + if (h != 0) { + uint8_t mask = ((1 << h) - 1); + uint8_t *px = p; + for (uint8_t x_ = x; x_ < xend; ++x_) { + if (color == WHITE) { + *px |= mask; + } else if (color == BLACK) { + *px &= ~mask; + } else { // INVERT + *px ^= mask; + } + ++px; + } + } +} + +void Adafruit_SSD1305::fillScreen(uint16_t color) { + if (color == BLACK) { + clearDisplay(); + } else if (color == WHITE) { + memset(buffer, 0xFF, (SSD1305_LCDWIDTH * SSD1305_LCDHEIGHT / 8)); + } else { // INVERT + for (uint8_t *p = buffer, + *e = buffer + (SSD1305_LCDWIDTH * SSD1305_LCDHEIGHT / 8); + p != e; ++p) { + *p = ~*p; + } } - else // BLACK - buffer[x+ (y/8)*SSD1305_LCDWIDTH] &= ~_BV((y%8)); } void Adafruit_SSD1305::begin(uint8_t i2caddr) { @@ -161,9 +349,9 @@ void Adafruit_SSD1305::begin(uint8_t i2caddr) { pinMode(sid, OUTPUT); pinMode(sclk, OUTPUT); #ifdef __AVR__ - clkport = portOutputRegister(digitalPinToPort(sclk)); - clkpinmask = digitalPinToBitMask(sclk); - mosiport = portOutputRegister(digitalPinToPort(sid)); + clkport = portOutputRegister(digitalPinToPort(sclk)); + clkpinmask = digitalPinToBitMask(sclk); + mosiport = portOutputRegister(digitalPinToPort(sid)); mosipinmask = digitalPinToBitMask(sid); #endif } else if (cs != -1) { @@ -178,10 +366,10 @@ void Adafruit_SSD1305::begin(uint8_t i2caddr) { pinMode(dc, OUTPUT); pinMode(cs, OUTPUT); } - + if (rst != -1) { pinMode(rst, OUTPUT); - + digitalWrite(rst, HIGH); // VDD (3.3V) goes high at start, lets just chill for a ms delay(1); @@ -195,31 +383,31 @@ void Adafruit_SSD1305::begin(uint8_t i2caddr) { #if defined SSD1305_128_32 // Init sequence for 128x32 OLED module - command(SSD1305_DISPLAYOFF); // 0xAE + command(SSD1305_DISPLAYOFF); // 0xAE command(SSD1305_SETLOWCOLUMN | 0x0); // low col = 0 - command(SSD1305_SETHIGHCOLUMN | 0x0); // hi col = 0 - command(SSD1305_SETSTARTLINE | 0x0); // line #0 - command(0x2E); //?? - command(SSD1305_SETCONTRAST); // 0x81 + command(SSD1305_SETHIGHCOLUMN | 0x0); // hi col = 0 + command(SSD1305_SETSTARTLINE | 0x0); // line #0 + command(0x2E); //?? + command(SSD1305_SETCONTRAST); // 0x81 command(0x32); - command(SSD1305_SETBRIGHTNESS); // 0x82 + command(SSD1305_SETBRIGHTNESS); // 0x82 command(0x80); command(SSD1305_SEGREMAP | 0x00); - command(SSD1305_NORMALDISPLAY); // 0xA6 - command(SSD1305_SETMULTIPLEX); // 0xA8 - command(0x3F); // 1/64 + command(SSD1305_NORMALDISPLAY); // 0xA6 + command(SSD1305_SETMULTIPLEX); // 0xA8 + command(0x3F); // 1/64 command(SSD1305_MASTERCONFIG); command(0x8e); /* external vcc supply */ command(SSD1305_COMSCANDEC); - command(SSD1305_SETDISPLAYOFFSET); // 0xD3 - command(0x40); - command(SSD1305_SETDISPLAYCLOCKDIV); // 0xD5 - command(0xf0); - command(SSD1305_SETAREACOLOR); + command(SSD1305_SETDISPLAYOFFSET); // 0xD3 + command(0x40); + command(SSD1305_SETDISPLAYCLOCKDIV); // 0xD5 + command(0xf0); + command(SSD1305_SETAREACOLOR); command(0x05); - command(SSD1305_SETPRECHARGE); // 0xd9 + command(SSD1305_SETPRECHARGE); // 0xd9 command(0xF1); - command(SSD1305_SETCOMPINS); // 0xDA + command(SSD1305_SETCOMPINS); // 0xDA command(0x12); command(SSD1305_SETLUT); @@ -228,35 +416,35 @@ void Adafruit_SSD1305::begin(uint8_t i2caddr) { command(0x3F); command(0x3F); - #endif +#endif - #if defined SSD1305_128_64 +#if defined SSD1305_128_64 // Init sequence for 128x64 OLED module - command(SSD1305_DISPLAYOFF); // 0xAE + command(SSD1305_DISPLAYOFF); // 0xAE command(SSD1305_SETLOWCOLUMN | 0x4); // low col = 0 - command(SSD1305_SETHIGHCOLUMN | 0x4); // hi col = 0 - command(SSD1305_SETSTARTLINE | 0x0); // line #0 - command(0x2E); //?? - command(SSD1305_SETCONTRAST); // 0x81 + command(SSD1305_SETHIGHCOLUMN | 0x4); // hi col = 0 + command(SSD1305_SETSTARTLINE | 0x0); // line #0 + command(0x2E); //?? + command(SSD1305_SETCONTRAST); // 0x81 command(0x32); - command(SSD1305_SETBRIGHTNESS); // 0x82 + command(SSD1305_SETBRIGHTNESS); // 0x82 command(0x80); command(SSD1305_SEGREMAP | 0x01); - command(SSD1305_NORMALDISPLAY); // 0xA6 - command(SSD1305_SETMULTIPLEX); // 0xA8 - command(0x3F); // 1/64 + command(SSD1305_NORMALDISPLAY); // 0xA6 + command(SSD1305_SETMULTIPLEX); // 0xA8 + command(0x3F); // 1/64 command(SSD1305_MASTERCONFIG); command(0x8e); /* external vcc supply */ command(SSD1305_COMSCANDEC); - command(SSD1305_SETDISPLAYOFFSET); // 0xD3 - command(0x40); - command(SSD1305_SETDISPLAYCLOCKDIV); // 0xD5 - command(0xf0); - command(SSD1305_SETAREACOLOR); + command(SSD1305_SETDISPLAYOFFSET); // 0xD3 + command(0x40); + command(SSD1305_SETDISPLAYCLOCKDIV); // 0xD5 + command(0xf0); + command(SSD1305_SETAREACOLOR); command(0x05); - command(SSD1305_SETPRECHARGE); // 0xd9 + command(SSD1305_SETPRECHARGE); // 0xd9 command(0xF1); - command(SSD1305_SETCOMPINS); // 0xDA + command(SSD1305_SETCOMPINS); // 0xDA command(0x12); command(SSD1305_SETLUT); @@ -264,9 +452,9 @@ void Adafruit_SSD1305::begin(uint8_t i2caddr) { command(0x3F); command(0x3F); command(0x3F); - #endif +#endif - command(SSD1305_DISPLAYON);//--turn on oled panel + command(SSD1305_DISPLAYON); //--turn on oled panel } void Adafruit_SSD1305::invertDisplay(uint8_t i) { @@ -277,7 +465,7 @@ void Adafruit_SSD1305::invertDisplay(uint8_t i) { } } -void Adafruit_SSD1305::command(uint8_t c) { +void Adafruit_SSD1305::command(uint8_t c) { if (cs != -1) { // SPI of sorts @@ -292,18 +480,18 @@ void Adafruit_SSD1305::command(uint8_t c) { SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(ADAFRUIT_SSD1305_SPI); #endif - } + } digitalWrite(cs, LOW); spixfer(c); digitalWrite(cs, HIGH); #ifdef SPI_HAS_TRANSACTION if (sclk == -1) - SPI.endTransaction(); // release the SPI bus + SPI.endTransaction(); // release the SPI bus #endif } else { // I2C - uint8_t control = 0x00; // Co = 0, D/C = 0 + uint8_t control = 0x00; // Co = 0, D/C = 0 Wire.beginTransmission(_i2caddr); Wire.write(control); Wire.write(c); @@ -311,16 +499,14 @@ void Adafruit_SSD1305::command(uint8_t c) { } } -uint8_t Adafruit_SSD1305::getBuffer(uint16_t i) { - return buffer[i]; -} +uint8_t Adafruit_SSD1305::getBuffer(uint16_t i) { return buffer[i]; } void Adafruit_SSD1305::data(uint8_t c) { if (cs != -1) { // SPI of sorts digitalWrite(cs, HIGH); digitalWrite(dc, HIGH); - + if (sclk == -1) { #ifdef SPI_HAS_TRANSACTION SPI.beginTransaction(oledspi); @@ -329,33 +515,34 @@ void Adafruit_SSD1305::data(uint8_t c) { SPI.setClockDivider(ADAFRUIT_SSD1305_SPI); #endif } - + digitalWrite(cs, LOW); spixfer(c); digitalWrite(cs, HIGH); - + #ifdef SPI_HAS_TRANSACTION if (sclk == -1) - SPI.endTransaction(); // release the SPI bus + SPI.endTransaction(); // release the SPI bus #endif } else { // I2C - uint8_t control = 0x40; // Co = 0, D/C = 1 + uint8_t control = 0x40; // Co = 0, D/C = 1 Wire.beginTransmission(_i2caddr); Wire.write(control); Wire.write(c); Wire.endTransmission(); - } + } } void Adafruit_SSD1305::display(void) { - uint16_t i=0; + uint16_t i = 0; uint8_t page; - if (SSD1305_LCDHEIGHT == 64) page = 0; - if (SSD1305_LCDHEIGHT == 32) page = 4; + if (SSD1305_LCDHEIGHT == 64) + page = 0; + if (SSD1305_LCDHEIGHT == 32) + page = 4; - for(; page<8; page++) - { + for (; page < 8; page++) { command(SSD1305_SETPAGESTART + page); command(0x00); command(0x10); @@ -366,19 +553,19 @@ void Adafruit_SSD1305::display(void) { uint8_t twbrbackup = TWBR; TWBR = 12; // upgrade to 400KHz! #endif - - //Serial.println(TWBR, DEC); - //Serial.println(TWSR & 0x3, DEC); - + + // Serial.println(TWBR, DEC); + // Serial.println(TWSR & 0x3, DEC); + // I2C has max 16 bytes per xmision // send a bunch of data in one xmission - for (uint8_t w=0; w<128/16; w++) { - Wire.beginTransmission(_i2caddr); - Wire.write(0x40); - for (uint8_t x=0; x<16; x++) { - Wire.write(buffer[i++]); - } - Wire.endTransmission(); + for (uint8_t w = 0; w < 128 / 16; w++) { + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (uint8_t x = 0; x < 16; x++) { + Wire.write(buffer[i++]); + } + Wire.endTransmission(); } #ifdef __AVR__ @@ -388,25 +575,25 @@ void Adafruit_SSD1305::display(void) { // SPI if (sclk == -1) { #ifdef SPI_HAS_TRANSACTION - SPI.beginTransaction(oledspi); + SPI.beginTransaction(oledspi); #else - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(ADAFRUIT_SSD1305_SPI); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(ADAFRUIT_SSD1305_SPI); #endif } - + digitalWrite(cs, HIGH); digitalWrite(dc, HIGH); digitalWrite(cs, LOW); - - for(uint8_t x=0; x<128; x++) { - spixfer(buffer[i++]); + + for (uint8_t x = 0; x < 128; x++) { + spixfer(buffer[i++]); } - + digitalWrite(cs, HIGH); #ifdef SPI_HAS_TRANSACTION if (sclk == -1) - SPI.endTransaction(); // release the SPI bus + SPI.endTransaction(); // release the SPI bus #endif } } @@ -414,28 +601,31 @@ void Adafruit_SSD1305::display(void) { // clear everything void Adafruit_SSD1305::clearDisplay(void) { - memset(buffer, 0, (SSD1305_LCDWIDTH*SSD1305_LCDHEIGHT/8)); + memset(buffer, 0, (SSD1305_LCDWIDTH * SSD1305_LCDHEIGHT / 8)); } - void Adafruit_SSD1305::spixfer(uint8_t x) { if (sclk == -1) { SPI.transfer(x); return; } // software spi - //Serial.println("Software SPI"); + // Serial.println("Software SPI"); - for(uint8_t bit = 0x80; bit; bit >>= 1) { + for (uint8_t bit = 0x80; bit; bit >>= 1) { #if defined(AVR) *clkport &= ~clkpinmask; - if(x & bit) *mosiport |= mosipinmask; - else *mosiport &= ~mosipinmask; - *clkport |= clkpinmask; + if (x & bit) + *mosiport |= mosipinmask; + else + *mosiport &= ~mosipinmask; + *clkport |= clkpinmask; #else digitalWrite(sclk, LOW); - if(x & bit) digitalWrite(sid, HIGH); - else digitalWrite(sid, LOW); + if (x & bit) + digitalWrite(sid, HIGH); + else + digitalWrite(sid, LOW); digitalWrite(sclk, HIGH); #endif } diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 5c8cee196..67f319234 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -125,6 +125,10 @@ class Adafruit_SSD1305 : public Adafruit_GFX { void drawPixel(int16_t x, int16_t y, uint16_t color); + virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + virtual void fillScreen(uint16_t color); + private: uint8_t _i2caddr; int8_t sid, sclk, dc, rst, cs; From 2146f2275be55231b143bc19321ce903c43f0040 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 1 Nov 2019 17:35:52 +1100 Subject: [PATCH 199/469] add reverse colour trimetric sprites --- art/sprites/trimetric/analog4_rev.png | Bin 0 -> 494 bytes art/sprites/trimetric/mc_rev.png | Bin 0 -> 236 bytes art/sprites/trimetric/md_rev.png | Bin 0 -> 527 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/trimetric/analog4_rev.png create mode 100644 art/sprites/trimetric/mc_rev.png create mode 100644 art/sprites/trimetric/md_rev.png diff --git a/art/sprites/trimetric/analog4_rev.png b/art/sprites/trimetric/analog4_rev.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3a64b6dd3983255e44ec16de1efe642ef1286d GIT binary patch literal 494 zcmVPx$s7XXYR7efAmVpj}FbqV2`2U{_`bZu*DjOQ-64Lf|y&eOJ^E6G{>-x)@=Xu(; zZNJESi{0Ty(m)RHnOTxJDqq+2I0a5b1LUnW)JW?wd}3yZkvjuEs@}=`WTa<+N8n%| zNAG05HnJ?sl<$B;hzw%E$t4cf${*R#%01c?Nv?S1B`&{W)bxF1gcD%Vd-2h`x;0SX&`$~Mv|n6!ia%Y zj{C;k7b8~OTF(B77j&2rl6O-%3e+fHJo~_`-Isql5jXSE*r>UzQ&>SYT0HkFe||M= zej5~qBvHXCfZ61c!Z-4Evm$;vZ0K^m!&sfFe zi(4DLvDCQAPcJKkG*j~wa8NZMN8I`@9)FA!CUO_nRj}gL4yt_dyLcFZn4$<$E9_pa z9RO~R*f;THghmIFr?7*3DcNU%k63R;5XFqA;UEoGe1r)!EQU?b k4<=L{meD+oMaxg=6N%@90A9xmf&c&j07*qoM6N<$g4*ugmjD0& literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/mc_rev.png b/art/sprites/trimetric/mc_rev.png new file mode 100644 index 0000000000000000000000000000000000000000..e6cd1fa94c3f212d63044cebd8a70940f8c17f2e GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?58;~rMGjRk`jKx9jP7LeL$-D$|mU+53hG>Y^ zP6`xiP~dPm{88!N{{Xq8F>fqiWYkPKurE|cd+CCaHxBD_4=a66cK09Uqr~JH`2_M)y1AlDHXPd*I ktnlhHe>a;+zQAAc@Mo>s!S6$60bR`C>FVdQ&MBb@0P-qb6951J literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/md_rev.png b/art/sprites/trimetric/md_rev.png new file mode 100644 index 0000000000000000000000000000000000000000..6782777c90c673cf845ddd2f00418a92740eba72 GIT binary patch literal 527 zcmV+q0`UEbP)Px$$w@>(R7efAmVs`AFbqTi?f*X+#tVDsa3NI(wNxCR&zFN_(#ByJmi70Q7{_r~ zmZkkz_hRZb|J!rZd7gI@80y7Wj9eM$nR(R6)iG$mqdNOCj~MA0U~`WO19GdGTSl%# z^9$sL;F^+S|6?Q5G!5PXMDmRlhm#?mT>9dye|WqL1Q^OeHxrYCmY6e@3kN;{rx|43 zkj^88&qN(fGhn${a$J+UMik%(3^7fKlY2e-%=iM-ggVT!emNQ%X+RMNH<|5O4Jazd zS$W{YXpBIxi(qI*-wew^{?K3VMZp_Nrt_dX8=0^C#T$7WZb#hn}JuZEX z9li(+f;X3-5ZN`HG;2*Z{*@x{ok Date: Fri, 1 Nov 2019 15:03:20 +0800 Subject: [PATCH 200/469] move sprites to cpp --- avr/cores/megacommand/MCL/GridSavePage.cpp | 9 ++-- avr/cores/megacommand/MCL/MCLGUI.cpp | 56 ++++++++++++++++++---- avr/cores/megacommand/MCL/MCLGUI.h | 38 ++++----------- 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 5127443b7..270d84570 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -14,7 +14,7 @@ void GridSavePage::setup() { void GridSavePage::init() { #ifdef OLED_DISPLAY - mcl_gui.draw_popup("SAVE", true, 28); + mcl_gui.draw_popup("SAVE TO GRID", true, 28); #endif } @@ -53,7 +53,8 @@ void GridSavePage::display() { #else void GridSavePage::display() { - oled_display.fillRect(MCLGUI::s_menu_x + 3, MCLGUI::s_menu_y + 20, 98, 7, BLACK); + mcl_gui.clear_popup(28); + mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); const char* merge = "NO"; @@ -61,7 +62,6 @@ void GridSavePage::display() { merge = "YES"; } - oled_display.fillRect(MCLGUI::s_menu_x + 8, MCLGUI::s_menu_y + 5, 18, 16, BLACK); mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 5, "MERGE", merge); char step[4] = {'\0'}; @@ -71,8 +71,7 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, step, 10); - oled_display.fillRect(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, 16, 16, BLACK); - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, "STEP", step); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 5, "STEP", step); oled_display.display(); } #endif diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 33d5a8a8b..9e4c437a0 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -67,8 +67,7 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { oled_display.setFont(&TomThumb); // draw menu body - oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, - BLACK); + oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, BLACK); oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, h, WHITE); oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 4, WHITE); @@ -89,10 +88,11 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { } } -void MCLGUI::clear_popup() { - // XXX too slow - oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, s_menu_h - 5, - BLACK); +void MCLGUI::clear_popup(uint8_t h) { + if (h == 0) { + h = s_menu_h; + } + oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, h - 5, BLACK); } static constexpr uint8_t s_progress_x = 31; @@ -246,8 +246,8 @@ bool MCLGUI::show_encoder_value(Encoder *encoder) { return false; } -void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char* value) -{ +void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, + const char *value) { oled_display.setFont(&TomThumb); oled_display.setTextColor(WHITE); oled_display.setCursor(x + 4, y + 6); @@ -462,8 +462,7 @@ void MCLGUI::draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, oled_display.drawFastHLine(x - 1, y + 2, seq_w + 2, WHITE); } else { // draw top, bottom oled_display.drawFastHLine(x - 1, y, seq_w + 2, WHITE); - oled_display.drawFastHLine(x - 1, y + trig_h - 1, seq_w + 2, - WHITE); + oled_display.drawFastHLine(x - 1, y + trig_h - 1, seq_w + 2, WHITE); } if (noteson > 0 || notesoff > 0) { // left | @@ -571,3 +570,40 @@ void MCLGUI::draw_panel_number(uint8_t number) { } oled_display.print(number); } + +// ================ SPRITES ================ + +const unsigned char encoder_small_0 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, + 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder1', 11x11px +const unsigned char encoder_small_1 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x5c, 0x40, + 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder2', 11x11px +const unsigned char encoder_small_2 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x90, 0x20, 0x58, 0x40, + 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder3', 11x11px +const unsigned char encoder_small_3 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0xb0, 0x20, 0x58, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder4', 11x11px +const unsigned char encoder_small_4 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x58, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder5', 11x11px +const unsigned char encoder_small_5 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x50, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder6', 11x11px +const unsigned char encoder_small_6 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 98af9d5e6..5f35847d8 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -21,7 +21,7 @@ class MCLGUI { void draw_vertical_separator(uint8_t x); void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); /// Clear the content area of a popup - void clear_popup(); + void clear_popup(uint8_t h = 0); void draw_popup(const char* title, bool deferred_display = false, uint8_t h = 0); void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); @@ -129,40 +129,18 @@ const unsigned char encoder_medium_5 [] PROGMEM = { }; */ // 'encoder0', 11x11px -const unsigned char encoder_small_0 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, - 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_0 []; // 'encoder1', 11x11px -const unsigned char encoder_small_1 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x5c, 0x40, - 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_1 []; // 'encoder2', 11x11px -const unsigned char encoder_small_2 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x90, 0x20, 0x58, 0x40, - 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_2 []; // 'encoder3', 11x11px -const unsigned char encoder_small_3 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_3 []; // 'encoder4', 11x11px -const unsigned char encoder_small_4 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_4 []; // 'encoder5', 11x11px -const unsigned char encoder_small_5 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x50, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_5 []; // 'encoder6', 11x11px -const unsigned char encoder_small_6 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; - +extern const unsigned char encoder_small_6 []; #endif /* MCLGUI_H__ */ From 67fe5141191a9300237365e6f0acfaea8fea335e Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 15:37:38 +0800 Subject: [PATCH 201/469] GridSavePage: data flow visualization --- avr/cores/megacommand/MCL/GridSavePage.cpp | 34 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 270d84570..49c7f9097 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -58,11 +58,14 @@ void GridSavePage::display() { mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); const char* merge = "NO"; - if ((MidiClock.state != 2) && (encoders[0]->cur == 1)) { + if (encoders[0]->cur == 1) { merge = "YES"; } + if (MidiClock.state == 2) { + merge = "---"; + } - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 5, "MERGE", merge); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "MERGE", merge); char step[4] = {'\0'}; uint8_t step_count = @@ -71,7 +74,32 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, step, 10); - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 5, "STEP", step); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "STEP", step); + + oled_display.setFont(&TomThumb); + // draw data flow in the center + if (MidiClock.state != 2 && encoders[0]->cur == 1) { + oled_display.setCursor(53, MCLGUI::s_menu_y + 12); + oled_display.print("MD"); + oled_display.setCursor(50, MCLGUI::s_menu_y + 19); + oled_display.print("SEQ"); + + oled_display.fillRect(64, MCLGUI::s_menu_y + 7, 4, 2, WHITE); + oled_display.fillRect(64, MCLGUI::s_menu_y + 15, 4, 2, WHITE); + oled_display.fillRect(68, MCLGUI::s_menu_y + 7, 2, 10, WHITE); + oled_display.fillRect(70, MCLGUI::s_menu_y + 11, 2, 2, WHITE); + oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + } else { + oled_display.setCursor(50, MCLGUI::s_menu_y + 15); + oled_display.print("SEQ"); + oled_display.fillRect(64, MCLGUI::s_menu_y + 11, 8, 2, WHITE); + oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + } + + oled_display.setCursor(74, MCLGUI::s_menu_y + 15); + oled_display.print("GRID"); + + oled_display.display(); } #endif From 7d4b6527e82c0ffc6a2123f8667ed16dc5fa5043 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 16:30:21 +0800 Subject: [PATCH 202/469] chain page popup with data flow visualization --- avr/cores/megacommand/MCL/GridSavePage.cpp | 14 +++--- avr/cores/megacommand/MCL/GridWritePage.cpp | 51 ++++++++++++++++++++- avr/cores/megacommand/MCL/MCLGUI.cpp | 6 +++ avr/cores/megacommand/MCL/MCLGUI.h | 1 + 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 49c7f9097..1ea66da65 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -79,21 +79,19 @@ void GridSavePage::display() { oled_display.setFont(&TomThumb); // draw data flow in the center if (MidiClock.state != 2 && encoders[0]->cur == 1) { - oled_display.setCursor(53, MCLGUI::s_menu_y + 12); + oled_display.setCursor(52, MCLGUI::s_menu_y + 12); oled_display.print("MD"); oled_display.setCursor(50, MCLGUI::s_menu_y + 19); oled_display.print("SEQ"); - oled_display.fillRect(64, MCLGUI::s_menu_y + 7, 4, 2, WHITE); - oled_display.fillRect(64, MCLGUI::s_menu_y + 15, 4, 2, WHITE); - oled_display.fillRect(68, MCLGUI::s_menu_y + 7, 2, 10, WHITE); - oled_display.fillRect(70, MCLGUI::s_menu_y + 11, 2, 2, WHITE); - oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + oled_display.drawFastHLine(63, MCLGUI::s_menu_y + 8, 2, WHITE); + oled_display.drawFastHLine(63, MCLGUI::s_menu_y + 15, 2, WHITE); + oled_display.drawFastVLine(65, MCLGUI::s_menu_y + 8, 8, WHITE); + mcl_gui.draw_horizontal_arrow(66, MCLGUI::s_menu_y + 12, 5); } else { oled_display.setCursor(50, MCLGUI::s_menu_y + 15); oled_display.print("SEQ"); - oled_display.fillRect(64, MCLGUI::s_menu_y + 11, 8, 2, WHITE); - oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + mcl_gui.draw_horizontal_arrow(63, MCLGUI::s_menu_y + 12, 8); } oled_display.setCursor(74, MCLGUI::s_menu_y + 15); diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index ba80e7793..bbd56a332 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -23,12 +23,16 @@ void GridWritePage::setup() { // GUI.display(); curpage = W_PAGE; } + void GridWritePage::init() { #ifdef OLED_DISPLAY - oled_display.clearDisplay(); + mcl_gui.draw_popup("CHAIN FROM GRID", true, 28); #endif } + void GridWritePage::cleanup() {} + +#ifndef OLED_DISPLAY void GridWritePage::display() { GUI.setLine(GUI.LINE1); @@ -82,6 +86,51 @@ void GridWritePage::display() { GUI.put_value_at2(11, x); } } +#else +void GridWritePage::display() { + + mcl_gui.clear_popup(28); + + mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); + + char K[4] = { '\0' }; + + // draw step count + uint8_t step_count = + (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - + (64 * + ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); + itoa(step_count, K, 10); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "STEP", K); + + // draw quantize + strcpy(K, "---"); + if ((encoders[3]->getValue() < 7) && (encoders[3]->getValue() > 0)) { + uint8_t x = 1 << encoders[3]->getValue(); + itoa(x, K, 10); + } + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "QUANT", K); + + oled_display.setFont(&TomThumb); + // draw data flow in the center + oled_display.setCursor(48, MCLGUI::s_menu_y + 12); + oled_display.print("SND"); + oled_display.setCursor(46, MCLGUI::s_menu_y + 19); + oled_display.print("GRID"); + + mcl_gui.draw_horizontal_arrow(63, MCLGUI::s_menu_y + 8, 5); + mcl_gui.draw_horizontal_arrow(63, MCLGUI::s_menu_y + 15, 5); + + oled_display.setCursor(74, MCLGUI::s_menu_y + 12); + oled_display.print("MD"); + oled_display.setCursor(74, MCLGUI::s_menu_y + 19); + oled_display.print("SEQ"); + + oled_display.display(); +} + +#endif + bool GridWritePage::handleEvent(gui_event_t *event) { // Call parent GUI handler first. if (GridIOPage::handleEvent(event)) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 9e4c437a0..d8251db04 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -32,6 +32,12 @@ void MCLGUI::draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to) { } } +void MCLGUI::draw_horizontal_arrow(uint8_t x, uint8_t y, uint8_t w) { + oled_display.drawFastHLine(x, y, w, WHITE); + oled_display.drawFastVLine(x + w - 2, y - 1, 3, WHITE); + oled_display.drawFastVLine(x + w - 3, y - 2, 5, WHITE); +} + void MCLGUI::draw_vertical_separator(uint8_t x) { auto x_ = x + 2; for (uint8_t y = 0; y < 32; y += 2) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 5f35847d8..fd28d64da 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -16,6 +16,7 @@ class MCLGUI { bool wait_for_input(char *dst, const char *title, uint8_t len); void draw_vertical_dashline(uint8_t x, uint8_t from = 1, uint8_t to = 32); void draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to); + void draw_horizontal_arrow(uint8_t x, uint8_t y, uint8_t w); bool wait_for_confirm(const char *title, const char* text); void draw_infobox(const char* line1, const char* line2, const int line2_offset = 0); void draw_vertical_separator(uint8_t x); From f951dd52db4f8ba51bb34f61b0bbbe043f861bf9 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 20:59:58 +0800 Subject: [PATCH 203/469] popup layout adjust. trig visual feedback adjust. --- avr/cores/megacommand/MCL/MCLGUI.cpp | 22 ++++++------- avr/cores/megacommand/MCL/PageSelectPage.cpp | 7 ++-- avr/cores/megacommand/MCL/PageSelectPage.h | 34 +++++++++++--------- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index d8251db04..884f7a1e3 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -73,20 +73,20 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { oled_display.setFont(&TomThumb); // draw menu body - oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, BLACK); + oled_display.fillRect(s_menu_x - 1, s_menu_y - 2, s_menu_w + 2, h + 2, BLACK); oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, h, WHITE); - oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 4, WHITE); + oled_display.fillRect(s_menu_x + 1, s_menu_y - 1, s_menu_w - 2, 6, WHITE); // draw the title '____/**********\____' part - oled_display.drawRect(s_title_x, s_menu_y - 3, s_title_w, 3, BLACK); - oled_display.drawRect(s_title_x, s_menu_y - 2, s_title_w, 2, WHITE); - oled_display.drawPixel(s_title_x, s_menu_y - 2, BLACK); - oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 2, BLACK); + oled_display.drawRect(s_title_x, s_menu_y - 4, s_title_w, 4, BLACK); + oled_display.fillRect(s_title_x, s_menu_y - 3, s_title_w, 3, WHITE); + oled_display.drawPixel(s_title_x, s_menu_y - 3, BLACK); + oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 3, BLACK); oled_display.setTextColor(BLACK); - // auto len = strlen(title_buf) * 5; - // oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); - oled_display.setCursor(s_title_x + 2, s_menu_y + 4); + auto len = strlen(title_buf) * 4; + oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); + //oled_display.setCursor(s_title_x + 2, s_menu_y + 3); oled_display.println(title_buf); oled_display.setTextColor(WHITE); if (!deferred_display) { @@ -416,7 +416,7 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(x - 1, y, seq_w + 2, trig_h + 1, WHITE); + oled_display.fillRect(x + 1, y + 1, seq_w - 2, trig_h - 2, WHITE); } else if (!in_range) { // don't draw } else { @@ -501,7 +501,7 @@ void MCLGUI::draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(x - 1, y - 1, seq_w + 2, led_h + 1, WHITE); + oled_display.fillRect(x, y, seq_w, led_h, WHITE); } else if (!in_range) { // don't draw } else if (current ^ locked) { diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 788d63fc0..df6b68ebb 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -5,12 +5,12 @@ #define ROUTE_PAGE 1 #define RAM_PAGE_A 14 #define RAM_PAGE_B 15 -#define WAVD_PAGE 8 +#define LFO_PAGE 6 #define SOUND 7 +#define WAVD_PAGE 8 +#define LOUDNESS 9 #define FX_PAGE_A 10 #define FX_PAGE_B 11 -#define LOUDNESS 9 -#define LFO_PAGE 6 void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -107,6 +107,7 @@ uint8_t PageSelectPage::get_nextpage_up() { } return page_select; } + void PageSelectPage::loop() { MCLEncoder *enc_ = &enc1; // largest_sine_peak = 1.0 / 16.00; diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index aa1e92636..aba5cc90f 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -6,21 +6,25 @@ #include "GUI.h" class PageSelectPage : public LightPage { - public: - MCLEncoder enc1; - uint8_t page_select; - PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage( e1, e2, e3 ,e4) { - encoders[0] = &enc1; - } - void display(); - void setup(); - void init(); - void loop(); - void cleanup(); - LightPage *get_page(uint8_t page_number, char *str = NULL); - uint8_t get_nextpage_down(); - uint8_t get_nextpage_up(); - virtual bool handleEvent(gui_event_t *event); +public: + MCLEncoder enc1; + uint8_t page_select; + PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, + Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) { + encoders[0] = &enc1; + } + + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void loop(); + virtual void cleanup(); + virtual bool handleEvent(gui_event_t *event); + + LightPage *get_page(uint8_t page_number, char *str = NULL); + uint8_t get_nextpage_down(); + uint8_t get_nextpage_up(); }; extern PageSelectPage page_select_page; From f1ed51798fa85af8a026cd95effe056160c44518 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 2 Nov 2019 01:29:56 +0800 Subject: [PATCH 204/469] new page select implementation wip --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 138 ++++++++----------- 1 file changed, 61 insertions(+), 77 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index df6b68ebb..1e65cae46 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -1,16 +1,53 @@ #include "MCL.h" #include "PageSelectPage.h" +#include -#define MIX_PAGE 0 -#define ROUTE_PAGE 1 -#define RAM_PAGE_A 14 -#define RAM_PAGE_B 15 -#define LFO_PAGE 6 -#define SOUND 7 -#define WAVD_PAGE 8 -#define LOUDNESS 9 -#define FX_PAGE_A 10 -#define FX_PAGE_B 11 +struct PageCategory { + char Name[16]; + uint8_t PageCount; + uint8_t FirstPage; +}; + +struct PageSelectEntry { + char Name[16]; + LightPage *Page; + uint8_t PageNumber; // same as trig id + uint8_t CategoryId; +}; + +const PageCategory Categories[] PROGMEM = { + {"GRID", 1, 0}, {"SEQ", 4, 1}, {"MIX", 3, 5}, {"SOUND", 2, 8}, + {"FX", 2, 10}, {"RAM", 2, 12}, {"LFO", 1, 14}, {"CONFIG", 1, 15}, +}; + +const PageSelectEntry Entries[] PROGMEM = { + {"GRID", &grid_page, 0xFF, 0}, + + {"NOTES", &seq_step_page, 0, 1}, + {"RECORD", &seq_rtrk_page, 1, 1}, + {"LOCKS", &seq_param_page[0], 2, 1}, + {"CHROMA", &seq_ptc_page, 3, 1}, + + {"MIXER", &mixer_page, 4, 2}, + {"ROUTE", &route_page, 5, 2}, + {"LOUDNESS", &loudness_page, 6, 2}, + + {"WAV DESIGNER", &wd.pages[0], 7, 3}, + {"SOUND MANAGER", &sound_browser, 8, 3}, + + {"DELAY", &fx_page_a, 9, 4}, + {"REVERB", &fx_page_b, 10, 4}, + + {"RAM-1", &ram_page_a, 11, 5}, + {"RAM-2", &fx_page_b, 12, 5}, + + {"LFO", &lfo_page, 13, 6}, + + {"CONFIG", &system_page, 0xFF, 7}, +}; + +constexpr uint8_t n_category = sizeof(Categories) / sizeof(PageCategory); +constexpr uint8_t n_entry = sizeof(Entries) / sizeof(PageSelectEntry); void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -20,74 +57,21 @@ void PageSelectPage::init() { md_exploit.on(); note_interface.state = true; } -void PageSelectPage::cleanup() { - note_interface.init_notes(); -} +void PageSelectPage::cleanup() { note_interface.init_notes(); } LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { - LightPage *r_page = NULL; - switch (page_number) { - case MIX_PAGE: - if (str) - strncpy(str, "MIX", 4); - r_page = &mixer_page; - break; - case ROUTE_PAGE: - if (str) - strncpy(str, "ROUTE", 6); - r_page = &route_page; - break; - case RAM_PAGE_A: - if (str) - strncpy(str, "RAM 1", 6); - r_page = &ram_page_a; - break; - case RAM_PAGE_B: - if (str) - strncpy(str, "RAM 2", 6); - r_page = &ram_page_b; - break; - case FX_PAGE_A: - if (str) - strncpy(str, "DELAY", 8); - r_page = &fx_page_a; - break; - case FX_PAGE_B: - if (str) - strncpy(str, "REVERB", 8); - r_page = &fx_page_b; - break; - case LFO_PAGE: - if (str) - strncpy(str, "LFO", 8); - r_page = &lfo_page; - break; -#ifdef WAV_DESIGNER - case WAVD_PAGE: - if (str) - strncpy(str, "WAV DESIGNER", 13); - r_page = wd.last_page; - break; -#endif -#ifdef SOUND_PAGE - case SOUND: - if (str) - strncpy(str, "SOUND", 6); - r_page = &sound_browser; - break; -#endif -#ifdef LOUDNESS_PAGE - case LOUDNESS: - if (str) - strncpy(str, "LOUDNESS",9); - r_page = &loudness_page; - break; -#endif - default: - if (str) - strncpy(str, "----", 5); + for (uint8_t i = 0; i < n_entry; ++i) { + if (page_number == pgm_read_byte(&Entries[i].PageNumber)) { + if (str) { + m_strncpy_p(str, (PGM_P) & (Entries[i].Name), 16); + } + return pgm_read_word(&Entries[i].Page); + } + } + if (str) { + strncpy(str, "----", 5); } - return r_page; + return NULL; } uint8_t PageSelectPage::get_nextpage_down() { @@ -126,9 +110,9 @@ void PageSelectPage::loop() { } void PageSelectPage::display() { - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY oled_display.clearDisplay(); - #endif +#endif GUI.setLine(GUI.LINE1); char str[16]; get_page(page_select, str); From f841442ac6479991f767dcc15aae43be9d148bcd Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 01:07:06 +0800 Subject: [PATCH 205/469] save page on a dialog --- avr/cores/megacommand/MCL/GridSavePage.cpp | 35 ++++++++++++++++++---- avr/cores/megacommand/MCL/MCLGUI.cpp | 21 +++++++++++-- avr/cores/megacommand/MCL/MCLGUI.h | 5 ++-- avr/cores/megacommand/MCL/SeqPage.cpp | 9 +----- avr/cores/megacommand/MCL/SeqPage.h | 1 + 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index e4679bce3..5127443b7 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -12,14 +12,15 @@ void GridSavePage::setup() { grid_page.reload_slot_models = false; } -void GridSavePage::init() { +void GridSavePage::init() { #ifdef OLED_DISPLAY - oled_display.clearDisplay(); + mcl_gui.draw_popup("SAVE", true, 28); #endif } void GridSavePage::cleanup() {} +#ifndef OLED_DISPLAY void GridSavePage::display() { GUI.setLine(GUI.LINE1); char strn[17] = "----------------"; @@ -28,11 +29,7 @@ void GridSavePage::display() { if (note_interface.notes[i] != 0) { -#ifdef OLED_DISPLAY - strn[i] = (char)2; -#else strn[i] = (char)219; -#endif } } @@ -53,6 +50,32 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); GUI.put_value_at2(14, step_count); } +#else +void GridSavePage::display() { + + oled_display.fillRect(MCLGUI::s_menu_x + 3, MCLGUI::s_menu_y + 20, 98, 7, BLACK); + mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); + + const char* merge = "NO"; + if ((MidiClock.state != 2) && (encoders[0]->cur == 1)) { + merge = "YES"; + } + + oled_display.fillRect(MCLGUI::s_menu_x + 8, MCLGUI::s_menu_y + 5, 18, 16, BLACK); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 5, "MERGE", merge); + + char step[4] = {'\0'}; + uint8_t step_count = + (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - + (64 * + ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); + itoa(step_count, step, 10); + + oled_display.fillRect(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, 16, 16, BLACK); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, "STEP", step); + oled_display.display(); +} +#endif bool GridSavePage::handleEvent(gui_event_t *event) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index a1e2e8cd6..33d5a8a8b 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -56,17 +56,20 @@ void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, static char title_buf[16]; // ref: Design/popup_menu.png -void MCLGUI::draw_popup(const char *title, bool deferred_display) { +void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { strcpy(title_buf, title); m_toupper(title_buf); + if (h == 0) { + h = s_menu_h; + } oled_display.setFont(&TomThumb); // draw menu body - oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, s_menu_h + 2, + oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, BLACK); - oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, s_menu_h, WHITE); + oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, h, WHITE); oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 4, WHITE); // draw the title '____/**********\____' part @@ -243,6 +246,18 @@ bool MCLGUI::show_encoder_value(Encoder *encoder) { return false; } +void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char* value) +{ + oled_display.setFont(&TomThumb); + oled_display.setTextColor(WHITE); + oled_display.setCursor(x + 4, y + 6); + oled_display.print(name); + + oled_display.setFont(); + oled_display.setCursor(x + 4, y + 8); + oled_display.print(value); +} + void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name) { bool show_value = show_encoder_value(encoder); diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 79f66f946..98af9d5e6 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -22,7 +22,7 @@ class MCLGUI { void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); /// Clear the content area of a popup void clear_popup(); - void draw_popup(const char* title, bool deferred_display = false); + void draw_popup(const char* title, bool deferred_display = false, uint8_t h = 0); void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); void clear_leftpane(); @@ -33,6 +33,7 @@ class MCLGUI { bool show_encoder_value(Encoder *encoder); + void draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char* value); void draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); void draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); @@ -51,7 +52,7 @@ class MCLGUI { static constexpr uint8_t led_h = 3; static constexpr uint8_t trig_h = 5; - static constexpr uint8_t s_menu_w = 96; + static constexpr uint8_t s_menu_w = 104; static constexpr uint8_t s_menu_h = 24; static constexpr uint8_t s_menu_x = (128 - s_menu_w) / 2; static constexpr uint8_t s_menu_y = (32 - s_menu_h) / 2; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index ee3672531..3e2b095d3 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -625,14 +625,7 @@ void SeqPage::draw_knob_frame() { void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { uint8_t x = knob_x0 + i * knob_w; - oled_display.setFont(&TomThumb); - oled_display.setTextColor(WHITE); - oled_display.setCursor(x + 4, 7); - oled_display.print(title); - - oled_display.setFont(); - oled_display.setCursor(x + 4, 9); - oled_display.print(text); + mcl_gui.draw_text_encoder(x, knob_y0, title, text); } void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* title) { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index e52cc77b5..a47b72332 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -68,6 +68,7 @@ class SeqPage : public LightPage { static constexpr uint8_t knob_x0 = 31; static constexpr uint8_t knob_w = 24; static constexpr uint8_t knob_xend = 127; + static constexpr uint8_t knob_y0 = 1; static constexpr uint8_t knob_y = 20; }; From 2b1c5aac30851c64b2038ed01c10da205c88de20 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 15:03:20 +0800 Subject: [PATCH 206/469] move sprites to cpp --- avr/cores/megacommand/MCL/GridSavePage.cpp | 9 ++-- avr/cores/megacommand/MCL/MCLGUI.cpp | 56 ++++++++++++++++++---- avr/cores/megacommand/MCL/MCLGUI.h | 38 ++++----------- 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 5127443b7..270d84570 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -14,7 +14,7 @@ void GridSavePage::setup() { void GridSavePage::init() { #ifdef OLED_DISPLAY - mcl_gui.draw_popup("SAVE", true, 28); + mcl_gui.draw_popup("SAVE TO GRID", true, 28); #endif } @@ -53,7 +53,8 @@ void GridSavePage::display() { #else void GridSavePage::display() { - oled_display.fillRect(MCLGUI::s_menu_x + 3, MCLGUI::s_menu_y + 20, 98, 7, BLACK); + mcl_gui.clear_popup(28); + mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); const char* merge = "NO"; @@ -61,7 +62,6 @@ void GridSavePage::display() { merge = "YES"; } - oled_display.fillRect(MCLGUI::s_menu_x + 8, MCLGUI::s_menu_y + 5, 18, 16, BLACK); mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 5, "MERGE", merge); char step[4] = {'\0'}; @@ -71,8 +71,7 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, step, 10); - oled_display.fillRect(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, 16, 16, BLACK); - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 24, MCLGUI::s_menu_y + 5, "STEP", step); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 5, "STEP", step); oled_display.display(); } #endif diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 33d5a8a8b..9e4c437a0 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -67,8 +67,7 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { oled_display.setFont(&TomThumb); // draw menu body - oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, - BLACK); + oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, BLACK); oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, h, WHITE); oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 4, WHITE); @@ -89,10 +88,11 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { } } -void MCLGUI::clear_popup() { - // XXX too slow - oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, s_menu_h - 5, - BLACK); +void MCLGUI::clear_popup(uint8_t h) { + if (h == 0) { + h = s_menu_h; + } + oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, h - 5, BLACK); } static constexpr uint8_t s_progress_x = 31; @@ -246,8 +246,8 @@ bool MCLGUI::show_encoder_value(Encoder *encoder) { return false; } -void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char* value) -{ +void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, + const char *value) { oled_display.setFont(&TomThumb); oled_display.setTextColor(WHITE); oled_display.setCursor(x + 4, y + 6); @@ -462,8 +462,7 @@ void MCLGUI::draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, oled_display.drawFastHLine(x - 1, y + 2, seq_w + 2, WHITE); } else { // draw top, bottom oled_display.drawFastHLine(x - 1, y, seq_w + 2, WHITE); - oled_display.drawFastHLine(x - 1, y + trig_h - 1, seq_w + 2, - WHITE); + oled_display.drawFastHLine(x - 1, y + trig_h - 1, seq_w + 2, WHITE); } if (noteson > 0 || notesoff > 0) { // left | @@ -571,3 +570,40 @@ void MCLGUI::draw_panel_number(uint8_t number) { } oled_display.print(number); } + +// ================ SPRITES ================ + +const unsigned char encoder_small_0 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, + 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder1', 11x11px +const unsigned char encoder_small_1 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x5c, 0x40, + 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder2', 11x11px +const unsigned char encoder_small_2 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x90, 0x20, 0x58, 0x40, + 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder3', 11x11px +const unsigned char encoder_small_3 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0xb0, 0x20, 0x58, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder4', 11x11px +const unsigned char encoder_small_4 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x58, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder5', 11x11px +const unsigned char encoder_small_5 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x50, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; +// 'encoder6', 11x11px +const unsigned char encoder_small_6 [] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, + 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 +}; diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 98af9d5e6..5f35847d8 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -21,7 +21,7 @@ class MCLGUI { void draw_vertical_separator(uint8_t x); void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); /// Clear the content area of a popup - void clear_popup(); + void clear_popup(uint8_t h = 0); void draw_popup(const char* title, bool deferred_display = false, uint8_t h = 0); void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); @@ -129,40 +129,18 @@ const unsigned char encoder_medium_5 [] PROGMEM = { }; */ // 'encoder0', 11x11px -const unsigned char encoder_small_0 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, - 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_0 []; // 'encoder1', 11x11px -const unsigned char encoder_small_1 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x5c, 0x40, - 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_1 []; // 'encoder2', 11x11px -const unsigned char encoder_small_2 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x90, 0x20, 0x58, 0x40, - 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_2 []; // 'encoder3', 11x11px -const unsigned char encoder_small_3 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_3 []; // 'encoder4', 11x11px -const unsigned char encoder_small_4 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_4 []; // 'encoder5', 11x11px -const unsigned char encoder_small_5 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x50, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +extern const unsigned char encoder_small_5 []; // 'encoder6', 11x11px -const unsigned char encoder_small_6 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; - +extern const unsigned char encoder_small_6 []; #endif /* MCLGUI_H__ */ From b3b5240d5b7585855e2104ec83ce09a48011527e Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 15:37:38 +0800 Subject: [PATCH 207/469] GridSavePage: data flow visualization --- avr/cores/megacommand/MCL/GridSavePage.cpp | 34 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 270d84570..49c7f9097 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -58,11 +58,14 @@ void GridSavePage::display() { mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); const char* merge = "NO"; - if ((MidiClock.state != 2) && (encoders[0]->cur == 1)) { + if (encoders[0]->cur == 1) { merge = "YES"; } + if (MidiClock.state == 2) { + merge = "---"; + } - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 5, "MERGE", merge); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "MERGE", merge); char step[4] = {'\0'}; uint8_t step_count = @@ -71,7 +74,32 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, step, 10); - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 5, "STEP", step); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "STEP", step); + + oled_display.setFont(&TomThumb); + // draw data flow in the center + if (MidiClock.state != 2 && encoders[0]->cur == 1) { + oled_display.setCursor(53, MCLGUI::s_menu_y + 12); + oled_display.print("MD"); + oled_display.setCursor(50, MCLGUI::s_menu_y + 19); + oled_display.print("SEQ"); + + oled_display.fillRect(64, MCLGUI::s_menu_y + 7, 4, 2, WHITE); + oled_display.fillRect(64, MCLGUI::s_menu_y + 15, 4, 2, WHITE); + oled_display.fillRect(68, MCLGUI::s_menu_y + 7, 2, 10, WHITE); + oled_display.fillRect(70, MCLGUI::s_menu_y + 11, 2, 2, WHITE); + oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + } else { + oled_display.setCursor(50, MCLGUI::s_menu_y + 15); + oled_display.print("SEQ"); + oled_display.fillRect(64, MCLGUI::s_menu_y + 11, 8, 2, WHITE); + oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + } + + oled_display.setCursor(74, MCLGUI::s_menu_y + 15); + oled_display.print("GRID"); + + oled_display.display(); } #endif From 4c264dc8f19220172d7aa42f49983c63826f0e83 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 16:30:21 +0800 Subject: [PATCH 208/469] chain page popup with data flow visualization --- avr/cores/megacommand/MCL/GridSavePage.cpp | 14 +++--- avr/cores/megacommand/MCL/GridWritePage.cpp | 51 ++++++++++++++++++++- avr/cores/megacommand/MCL/MCLGUI.cpp | 6 +++ avr/cores/megacommand/MCL/MCLGUI.h | 1 + 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 49c7f9097..1ea66da65 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -79,21 +79,19 @@ void GridSavePage::display() { oled_display.setFont(&TomThumb); // draw data flow in the center if (MidiClock.state != 2 && encoders[0]->cur == 1) { - oled_display.setCursor(53, MCLGUI::s_menu_y + 12); + oled_display.setCursor(52, MCLGUI::s_menu_y + 12); oled_display.print("MD"); oled_display.setCursor(50, MCLGUI::s_menu_y + 19); oled_display.print("SEQ"); - oled_display.fillRect(64, MCLGUI::s_menu_y + 7, 4, 2, WHITE); - oled_display.fillRect(64, MCLGUI::s_menu_y + 15, 4, 2, WHITE); - oled_display.fillRect(68, MCLGUI::s_menu_y + 7, 2, 10, WHITE); - oled_display.fillRect(70, MCLGUI::s_menu_y + 11, 2, 2, WHITE); - oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + oled_display.drawFastHLine(63, MCLGUI::s_menu_y + 8, 2, WHITE); + oled_display.drawFastHLine(63, MCLGUI::s_menu_y + 15, 2, WHITE); + oled_display.drawFastVLine(65, MCLGUI::s_menu_y + 8, 8, WHITE); + mcl_gui.draw_horizontal_arrow(66, MCLGUI::s_menu_y + 12, 5); } else { oled_display.setCursor(50, MCLGUI::s_menu_y + 15); oled_display.print("SEQ"); - oled_display.fillRect(64, MCLGUI::s_menu_y + 11, 8, 2, WHITE); - oled_display.drawPixel(71, MCLGUI::s_menu_y + 11, BLACK); + mcl_gui.draw_horizontal_arrow(63, MCLGUI::s_menu_y + 12, 8); } oled_display.setCursor(74, MCLGUI::s_menu_y + 15); diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index ba80e7793..bbd56a332 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -23,12 +23,16 @@ void GridWritePage::setup() { // GUI.display(); curpage = W_PAGE; } + void GridWritePage::init() { #ifdef OLED_DISPLAY - oled_display.clearDisplay(); + mcl_gui.draw_popup("CHAIN FROM GRID", true, 28); #endif } + void GridWritePage::cleanup() {} + +#ifndef OLED_DISPLAY void GridWritePage::display() { GUI.setLine(GUI.LINE1); @@ -82,6 +86,51 @@ void GridWritePage::display() { GUI.put_value_at2(11, x); } } +#else +void GridWritePage::display() { + + mcl_gui.clear_popup(28); + + mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); + + char K[4] = { '\0' }; + + // draw step count + uint8_t step_count = + (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - + (64 * + ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); + itoa(step_count, K, 10); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "STEP", K); + + // draw quantize + strcpy(K, "---"); + if ((encoders[3]->getValue() < 7) && (encoders[3]->getValue() > 0)) { + uint8_t x = 1 << encoders[3]->getValue(); + itoa(x, K, 10); + } + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "QUANT", K); + + oled_display.setFont(&TomThumb); + // draw data flow in the center + oled_display.setCursor(48, MCLGUI::s_menu_y + 12); + oled_display.print("SND"); + oled_display.setCursor(46, MCLGUI::s_menu_y + 19); + oled_display.print("GRID"); + + mcl_gui.draw_horizontal_arrow(63, MCLGUI::s_menu_y + 8, 5); + mcl_gui.draw_horizontal_arrow(63, MCLGUI::s_menu_y + 15, 5); + + oled_display.setCursor(74, MCLGUI::s_menu_y + 12); + oled_display.print("MD"); + oled_display.setCursor(74, MCLGUI::s_menu_y + 19); + oled_display.print("SEQ"); + + oled_display.display(); +} + +#endif + bool GridWritePage::handleEvent(gui_event_t *event) { // Call parent GUI handler first. if (GridIOPage::handleEvent(event)) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 9e4c437a0..d8251db04 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -32,6 +32,12 @@ void MCLGUI::draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to) { } } +void MCLGUI::draw_horizontal_arrow(uint8_t x, uint8_t y, uint8_t w) { + oled_display.drawFastHLine(x, y, w, WHITE); + oled_display.drawFastVLine(x + w - 2, y - 1, 3, WHITE); + oled_display.drawFastVLine(x + w - 3, y - 2, 5, WHITE); +} + void MCLGUI::draw_vertical_separator(uint8_t x) { auto x_ = x + 2; for (uint8_t y = 0; y < 32; y += 2) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 5f35847d8..fd28d64da 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -16,6 +16,7 @@ class MCLGUI { bool wait_for_input(char *dst, const char *title, uint8_t len); void draw_vertical_dashline(uint8_t x, uint8_t from = 1, uint8_t to = 32); void draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to); + void draw_horizontal_arrow(uint8_t x, uint8_t y, uint8_t w); bool wait_for_confirm(const char *title, const char* text); void draw_infobox(const char* line1, const char* line2, const int line2_offset = 0); void draw_vertical_separator(uint8_t x); From 25ed55bb29eb3eae6c94d6919fbf1ce6f5afc94d Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 1 Nov 2019 20:59:58 +0800 Subject: [PATCH 209/469] popup layout adjust. trig visual feedback adjust. --- avr/cores/megacommand/MCL/MCLGUI.cpp | 22 ++++++------- avr/cores/megacommand/MCL/PageSelectPage.cpp | 7 ++-- avr/cores/megacommand/MCL/PageSelectPage.h | 34 +++++++++++--------- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index d8251db04..884f7a1e3 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -73,20 +73,20 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { oled_display.setFont(&TomThumb); // draw menu body - oled_display.fillRect(s_menu_x - 1, s_menu_y - 1, s_menu_w + 2, h + 2, BLACK); + oled_display.fillRect(s_menu_x - 1, s_menu_y - 2, s_menu_w + 2, h + 2, BLACK); oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, h, WHITE); - oled_display.fillRect(s_menu_x + 1, s_menu_y + 1, s_menu_w - 2, 4, WHITE); + oled_display.fillRect(s_menu_x + 1, s_menu_y - 1, s_menu_w - 2, 6, WHITE); // draw the title '____/**********\____' part - oled_display.drawRect(s_title_x, s_menu_y - 3, s_title_w, 3, BLACK); - oled_display.drawRect(s_title_x, s_menu_y - 2, s_title_w, 2, WHITE); - oled_display.drawPixel(s_title_x, s_menu_y - 2, BLACK); - oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 2, BLACK); + oled_display.drawRect(s_title_x, s_menu_y - 4, s_title_w, 4, BLACK); + oled_display.fillRect(s_title_x, s_menu_y - 3, s_title_w, 3, WHITE); + oled_display.drawPixel(s_title_x, s_menu_y - 3, BLACK); + oled_display.drawPixel(s_title_x + s_title_w - 1, s_menu_y - 3, BLACK); oled_display.setTextColor(BLACK); - // auto len = strlen(title_buf) * 5; - // oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); - oled_display.setCursor(s_title_x + 2, s_menu_y + 4); + auto len = strlen(title_buf) * 4; + oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); + //oled_display.setCursor(s_title_x + 2, s_menu_y + 3); oled_display.println(title_buf); oled_display.setTextColor(WHITE); if (!deferred_display) { @@ -416,7 +416,7 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(x - 1, y, seq_w + 2, trig_h + 1, WHITE); + oled_display.fillRect(x + 1, y + 1, seq_w - 2, trig_h - 2, WHITE); } else if (!in_range) { // don't draw } else { @@ -501,7 +501,7 @@ void MCLGUI::draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(x - 1, y - 1, seq_w + 2, led_h + 1, WHITE); + oled_display.fillRect(x, y, seq_w, led_h, WHITE); } else if (!in_range) { // don't draw } else if (current ^ locked) { diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 788d63fc0..df6b68ebb 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -5,12 +5,12 @@ #define ROUTE_PAGE 1 #define RAM_PAGE_A 14 #define RAM_PAGE_B 15 -#define WAVD_PAGE 8 +#define LFO_PAGE 6 #define SOUND 7 +#define WAVD_PAGE 8 +#define LOUDNESS 9 #define FX_PAGE_A 10 #define FX_PAGE_B 11 -#define LOUDNESS 9 -#define LFO_PAGE 6 void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -107,6 +107,7 @@ uint8_t PageSelectPage::get_nextpage_up() { } return page_select; } + void PageSelectPage::loop() { MCLEncoder *enc_ = &enc1; // largest_sine_peak = 1.0 / 16.00; diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index aa1e92636..aba5cc90f 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -6,21 +6,25 @@ #include "GUI.h" class PageSelectPage : public LightPage { - public: - MCLEncoder enc1; - uint8_t page_select; - PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage( e1, e2, e3 ,e4) { - encoders[0] = &enc1; - } - void display(); - void setup(); - void init(); - void loop(); - void cleanup(); - LightPage *get_page(uint8_t page_number, char *str = NULL); - uint8_t get_nextpage_down(); - uint8_t get_nextpage_up(); - virtual bool handleEvent(gui_event_t *event); +public: + MCLEncoder enc1; + uint8_t page_select; + PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, + Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) { + encoders[0] = &enc1; + } + + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void loop(); + virtual void cleanup(); + virtual bool handleEvent(gui_event_t *event); + + LightPage *get_page(uint8_t page_number, char *str = NULL); + uint8_t get_nextpage_down(); + uint8_t get_nextpage_up(); }; extern PageSelectPage page_select_page; From aa82bd7a5efdc7cf0548bad20a706547ab9b121c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 2 Nov 2019 18:14:46 +1100 Subject: [PATCH 210/469] Revert page select changes --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 7 ++-- avr/cores/megacommand/MCL/PageSelectPage.h | 34 +++++++++----------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index df6b68ebb..788d63fc0 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -5,12 +5,12 @@ #define ROUTE_PAGE 1 #define RAM_PAGE_A 14 #define RAM_PAGE_B 15 -#define LFO_PAGE 6 -#define SOUND 7 #define WAVD_PAGE 8 -#define LOUDNESS 9 +#define SOUND 7 #define FX_PAGE_A 10 #define FX_PAGE_B 11 +#define LOUDNESS 9 +#define LFO_PAGE 6 void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -107,7 +107,6 @@ uint8_t PageSelectPage::get_nextpage_up() { } return page_select; } - void PageSelectPage::loop() { MCLEncoder *enc_ = &enc1; // largest_sine_peak = 1.0 / 16.00; diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index aba5cc90f..aa1e92636 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -6,25 +6,21 @@ #include "GUI.h" class PageSelectPage : public LightPage { -public: - MCLEncoder enc1; - uint8_t page_select; - PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, - Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4) { - encoders[0] = &enc1; - } - - virtual void display(); - virtual void setup(); - virtual void init(); - virtual void loop(); - virtual void cleanup(); - virtual bool handleEvent(gui_event_t *event); - - LightPage *get_page(uint8_t page_number, char *str = NULL); - uint8_t get_nextpage_down(); - uint8_t get_nextpage_up(); + public: + MCLEncoder enc1; + uint8_t page_select; + PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage( e1, e2, e3 ,e4) { + encoders[0] = &enc1; + } + void display(); + void setup(); + void init(); + void loop(); + void cleanup(); + LightPage *get_page(uint8_t page_number, char *str = NULL); + uint8_t get_nextpage_down(); + uint8_t get_nextpage_up(); + virtual bool handleEvent(gui_event_t *event); }; extern PageSelectPage page_select_page; From bfed3e3a9ce6b6bc148d23318647fe67ccbb1f31 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 2 Nov 2019 18:26:37 +1100 Subject: [PATCH 211/469] Shorten titles for Grid Save / Chain Remove step sequencer count from save page. --- avr/cores/megacommand/MCL/GridSavePage.cpp | 4 ++-- avr/cores/megacommand/MCL/GridWritePage.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 1ea66da65..4bc275bd7 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -14,7 +14,7 @@ void GridSavePage::setup() { void GridSavePage::init() { #ifdef OLED_DISPLAY - mcl_gui.draw_popup("SAVE TO GRID", true, 28); + mcl_gui.draw_popup("SAVE", true, 28); #endif } @@ -74,7 +74,7 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, step, 10); - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "STEP", step); + // mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "STEP", step); oled_display.setFont(&TomThumb); // draw data flow in the center diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index bbd56a332..a5e948959 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -26,7 +26,7 @@ void GridWritePage::setup() { void GridWritePage::init() { #ifdef OLED_DISPLAY - mcl_gui.draw_popup("CHAIN FROM GRID", true, 28); + mcl_gui.draw_popup("CHAIN", true, 28); #endif } From aea1990c01bb6cec120d1b9ea546108d588e918a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 2 Nov 2019 19:00:55 +1100 Subject: [PATCH 212/469] Move draw_knob functions to MCLGUI --- avr/cores/megacommand/MCL/MCLGUI.cpp | 19 +++++++++++++++++++ avr/cores/megacommand/MCL/MCLGUI.h | 10 ++++++++++ avr/cores/megacommand/MCL/SeqPage.cpp | 13 +++---------- avr/cores/megacommand/MCL/SeqPage.h | 5 ----- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 884f7a1e3..57f8c0527 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -59,6 +59,25 @@ void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, oled_display.drawRect(x, y, 5, length, WHITE); } +void MCLGUI::draw_knob_frame() { + for (uint8_t x = knob_x0; x <= knob_xend; x += knob_w) { + mcl_gui.draw_vertical_dashline(x, 0, knob_y); + oled_display.drawPixel(x, knob_y, WHITE); + oled_display.drawPixel(x, knob_y + 1, WHITE); + } + mcl_gui.draw_horizontal_dashline(knob_y, knob_x0 + 1, knob_xend + 1); +} + +void MCLGUI::draw_knob(uint8_t i, const char *title, const char *text) { + uint8_t x = knob_x0 + i * knob_w; + draw_text_encoder(x, knob_y0, title, text); +} + +void MCLGUI::draw_knob(uint8_t i, Encoder* enc, const char* title) { + uint8_t x = knob_x0 + i * knob_w; + draw_light_encoder(x + 6, 6, enc, title); +} + static char title_buf[16]; // ref: Design/popup_menu.png diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index fd28d64da..08d435ba3 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -49,6 +49,10 @@ class MCLGUI { void draw_panel_status(bool recording, bool playing); void draw_panel_number(uint8_t number); + void draw_knob_frame(); + void draw_knob(uint8_t i, const char* title, const char* text); + void draw_knob(uint8_t i, Encoder* enc, const char* name); + static constexpr uint8_t seq_w = 5; static constexpr uint8_t led_h = 3; static constexpr uint8_t trig_h = 5; @@ -94,6 +98,12 @@ class MCLGUI { static constexpr uint8_t pane_trackid_x = 15; static constexpr uint8_t pane_trackid_y = 8; + + static constexpr uint8_t knob_x0 = 31; + static constexpr uint8_t knob_w = 24; + static constexpr uint8_t knob_xend = 127; + static constexpr uint8_t knob_y0 = 1; + static constexpr uint8_t knob_y = 20; }; extern MCLGUI mcl_gui; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 3e2b095d3..023610b69 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -614,23 +614,16 @@ void SeqPage::draw_knob_frame() { #ifndef OLED_DISPLAY return; #endif + mcl_gui.draw_knob_frame(); // draw frame - for (uint8_t x = knob_x0; x <= knob_xend; x += knob_w) { - mcl_gui.draw_vertical_dashline(x, 0, knob_y); - oled_display.drawPixel(x, knob_y, WHITE); - oled_display.drawPixel(x, knob_y + 1, WHITE); - } - mcl_gui.draw_horizontal_dashline(knob_y, knob_x0 + 1, knob_xend + 1); } void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { - uint8_t x = knob_x0 + i * knob_w; - mcl_gui.draw_text_encoder(x, knob_y0, title, text); + mcl_gui.draw_knob(i,title,text); } void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* title) { - uint8_t x = knob_x0 + i * knob_w; - mcl_gui.draw_light_encoder(x + 6, 6, enc, title); + mcl_gui.draw_knob(i,enc,title); } void SeqPageMidiEvents::setup_callbacks() { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index a47b72332..a95395493 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -65,11 +65,6 @@ class SeqPage : public LightPage { static constexpr uint8_t led_y = 22; static constexpr uint8_t trig_y = 26; - static constexpr uint8_t knob_x0 = 31; - static constexpr uint8_t knob_w = 24; - static constexpr uint8_t knob_xend = 127; - static constexpr uint8_t knob_y0 = 1; - static constexpr uint8_t knob_y = 20; }; #endif /* SEQPAGE_H__ */ From 3cee1cebf9ce4238a2bf608c089272a3e04ccb9a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 2 Nov 2019 19:15:36 +1100 Subject: [PATCH 213/469] Standardise FXPage GUI --- avr/cores/megacommand/MCL/AuxPages.cpp | 4 ++-- avr/cores/megacommand/MCL/FXPage.cpp | 32 +++++++++++++++----------- avr/cores/megacommand/MCL/FXPage.h | 6 ++++- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index e162b8374..3d7bbbc2d 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -41,10 +41,10 @@ fx_param_t fx_reverb_params[8] = { FXPage fx_page_a(&fx_param1, &fx_param2, &fx_param3, &fx_param4, - (fx_param_t*) &fx_echo_params, 8); + (fx_param_t*) &fx_echo_params, 8, "ECHO"); FXPage fx_page_b(&fx_param1, &fx_param2, &fx_param3, &fx_param4, - (fx_param_t*) &fx_reverb_params, 8); + (fx_param_t*) &fx_reverb_params, 8, "REVERB"); LFOPage lfo_page(&(mcl_seq.lfo_tracks[0]), &fx_param1, &fx_param2, &fx_param3, &fx_param4); diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index b012d8b7c..6f0e6ed1e 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -40,8 +40,9 @@ void FXPage::update_encoders() { } encoders[n]->old = encoders[n]->cur; + ((LightPage *)this)->encoders_used_clock[n] = + slowclock - SHOW_VALUE_TIMEOUT - 1; } - } void FXPage::cleanup() { @@ -99,14 +100,12 @@ void FXPage::display() { #endif #ifdef OLED_DISPLAY - oled_display.setFont(); - oled_display.setCursor(0, 0); - - oled_display.print("FX "); - oled_display.print(page_mode ? 1 : 0); - oled_display.print(" "); + auto oldfont = oled_display.getFont(); PGM_P param_name = NULL; char str[4]; + + mcl_gui.draw_knob_frame(); + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); @@ -115,14 +114,21 @@ void FXPage::display() { param_name = fx_param_name(fx_type, fx_param); m_strncpy_p(str, param_name, 4); - mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); - - // mcl_gui.draw_md_encoder(30 + 20 * i, 6, encoders[i], str); + mcl_gui.draw_knob(i, encoders[i], str); + // mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); } - - + oled_display.setFont(&TomThumb); + const char* info1; + const char* info2; + if (page_mode) { + info1 = "FX A"; + } else { + info1 = "FX B"; + } + info2 = &fx_page_title[0]; + mcl_gui.draw_panel_labels(info1, info2); oled_display.display(); - + oled_display.setFont(oldfont); #endif } diff --git a/avr/cores/megacommand/MCL/FXPage.h b/avr/cores/megacommand/MCL/FXPage.h index 32cfe6369..1530f4e98 100644 --- a/avr/cores/megacommand/MCL/FXPage.h +++ b/avr/cores/megacommand/MCL/FXPage.h @@ -17,16 +17,20 @@ typedef struct fx_param_t { class FXPage : public LightPage, MidiCallback { public: FXPage(Encoder *e1 = NULL, Encoder *e2 = NULL, - Encoder *e3 = NULL, Encoder *e4 = NULL, fx_param_t *params_ = NULL, uint8_t num_of_params_ = 0) + Encoder *e3 = NULL, Encoder *e4 = NULL, fx_param_t *params_ = NULL, uint8_t num_of_params_ = 0, const char* title = NULL) : LightPage(e1, e2, e3, e4) { params = params_; num_of_params = num_of_params_; + if (title) { + strcpy(fx_page_title, title); + } } bool handleEvent(gui_event_t *event); bool midi_state = false; + char fx_page_title[8]; fx_param_t *params; uint8_t num_of_params; From 79b55811491c43256a0088644c1a5606aba5bcaf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 2 Nov 2019 19:20:40 +1100 Subject: [PATCH 214/469] Reduce Mixer fade rate due to fps increase on drawRect --- avr/cores/megacommand/MCL/MixerPage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index 2e0a80924..37137a086 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -1,6 +1,8 @@ #include "MCL.h" #include "MixerPage.h" + #define FADER_LEN 16 +#define FADE_RATE 26 void MixerPage::setup() { encoders[0]->handler = encoder_level_handle; @@ -266,7 +268,7 @@ void MixerPage::display() { oled_display.display(); } #endif - uint8_t dec = MidiClock.get_tempo() / 10; + uint8_t dec = MidiClock.get_tempo() / FADE_RATE; for (uint8_t n = 0; n < 16; n++) { if (disp_levels[n] < dec) { disp_levels[n] = 0; From f1bb44aa1d43f02108854fd65469fc6cceaec6b5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 3 Nov 2019 15:59:51 +1100 Subject: [PATCH 215/469] Update RAMPage to new page layout --- avr/cores/megacommand/MCL/RAMPage.cpp | 132 ++++++++++++++++---------- 1 file changed, 80 insertions(+), 52 deletions(-) diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index b7f768404..9f2b32172 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -39,10 +39,13 @@ void RAMPage::init() { setup_callbacks(); } if (mcl_cfg.ram_page_mode == LINK) { - for (uint8_t n = 0; n < 4; n++) { - if (page_id == 0) { encoders[n]->cur = ram_page_b.encoders[n]->cur; } - else { encoders[n]->cur = ram_page_a.encoders[n]->cur; } - } + for (uint8_t n = 0; n < 4; n++) { + if (page_id == 0) { + encoders[n]->cur = ram_page_b.encoders[n]->cur; + } else { + encoders[n]->cur = ram_page_a.encoders[n]->cur; + } + } } } @@ -427,14 +430,14 @@ void RAMPage::setup_ram_rec_stereo(uint8_t track, uint8_t lev, uint8_t source, void RAMPage::loop() { - //Prevent number of slices exceeding number of steps. + // Prevent number of slices exceeding number of steps. uint8_t steps = encoders[3]->cur * 4; uint8_t slices = 1 << encoders[2]->cur; while (slices > steps) { - encoders[2]->cur--; - // encoders[2]->old = encoders[2]->cur; - slices = 1 << encoders[2]->cur; + encoders[2]->cur--; + // encoders[2]->old = encoders[2]->cur; + slices = 1 << encoders[2]->cur; } uint8_t n = 14 + page_id; @@ -541,28 +544,9 @@ void RAMPage::display() { #endif #ifdef OLED_DISPLAY float remain; - oled_display.drawRoundRect(104, 28, 22, 4, 1, WHITE); - if ((RAMPage::rec_states[page_id] != STATE_NOSTATE)) { - if (MidiClock.clock_less_than(transition_step + record_len, - MidiClock.div16th_counter)) { - - remain = ((float)(MidiClock.div16th_counter) / - (float)(transition_step + record_len)); - } - // else if (RAMPage::rec_states[page_id] == STATE_RECORD{ - else { - uint8_t n = 14 + page_id; - remain = (float)mcl_seq.md_tracks[n].step_count / - (float)mcl_seq.md_tracks[n].length; - } - uint8_t width = remain * 21; - oled_display.fillRect(105, 28, width, 4, WHITE); - } + auto oldfont = oled_display.getFont(); oled_display.setFont(); - oled_display.setCursor(0, 0); - - oled_display.print("RAM"); - oled_display.print(page_id + 1); + oled_display.setCursor(34, 24); switch (RAMPage::rec_states[page_id]) { case STATE_QUEUE: oled_display.print(" [Queue]"); @@ -574,66 +558,109 @@ void RAMPage::display() { oled_display.print(" [Play]"); break; } - oled_display.setFont(&TomThumb); + oled_display.setCursor(0, 32); - oled_display.setCursor(0, 16); + oled_display.print("RAM "); + oled_display.print(page_id + 1); + + oled_display.setCursor(105, 32); if (mcl_cfg.ram_page_mode == 0) { - oled_display.print("MONO "); + oled_display.print("MONO"); } else { - oled_display.print("STEREO "); + oled_display.print("LINK"); } oled_display.setFont(); oled_display.setCursor(0, 24); + + char *source; + switch (encoders[0]->cur) { case SOURCE_MAIN: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - oled_display.print("LEFT "); + source = "L "; } if (page_id == 1) { - oled_display.print("RIGHT"); + source = "R"; } } else { - oled_display.print("MAIN "); + source = "INT"; } break; case SOURCE_INPA: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - oled_display.print("INPA "); + source = "A "; } else { - oled_display.print("INPB "); + source = "B "; } } else { - oled_display.print("INPA "); + source = "A "; } break; case SOURCE_INPB: if (mcl_cfg.ram_page_mode == LINK) { if (page_id == 0) { - oled_display.print("INPA "); + source = "A "; } else { - oled_display.print("INPB "); + source = "B "; } } else { - oled_display.print("INPB "); + source = "B "; } break; } + /* + oled_display.print(encoders[1]->cur); + oled_display.print(" S:"); + oled_display.print(1 << encoders[2]->cur); + oled_display.print(" L:"); + oled_display.print(encoders[3]->cur * 4); + */ + mcl_gui.draw_knob_frame(); + mcl_gui.draw_knob(0, "SRC", source); + + + char val[4]; + + itoa(encoders[1]->cur, val, 10); + mcl_gui.draw_knob(1, "MOD", val); - oled_display.print(encoders[1]->cur); - oled_display.print(" S:"); - oled_display.print(1 << encoders[2]->cur); - oled_display.print(" L:"); - oled_display.print(encoders[3]->cur * 4); + itoa(1 << encoders[2]->cur, val, 10); + mcl_gui.draw_knob(2, "SLI", val); - uint8_t w_x = 104, w_y = 0; - oled_display.drawPixel(w_x + 19, w_y + 24, WHITE); - oled_display.drawCircle(w_x + 19, w_y + 24, 2, WHITE); - oled_display.drawLine(w_x + 18, w_y + 9, w_x + 21, w_y + 23, WHITE); - oled_display.drawLine(w_x + 7, w_y + 19, w_x + 19, w_y + 26, WHITE); + itoa(encoders[3]->cur * 4, val, 10); + mcl_gui.draw_knob(3, "LEN", val); + + uint8_t w_x = 0, w_y = 2; + oled_display.drawPixel(w_x + 24, w_y + 0, WHITE); + oled_display.drawCircle(w_x + 24, w_y + 0, 2, WHITE); + oled_display.drawLine(w_x + 12, w_y - 1, w_x + 24, w_y - 3, WHITE); + oled_display.drawLine(w_x + 17, w_y + 15, w_x + 26, w_y + 2, WHITE); + + uint8_t progress_x = w_x + 0; + uint8_t progress_y = w_y + 20; + uint8_t progress_w = 19; + oled_display.drawRoundRect(progress_x, progress_y, progress_w, 4, 1, WHITE); + + if ((RAMPage::rec_states[page_id] != STATE_NOSTATE)) { + if (MidiClock.clock_less_than(transition_step + record_len, + MidiClock.div16th_counter)) { + + remain = ((float)(MidiClock.div16th_counter) / + (float)(transition_step + record_len)); + } + // else if (RAMPage::rec_states[page_id] == STATE_RECORD{ + else { + uint8_t n = 14 + page_id; + remain = (float)mcl_seq.md_tracks[n].step_count / + (float)mcl_seq.md_tracks[n].length; + } + uint8_t width = remain * (progress_w - 1); + oled_display.fillRect(progress_x + 1, progress_y, width, 4, WHITE); + } switch (wheel_spin) { case 0: @@ -679,6 +706,7 @@ void RAMPage::display() { wheel_spin_last_clock = MidiClock.div16th_counter; } oled_display.display(); + oled_display.setFont(oldfont); #endif } void RAMPage::onControlChangeCallback_Midi(uint8_t *msg) { From 0cfbeb5aacdfec9ed2cef2b12b230cf55b55e184 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 3 Nov 2019 16:21:36 +1100 Subject: [PATCH 216/469] tune MixerPage decay rate again --- avr/cores/megacommand/MCL/MixerPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index 37137a086..ac50c8a84 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -2,7 +2,7 @@ #include "MixerPage.h" #define FADER_LEN 16 -#define FADE_RATE 26 +#define FADE_RATE 16 void MixerPage::setup() { encoders[0]->handler = encoder_level_handle; From f8388a60e4179b622a454b9676e786c500a79ff9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 3 Nov 2019 19:16:07 +1100 Subject: [PATCH 217/469] Add PageSelect icons + trimetric sprites --- art/sprites/FX.png | Bin 0 -> 168 bytes art/sprites/chromatic.png | Bin 0 -> 452 bytes art/sprites/chromatic_flipped.png | Bin 0 -> 460 bytes art/sprites/gatebox.png | Bin 0 -> 246 bytes art/sprites/gateboxlarge.png | Bin 0 -> 343 bytes art/sprites/grid.png | Bin 0 -> 259 bytes art/sprites/lfo.png | Bin 0 -> 310 bytes art/sprites/rythmecho.png | Bin 0 -> 296 bytes art/sprites/step.png | Bin 0 -> 459 bytes art/sprites/trimetric/keyboard.png | Bin 0 -> 597 bytes art/sprites/trimetric/keyboard_rev.png | Bin 0 -> 515 bytes 11 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/FX.png create mode 100644 art/sprites/chromatic.png create mode 100644 art/sprites/chromatic_flipped.png create mode 100644 art/sprites/gatebox.png create mode 100644 art/sprites/gateboxlarge.png create mode 100644 art/sprites/grid.png create mode 100644 art/sprites/lfo.png create mode 100644 art/sprites/rythmecho.png create mode 100644 art/sprites/step.png create mode 100644 art/sprites/trimetric/keyboard.png create mode 100644 art/sprites/trimetric/keyboard_rev.png diff --git a/art/sprites/FX.png b/art/sprites/FX.png new file mode 100644 index 0000000000000000000000000000000000000000..fc00cf67f60790a58ec6bdae457bf56fe8342f2a GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp@K+MCz1|)ZGH@^v_7>k44ofy`glX(f`Bzd|xhG>Yk z`g8IfP~c$R{J(x?r>F1p6*DxJE#Ecs=$)rsD^>=slK#NBKIq`*J8Ep7-@W60WRP(3 zkKDOKPi^>aPO(zitj=5>JpJ6U?8`UX9$aI2xO^p(VnF>avy~2+PTbOsdyku|i#RfO R@&j#S@O1TaS?83{1OQwcKHUHS literal 0 HcmV?d00001 diff --git a/art/sprites/chromatic.png b/art/sprites/chromatic.png new file mode 100644 index 0000000000000000000000000000000000000000..bd7c660feafbc52f071c71449f239199ca010ac1 GIT binary patch literal 452 zcmV;#0XzPQP)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px$en~_@R7efAl-rJkFbqQh?f-up!Ww1~82On=^5x8pxGMm` zg@Cv*JL?YTEMJsva8?ASaK>qpv7FrdThvJ7JHXwvc4^@ihjW*;?|H%vWJLrDGIJp#jUy9zy7Z4 usEQ-f--X1^@s6D=Y3@00001b5ch_0Itp) z=>Px$hDk(0R7efAmC=%eFbGA}+5i7!vpKXUi!>SS!!o0U%jJSGwfnYhtzZ9U2g>`t zFB`PhHZMr5tFQSS9|Y%lM$xJf_$o(X&E@zfl>5!ea?)J&a>a>Pb@2owB|x4DWK6CN zh;u%#_a2OwY+-^%W5vGpyxnLp6P;n-Hi#{vi0ShaWO4Gu9ERhFg9r4;;t42L+^X6@ zT<<`)yiRC}Lw2puOd#_r-#Y!}9Y|Kx7_yj!)w^|-7Y*{>N;u*;_|@q+Bkf_jV%J0nS$n4hrv9}wCNL=te!?( z3g|N~X6;uEC{}C;yfNY`4mLg&1b2owhLB>_SB+m2Rdn|D(N#DA0000Px#ut`KgR5%fplK~EeAPhsnaQ`c#)jU#QnfenmxAxUZzO|InuPgoa{#a_QoA1~r zHV_ERcuyH zKEg);0}PQFkJt2n;6}FbneMj@Rs*1KCJ+5<07io82@V4gfuc&#bOr)-Ax5CjX1^@s6D=Y3@00001b5ch_0Itp) z=>Px$5lKWrR7efAlz|SzAP7Y3(*OTuw_EZW3aGfHCJhM3-D%gg<2d@~d2GFYVeY*j zdMay6AR}H0ofQ`=68f#RKsOa&FidR}BpEJqml)@oBgaE}0)Z+bkj=0`0Pt*h*26h5 z2++ucJhArRnDcQc01yICh8Va}JRuOEG6aS>zZVa^FQ@iOShh(`QADF2eTyeAeaX0F zD2o!$3`_+?ymD^EG9&3r2ADargyi7a+ZY7?b!^F%2LTmvG&%u&VOJ~tFY-eY+99>` zB>_9In_pMtuOoXGvl=J#r@aK^43Kq6%^JvRcE;v60W~1nAraQkArd|`hyiHtYxIa* pixr}GI{_|eDpqTD#^#Uzir&Z*nKah-%$ooJ002ovPDHLkV1f&`hDrbc literal 0 HcmV?d00001 diff --git a/art/sprites/grid.png b/art/sprites/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..bf48a122ee39bbfcabe79caffbb3afa3e26f4a58 GIT binary patch literal 259 zcmV+e0sQ`nP)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#y-7qtR7ef&l*@tpNxIEUFCQgFzL*!2ns5X4jgRJ^-p1JQCe1`$+U(iMUT?_rObA ze#jjWMN?3J-#LGk%l%iq)H?+?y8%DvjqNqgd#-9FX8;f{fRzAbR&fRX1^@s6D=Y3@00001b5ch_0Itp) z=>Px#@JU2LR7efIlZ_68FbIU##`k~a&1?1*B$!8vBeS0=kb>a>|=mYmC36{UwQ)HH0!4(pc#|MKbr literal 0 HcmV?d00001 diff --git a/art/sprites/rythmecho.png b/art/sprites/rythmecho.png new file mode 100644 index 0000000000000000000000000000000000000000..7b22ca1fb0fcdd05972628634a3da062c88ccbb7 GIT binary patch literal 296 zcmV+@0oVSCP)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#;z>k7R7efAR6!2IFbG?w{r}5dndHbYlae;cg+Rc@mfCil=e6;C7`wH`>EpZY z{sziwaNu?V6jntz-z`F6hM=Ol6EJW#SY3kqZd;Ts!He4ck02Z5t+E~iSYB0iBW-aPgjs_e+7`ufus|lprVnCh(;tof~+Be#+;YQFiHv- zXqKd%1N(p1QJ!&*YW&W3rF7aN2C3ZLcc9!-wG3FPaSF_qXpI7^^kB{rptv$R0+U#= uq|$>W9f7h))i4~Rvd$62fff1o5A6%(v|2l62>8MP0000X1^@s6D=Y3@00001b5ch_0Itp) z=>Px$g-Jv~R7ee-m5XwNFbo8N>HmK+l(n)&fxdigkx z1HM+lN$0IaO!2H0qY>*>gK3(Ed7ihODuR&0aB|V+@`xN+0g6)uJah8lf!{W;7@NWf z*xif3-u0D%;Kv+LU2+O0wE0&+IvXfmHZygG##__&hWLk7gv zw&VxX$11LfadPlkmph7-AKw@mFELiFQPiu})xdPD7HYM8Y=|OB~N_UfFKE3pg=ynR=kSdt!)B{G~rSd+{P8R=E@#E zwA^`$w~p$RLkzSDfDx8`s}XB8=0C%lVKCpc?@kW*9(Kl8@e#|@Yw zC!si3%^4paau(^kV~fPo$`Lbu)Zj{3K{*PG{{l}5J}b;&$QA$q002ovPDHLkV1hs; B(Te~8 literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/keyboard.png b/art/sprites/trimetric/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..685ac6232bdf149f33c64a45344bb88c572f9ca5 GIT binary patch literal 597 zcmV-b0;>IqP)Px%4@pEpR7ef2mVs`=AP_^BX#fAkNxMASml+0Gq6TT@&iH(WfVAnkjjx+VN=?&L zsgmby8nu?M?;k1!%`(&tlN=LFs zCw1Ei(ZoiYNz9`FOXK3sAKxJxXd;{SFdMVx2E>>TPbdW;n=7WEIQa;po{?O2UMsKP z(|5>X8ggNmW!djb3L3+6qzN#!GTu2`uwUvVD(0NeSFAUu5!ThTjqBpc;YI#?LMf2N z#5v@N^%{;$$wi|wQuSB4})zG8$r1+xh_I&N;n$(r4e zJIzI7gdOtCZ9;Bro!lc_9cK7Eg>tD=q!p0N?2tp=i(5T-X^`BJ_|Q;GCeJ43mDsk! z#C!Zgc>iRrcripF-`RU(E{PcofnJjo9&u7D`Rcm9txeB$U0>hty+aq7v%q*{Dsh^+ zL3P+Pxc#z^arcucK=CNf<{8V+IbT^3?EIiXzw*im6os4lSyR-dhB`eQEQe;ms%Nht zGrKZxP4)0>ko?4{b1m_Viy?`SZ3ZdxHfBA!^5Ny(0lzZ#6S629gJn+KYY4_@4Hod? zPxTKPg8XSB^K2;RP9yrR45blciI{Nsd^Ie7$6Ogg&|Py6G$rPocSP(CGo}Bw%ipbc jRI}j`m9^w{q)tIUJ?kh2A08Z100000NkvXXu0mjf>5~*v literal 0 HcmV?d00001 diff --git a/art/sprites/trimetric/keyboard_rev.png b/art/sprites/trimetric/keyboard_rev.png new file mode 100644 index 0000000000000000000000000000000000000000..06b3f8e63cd225d02fb1d853a183f0c037898eba GIT binary patch literal 515 zcmV+e0{s1nP)Px$y-7qtR7efYmJM%%FbqTi_5c6ks$C!2L$_ek&<|;mCL8;FcZ8(cJWbR6@%L}m z@@*&cJg*Y>``CMf9z&jVjuA|>Ml^Ut_ccSUku=aV_3=-ZyMnMgrJA;~e)TjIbdU@eWn!>3M6n;#mY zamI*dm`f4r8tGwjW_O0NX%3moxAz&0HFM`i>hci5Z-$bjB^I2+VyIPl%a5;+05is=_3f_^g>b8C^R=3?7O2cA8Z%>nUtKYtQn{5I_Zp z?T?uKBeN;=iV^4k7*cd=#*G^_k~n$J#GRpMrV#WtD)i2d$YCrEY09&rV180+)+PHy zJp7LlF%4)?!IA4L_H69lM|U8jd73>d+QT1yvfj`)dq4fJhcn Date: Sun, 3 Nov 2019 19:44:35 +1100 Subject: [PATCH 218/469] add parameter lock page sprite --- art/sprites/para.png | Bin 0 -> 312 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/para.png diff --git a/art/sprites/para.png b/art/sprites/para.png new file mode 100644 index 0000000000000000000000000000000000000000..fd947e581a21d2287e99730ba083600ca7d4ca62 GIT binary patch literal 312 zcmV-80muG{P)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#@<~KNR7efIl-mx&APhsxwEzF*Zhknb2^r7}t5nfAi5&xK*|u%}zRRBX6U_U5 z%vegPC|^}{Gl=A(J0+k=dE+c1cPHfK=kNy_RvLZ63{#tMuRG-+=uU-02Ta;y^NCgG zE-D8zWIdrQR|#YVS1@-}2`CX=cyT5+%2xsz!CfppWDbcH5qCG5oSsxbNyui2AdGAJ zFM>Q4J3cG?(?KM^8mA5*ixZDx`g~n}i Date: Mon, 4 Nov 2019 20:34:56 +1100 Subject: [PATCH 219/469] fix undefined reference to knob constants --- avr/cores/megacommand/MCL/LFOPage.cpp | 4 ++-- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 9db0fb3a4..75dc39648 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -261,7 +261,7 @@ void LFOPage::display() { mcl_gui.draw_panel_toggle("ON", "OFF", lfo_track->enable); draw_page_index(false); - uint8_t x = knob_x0 + 5; + uint8_t x = mcl_gui.knob_x0 + 5; uint8_t y = 8; uint8_t lfo_height = 7; uint8_t width = 13; @@ -286,7 +286,7 @@ void LFOPage::display() { } } - x = knob_x0 + 2; + x = mcl_gui.knob_x0 + 2; oled_display.setCursor(x + 4, 7); oled_display.print("WAV"); diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 3cb443c11..0a06e6da7 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -211,7 +211,7 @@ void SeqExtStepPage::display() { draw_knob(2, "LEN", K); if (notes_held > 0) { - uint8_t x = knob_x0 + knob_w * 3 + 2; + uint8_t x = mcl_gui.knob_x0 + mcl_gui.knob_w * 3 + 2; auto *oldfont = oled_display.getFont(); oled_display.setFont(&TomThumb); uint8_t note_idx = 0; From 14d96fc58f52e203b2f8960b4ddba35a3669b771 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 4 Nov 2019 20:42:52 +1100 Subject: [PATCH 220/469] Add rhythmecho and gatebox icons to FX pages --- avr/cores/megacommand/MCL/FXPage.cpp | 8 ++++++++ avr/cores/megacommand/MCL/FXPage.h | 4 ++-- avr/cores/megacommand/MCL/MCLGUI.cpp | 20 +++++++++++++++++++- avr/cores/megacommand/MCL/MCLGUI.h | 4 ++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 6f0e6ed1e..a5679e3b3 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -1,6 +1,8 @@ #include "FXPage.h" #include "MCL.h" #include "RAMPage.h" +#include "MCLGUI.h" + #define FX_TYPE 0 #define FX_PARAM 1 #define INTERPOLATE @@ -104,6 +106,12 @@ void FXPage::display() { PGM_P param_name = NULL; char str[4]; + if (page_id == 0) { + oled_display.drawBitmap(0, 0, icon_rhytmecho, 24, 18, WHITE); + } + else { + oled_display.drawBitmap(0, 0, icon_gatebox, 24, 18, WHITE); + } mcl_gui.draw_knob_frame(); for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { diff --git a/avr/cores/megacommand/MCL/FXPage.h b/avr/cores/megacommand/MCL/FXPage.h index 1530f4e98..a431a3b94 100644 --- a/avr/cores/megacommand/MCL/FXPage.h +++ b/avr/cores/megacommand/MCL/FXPage.h @@ -17,9 +17,9 @@ typedef struct fx_param_t { class FXPage : public LightPage, MidiCallback { public: FXPage(Encoder *e1 = NULL, Encoder *e2 = NULL, - Encoder *e3 = NULL, Encoder *e4 = NULL, fx_param_t *params_ = NULL, uint8_t num_of_params_ = 0, const char* title = NULL) + Encoder *e3 = NULL, Encoder *e4 = NULL, fx_param_t *params_ = NULL, uint8_t num_of_params_ = 0, const char* title = NULL, uint8_t page_id_ = 0) : LightPage(e1, e2, e3, e4) { - + page_id = page_id_; params = params_; num_of_params = num_of_params_; if (title) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 57f8c0527..666540d80 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -629,6 +629,24 @@ const unsigned char encoder_small_5 [] PROGMEM = { }; // 'encoder6', 11x11px const unsigned char encoder_small_6 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 }; + +// 'gateboxlarge', 24x25px +const unsigned char icon_gatebox [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x80, 0x00, 0x7e, 0xe0, 0x01, 0xfe, 0xe0, 0x07, + 0xff, 0x90, 0x03, 0xfe, 0x70, 0x0c, 0xf9, 0xf0, 0x0f, 0x26, 0xf0, 0x0f, 0xdc, 0xf0, 0xef, 0xd3, + 0x37, 0x0f, 0xdf, 0xc0, 0x0f, 0x9f, 0xf0, 0x0e, 0x5f, 0xe0, 0x09, 0xdf, 0x80, 0x07, 0xde, 0x00, + 0x01, 0xd8, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xef, 0x48, 0x21, 0x29, 0x48, 0x2d, + 0xe9, 0x30, 0x25, 0x29, 0x48, 0x3d, 0xef, 0x48, 0x00, 0x00, 0x00 +}; + +// 'rythmecho', 24x25px +const unsigned char icon_rhytmecho [] PROGMEM = { + 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x3f, 0xff, 0xfe, 0x3f, 0xff, 0xfc, 0x00, 0x01, 0xfc, 0x07, + 0xe1, 0xf8, 0x07, 0xe3, 0xf8, 0x0f, 0xc7, 0xf0, 0x0f, 0xff, 0xf0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, + 0x00, 0x3f, 0x7f, 0x80, 0x3f, 0x1f, 0xc0, 0x7e, 0x0f, 0xe0, 0x7e, 0x07, 0xf0, 0x00, 0x00, 0x00, + 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x63, 0x1b, 0x6c, 0x7b, 0x1f, 0x6c, 0x7b, 0x1f, 0x6c, 0x63, + 0x1b, 0x6c, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x00, 0x00, 0x00 +}; diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 08d435ba3..5ea03f877 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -153,5 +153,9 @@ extern const unsigned char encoder_small_4 []; extern const unsigned char encoder_small_5 []; // 'encoder6', 11x11px extern const unsigned char encoder_small_6 []; +// 'gatebox', 24x25px +extern const unsigned char icon_gatebox []; +// 'rythmecho', 24x25px +extern const unsigned char icon_rhytmecho []; #endif /* MCLGUI_H__ */ From 19ac3805a063b5aac3f0afa4aac3853e15bbdc75 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 5 Nov 2019 00:57:34 +0800 Subject: [PATCH 221/469] minor touch. --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 1e65cae46..d51fb9f99 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -23,7 +23,7 @@ const PageCategory Categories[] PROGMEM = { const PageSelectEntry Entries[] PROGMEM = { {"GRID", &grid_page, 0xFF, 0}, - {"NOTES", &seq_step_page, 0, 1}, + {"STEPS", &seq_step_page, 0, 1}, {"RECORD", &seq_rtrk_page, 1, 1}, {"LOCKS", &seq_param_page[0], 2, 1}, {"CHROMA", &seq_ptc_page, 3, 1}, From 738b7fcae7993fc4dbef3d921a707470138044c8 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 5 Nov 2019 01:21:46 +0800 Subject: [PATCH 222/469] add comments on MidiClock --- avr/cores/megacommand/Midi/MidiClock.cpp | 1 - avr/cores/megacommand/Midi/MidiClock.h | 73 +++++++++++++++++------- avr/cores/megacommand/MidiUart.cpp | 2 +- avr/cores/megacommand/main.cpp | 62 ++++++++++---------- 4 files changed, 83 insertions(+), 55 deletions(-) diff --git a/avr/cores/megacommand/Midi/MidiClock.cpp b/avr/cores/megacommand/Midi/MidiClock.cpp index 7b50b071e..5f4295da9 100644 --- a/avr/cores/megacommand/Midi/MidiClock.cpp +++ b/avr/cores/megacommand/Midi/MidiClock.cpp @@ -32,7 +32,6 @@ void MidiClockClass::init() { clock_last_time = clock; mod12_counter = 0; mod6_counter = inmod6_counter = 0; - mod3_counter = 0; bar_counter = 1; beat_counter = 1; step_counter = 1; diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index b3ce5f58d..be2d40a15 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -1,3 +1,4 @@ + /* Copyright (c) 2009 - http://ruinwesen.com/ */ #ifndef MIDICLOCK_H__ @@ -44,12 +45,11 @@ class MidiClockClass { volatile uint8_t mod12_counter; volatile uint8_t mod6_counter; - volatile uint8_t mod3_counter; volatile uint8_t mod8_free_counter; - volatile uint16_t div196_counter; - volatile uint16_t div196_time; - volatile uint8_t div196th_countdown; + volatile uint16_t div192_counter; + volatile uint16_t div192_time; + volatile uint8_t div192th_countdown; volatile uint16_t clock_last_time; volatile uint16_t last_diff_clock8; @@ -252,17 +252,15 @@ class MidiClockClass { inCallback = false; } - ALWAYS_INLINE() void handleImmediateClock() { + ALWAYS_INLINE() void handleImmediateClock() { // if (clock > clock_last_time) { // div192th_time = (clock - clock_last_time) / 2; // DEBUG_PRINTLN( (clock - clock_last_time) / 2); // } clock_last_time = clock; - uint8_t _mod6_counter = mod6_counter; - div196th_countdown = 0; + div192th_countdown = 0; if (transmit_uart1) { - // MidiUart.putc(0xF8); MidiUart.m_putc_immediate(0xF8); } if (transmit_uart2) { @@ -279,19 +277,22 @@ class MidiClockClass { } /* in interrupt on receiving 0xF8 */ - ALWAYS_INLINE() void handleClock() { + ALWAYS_INLINE() void handleClock() { if (useImmediateClock) { handleImmediateClock(); return; } } - ALWAYS_INLINE() void increment192Counter() { + + /* in interrupt on 5000Hz internal timer timeout */ + ALWAYS_INLINE() void increment192Counter() { if (state == STARTED) { div192th_counter++; mod12_counter++; } } + uint16_t midi_clock_diff(uint16_t old_clock, uint16_t new_clock) { if (new_clock >= old_clock) return new_clock - old_clock; @@ -302,7 +303,7 @@ class MidiClockClass { void calc_tempo() { DEBUG_PRINTLN(diff_clock8); if (last_diff_clock8 != diff_clock8) { - tempo = ((float)100000 / ((float)diff_clock8)); + tempo = 100000.0f / diff_clock8; last_diff_clock8 = diff_clock8; } } @@ -312,30 +313,59 @@ class MidiClockClass { return tempo; } - ALWAYS_INLINE() void MidiClockClass::incrementCounters() { + /* in interrupt, called on receiving MIDI_CLOCK + * + * MIDI_CLOCK is sent at 24PPQN, that is, 6 pulses per step, or 96 pulses per bar. + * + * clock: incrementing at 5KHz. + * mod8_free_counter: divides 24PPQN to 3PPQN, that is, 3/4 pulses per step + * diff_clock8: measures clock ticks between two mod8. + * - because mod6 is div16th, mod8 is div12th. + * div192_time: clock ticks for what divides 3PPQN time by 16, that is, 48PPQN. + * div192th_countdown: incrementing at 5KHz, and triggers increment192Counter. + * + * For example, assume BPM=120: + * - 8 steps per second + * - 48 midi clock pulses per second + * - 6 clock_mod_8 pulses per second + * For a 5KHz clock, it means diff_clock8 should be 833.333 + * + * === calc_tempo === + * One minute @ 5KHz is 30K pulses + * One beat is 4 steps, that is, 3 * div8 pulses. + * BPM = 30K / 3 / diff_clock8 = 10K / diff_clock8 + * + * === playing step-related counters === + * mod6_counter: divides 24PPQN to 4PPQN, that is, 1 pulse per step. + * mod12_counter: divides 24PPQN to 2PPQN, that is, 1/2 pulses per step. + * step_counter: a single step [1..4], reset at 5 + * div16th_counter: same speed as step_counter, only reset at init or overflow. + * div32th_counter: 1/2 step. + * div96th_counter: 1/6 step, one MIDI_CLOCK pulse. + * div192th_counter: 1/12 step, half MIDI_CLOCK pulse. + */ + ALWAYS_INLINE() void incrementCounters() { mod8_free_counter++; if (mod8_free_counter == 8) { diff_clock8 = midi_clock_diff(last_clock8, clock); last_clock8 = clock; - div196_time = diff_clock8 / 16; + div192_time = diff_clock8 / 16; mod8_free_counter = 0; } - if (state == STARTED) { + if (state == STARTED) { div96th_counter++; mod6_counter++; mod12_counter++; - mod3_counter++; div192th_counter++; - if (mod3_counter == 3) { - mod3_counter = 0; - } if (mod6_counter == 6) { + // one step step_counter++; mod6_counter = 0; mod12_counter = 0; div16th_counter++; div32th_counter++; // div32th counter should be at most 2x div16th_counter + // on div16th overflow, also reset 32th, 96th and 192th. if (div16th_counter == 0) { div32th_counter = 0; div96th_counter = 0; @@ -344,6 +374,7 @@ class MidiClockClass { } else if (mod6_counter == 3) { div32th_counter++; } + if (step_counter == 5) { step_counter = 1; beat_counter++; @@ -355,13 +386,13 @@ class MidiClockClass { if (bar_counter == 101) { bar_counter = 1; } - } - else if (state == STARTING && (mode == INTERNAL_MIDI || useImmediateClock)) { + } else if (state == STARTING && + (mode == INTERNAL_MIDI || useImmediateClock)) { state = STARTED; callCallbacks(); } - } + void updateClockInterval(); bool clock_less_than(uint16_t a, uint16_t b); bool clock_less_than(uint32_t a, uint32_t b); diff --git a/avr/cores/megacommand/MidiUart.cpp b/avr/cores/megacommand/MidiUart.cpp index ff0497abf..1b04037b6 100644 --- a/avr/cores/megacommand/MidiUart.cpp +++ b/avr/cores/megacommand/MidiUart.cpp @@ -188,7 +188,7 @@ ISR(USART0_RX_vect) { if (MIDI_IS_REALTIME_STATUS_BYTE(c)) { MidiUart.recvActiveSenseTimer = 0; - if (((MidiClock.mode == MidiClock.EXTERNAL_UART1))) { + if (MidiClock.mode == MidiClock.EXTERNAL_UART1) { if (c == MIDI_CLOCK) { MidiClock.handleClock(); diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index 8426d8248..c90c9ad2c 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -35,15 +35,15 @@ void my_init_ram(void) __attribute__((naked)) __attribute__((used)) __attribute__((section(".init3"))); void my_init_ram(void) { - // Set PL6 as output - // - #ifdef MEGACOMMAND +// Set PL6 as output +// +#ifdef MEGACOMMAND DDRL |= _BV(PL6); PORTL &= ~(_BV(PL6)); - #else +#else DDRB |= _BV(PB0); PORTB &= ~(_BV(PB0)); - #endif +#endif XMCRA |= _BV(SRE); // MCUCR |= _BV(SRE); // uint8_t *ptr = 0x2000; @@ -117,12 +117,12 @@ void timer_init(void) { TCCR1B |= (1 << WGM12); // Prescaler 64 TCCR1B |= (1 << CS11) | (1 << CS10); - // Output Compare Match A Interrupt Enable - #ifdef MEGACOMMAND +// Output Compare Match A Interrupt Enable +#ifdef MEGACOMMAND TIMSK1 |= (1 << OCIE1A); - #else +#else TIMSK |= (1 << OCIE1A); - #endif +#endif // TCCR2A = _BV(WGM20) | _BV(WGM21) | _BV(CS20) | _BV(CS21); // ) | _BV(CS21); // // | _BV(COM21); @@ -135,13 +135,12 @@ void timer_init(void) { TCCR3B |= (1 << WGM32); // Prescaler 64 TCCR3B |= (1 << CS31) | (1 << CS30); - // Output Compare Match A Interrupt Enable - #ifdef MEGACOMMAND +// Output Compare Match A Interrupt Enable +#ifdef MEGACOMMAND TIMSK3 |= (1 << OCIE3A); - #else +#else ETIMSK |= (1 << OCIE3A); - #endif - +#endif } void init(void) { @@ -156,7 +155,7 @@ void init(void) { /* move interrupts to bootloader section */ MCUCR = _BV(IVCE); - //Enable External SRAM + // Enable External SRAM MCUCR = _BV(SRE); // activate lever converter @@ -195,19 +194,19 @@ ISR(TIMER1_COMPA_vect) { select_bank(0); clock++; - MidiClock.div196th_countdown++; + MidiClock.div192th_countdown++; if (MidiClock.state == 2) { - if (MidiClock.div196th_countdown >= MidiClock.div196_time) { + if (MidiClock.div192th_countdown >= MidiClock.div192_time) { if (MidiClock.div192th_counter != MidiClock.div192th_counter_last) { - MidiClock.increment192Counter(); - MidiClock.div196th_countdown = 0; - MidiClock.div192th_counter_last = MidiClock.div192th_counter; - if ((enable_clock_callbacks)) { - MidiClock.callCallbacks(); + MidiClock.increment192Counter(); + MidiClock.div192th_countdown = 0; + MidiClock.div192th_counter_last = MidiClock.div192th_counter; + if ((enable_clock_callbacks)) { + MidiClock.callCallbacks(); + } } } } - } } // XXX CMP to have better time @@ -250,8 +249,8 @@ ISR(TIMER3_COMPA_vect) { slowclock++; minuteclock++; if (minuteclock == 60000) { - minuteclock = 0; - clock_minutes++; + minuteclock = 0; + clock_minutes++; } if (abs(slowclock - lastRunningStatusReset) > 3000) { MidiUart.resetRunningStatus(); @@ -264,7 +263,6 @@ ISR(TIMER3_COMPA_vect) { #ifdef MIDIDUINO_POLL_GUI_IRQ gui_poll(); #endif - } /* uint8_t sysexBuf[5500]; @@ -273,8 +271,8 @@ uint8_t sysexBuf2[2800]; MidiClass Midi2(&MidiUart2, sysexBuf2, sizeof(sysexBuf2)); */ -MidiClass Midi(&MidiUart,NULL,SYSEX1_DATA_LEN, BANK1_SYSEX1_DATA_START); -MidiClass Midi2(&MidiUart2,NULL,SYSEX2_DATA_LEN, BANK1_SYSEX2_DATA_START); +MidiClass Midi(&MidiUart, NULL, SYSEX1_DATA_LEN, BANK1_SYSEX1_DATA_START); +MidiClass Midi2(&MidiUart2, NULL, SYSEX2_DATA_LEN, BANK1_SYSEX2_DATA_START); bool enable_clock_callbacks = true; @@ -333,12 +331,12 @@ int main(void) { DEBUG_INIT(); - // Set SD card select HIGH before initialising OLED. - #ifdef MEGACOMMAND +// Set SD card select HIGH before initialising OLED. +#ifdef MEGACOMMAND PORTB |= (1 << PB0); - #else +#else PORTE |= (1 << PE7); - #endif +#endif setup(); for (;;) { loop(); From 6750cabbd4d2465460450125c843f16ea94675b0 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 5 Nov 2019 02:25:18 +0800 Subject: [PATCH 223/469] update route page to new gui --- avr/cores/megacommand/MCL/MCLGUI.h | 4 ++ avr/cores/megacommand/MCL/RoutePage.cpp | 89 ++++++++++++++++++++++--- avr/cores/megacommand/MCL/RoutePage.h | 16 +++-- avr/cores/megacommand/MCL/SeqPage.cpp | 6 +- avr/cores/megacommand/MCL/SeqPage.h | 4 -- 5 files changed, 99 insertions(+), 20 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 5ea03f877..4adc44c61 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -99,6 +99,10 @@ class MCLGUI { static constexpr uint8_t pane_trackid_x = 15; static constexpr uint8_t pane_trackid_y = 8; + static constexpr uint8_t seq_x0 = 32; + static constexpr uint8_t led_y = 22; + static constexpr uint8_t trig_y = 26; + static constexpr uint8_t knob_x0 = 31; static constexpr uint8_t knob_w = 24; static constexpr uint8_t knob_xend = 127; diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index d846a3f3e..06ebfa040 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -1,5 +1,5 @@ -#include "MCL.h" #include "RoutePage.h" +#include "MCL.h" void RoutePage::setup() {} void RoutePage::init() { @@ -12,6 +12,8 @@ void RoutePage::set_level(int curtrack, int value) { MD.setTrackParam(curtrack, 33, value); // in_sysex = 0; } + +#ifndef OLED_DISPLAY void RoutePage::draw_routes(uint8_t line_number) { if (line_number == 0) { GUI.setLine(GUI.LINE1); @@ -44,6 +46,40 @@ void RoutePage::draw_routes(uint8_t line_number) { /*Display the routes*/ GUI.put_string_at(0, str); } +#else +void RoutePage::draw_routes() { + mcl_gui.draw_trigs(MCLGUI::seq_x0, MCLGUI::trig_y, 0, 0, -1, 16); + + char cur; + + oled_display.setFont(&TomThumb); + + /*Display 16 track routes on screen, + For 16 tracks check to see if there is a route*/ + for (int i = 0; i < 16; i++) { + + if (mcl_cfg.routing[i] != 6) { + cur = (char)'A' + mcl_cfg.routing[i]; + auto x = MCLGUI::seq_x0 + i * (MCLGUI::seq_w + 1); + oled_display.setCursor(x + 1, MCLGUI::trig_y + 5); + + if (note_interface.notes[i] > 0 && note_interface.notes[i] != 3) { + oled_display.fillRect(x, MCLGUI::trig_y, MCLGUI::seq_w, MCLGUI::trig_h, + WHITE); + oled_display.setTextColor(BLACK); + } else { + oled_display.fillRect(x, MCLGUI::trig_y, MCLGUI::seq_w, MCLGUI::trig_h, + BLACK); + oled_display.setTextColor(WHITE); + } + + oled_display.print(cur); + } + } + + oled_display.setTextColor(WHITE); +} +#endif void RoutePage::toggle_route(int i, uint8_t routing) { if (mcl_cfg.routing[i] != 6) { @@ -53,6 +89,7 @@ void RoutePage::toggle_route(int i, uint8_t routing) { } MD.setTrackRouting(i, mcl_cfg.routing[i]); } + void RoutePage::toggle_routes_batch(bool solo) { uint16_t quantize_mute; quantize_mute = 1 << encoders[1]->getValue(); @@ -79,21 +116,19 @@ void RoutePage::toggle_routes_batch(bool solo) { if ((note_interface.notes[i] == 3)) { toggle_route(i, encoders[0]->cur); } - } - else { - uint8_t routing_last = mcl_cfg.routing[i]; + } else { + uint8_t routing_last = mcl_cfg.routing[i]; if (note_interface.notes[i] == 3) { mcl_cfg.routing[i] = 6; - } - else { + } else { if (mcl_cfg.routing[i] == 6) { - mcl_cfg.routing[i] = encoders[0]->cur; + mcl_cfg.routing[i] = encoders[0]->cur; } } if (mcl_cfg.routing[i] != routing_last) { MD.setTrackRouting(i, mcl_cfg.routing[i]); } - }/// + } /// // note_interface.notes[i] = 0; // trackinfo_page.display(); } @@ -114,6 +149,7 @@ void RoutePage::update_globals() { } } +#ifndef OLED_DISPLAY void RoutePage::display() { GUI.clearLines(); GUI.setLine(GUI.LINE2); @@ -139,13 +175,50 @@ void RoutePage::display() { GUI.put_value_at2(14, step_count); draw_routes(0); } +#else +void RoutePage::display() { + uint8_t x; + + auto *oldfont = oled_display.getFont(); + oled_display.clearDisplay(); + + uint8_t step_count = + (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - + (64 * + ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); + mcl_gui.draw_panel_number(step_count); + mcl_gui.draw_panel_status(false, MidiClock.state == 2); + mcl_gui.draw_panel_labels("ROUTE", ""); + mcl_gui.draw_knob_frame(); + + char str_tmp[2] = "0"; + str_tmp[0] = encoders[0]->cur + 'A'; + mcl_gui.draw_knob(0, "ROUTE", str_tmp); + + char Q[4] = {'\0'}; + if (encoders[1]->getValue() == 0) { + strcpy(Q, "--"); + } else { + x = 1 << encoders[1]->getValue(); + itoa(x, Q, 10); + } + mcl_gui.draw_knob(1, "QUANT", Q); + + draw_routes(); + oled_display.display(); + oled_display.setFont(oldfont); +} +#endif + bool RoutePage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { uint8_t track = event->source - 128; if (midi_active_peering.get_device(event->port) != DEVICE_MD) { return true; } +#ifndef OLED_DISPLAY draw_routes(0); +#endif /* if (event->mask == EVENT_BUTTON_PRESSED) { if ((encoders[2]->getValue() == 0)) { diff --git a/avr/cores/megacommand/MCL/RoutePage.h b/avr/cores/megacommand/MCL/RoutePage.h index f1d77dacd..6dcd13096 100644 --- a/avr/cores/megacommand/MCL/RoutePage.h +++ b/avr/cores/megacommand/MCL/RoutePage.h @@ -11,15 +11,21 @@ class RoutePage : public LightPage { RoutePage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage( e1, e2, e3 ,e4) { } + + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void cleanup(); + void toggle_route(int i, uint8_t routing); void toggle_routes_batch(bool solo = false); void set_level(int curtrack, int value); +#ifndef OLED_DISPLAY void draw_routes(uint8_t line_number); - bool handleEvent(gui_event_t *event); - void display(); - void setup(); - void init(); +#else + void draw_routes(); +#endif void update_globals(); - void cleanup(); }; #endif /* ROUTEPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 023610b69..058388b21 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -419,7 +419,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, void SeqPage::draw_lock_mask(uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step) { - mcl_gui.draw_leds(seq_x0, led_y, offset, lock_mask, step_count, length, show_current_step); + mcl_gui.draw_leds(MCLGUI::seq_x0, MCLGUI::led_y, offset, lock_mask, step_count, length, show_current_step); } void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { @@ -428,7 +428,7 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { } void SeqPage::draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step) { - mcl_gui.draw_trigs(seq_x0, trig_y, offset, pattern_mask, step_count, length); + mcl_gui.draw_trigs(MCLGUI::seq_x0, MCLGUI::trig_y, offset, pattern_mask, step_count, length); } void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, @@ -439,7 +439,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, } #ifdef EXT_TRACKS else { - mcl_gui.draw_ext_track(seq_x0, trig_y, offset, last_ext_track, show_current_step); + mcl_gui.draw_ext_track(MCLGUI::seq_x0, MCLGUI::trig_y, offset, last_ext_track, show_current_step); } #endif } diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index a95395493..f0d664581 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -61,10 +61,6 @@ class SeqPage : public LightPage { static constexpr uint8_t pidx_w = 6; static constexpr uint8_t pidx_h = 3; - static constexpr uint8_t seq_x0 = 32; - static constexpr uint8_t led_y = 22; - static constexpr uint8_t trig_y = 26; - }; #endif /* SEQPAGE_H__ */ From 48f8c0310eb6833d5a831c24f88e9b11de54c91b Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 5 Nov 2019 03:07:45 +0800 Subject: [PATCH 224/469] minor touch --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index d51fb9f99..9c2765163 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -39,7 +39,7 @@ const PageSelectEntry Entries[] PROGMEM = { {"REVERB", &fx_page_b, 10, 4}, {"RAM-1", &ram_page_a, 11, 5}, - {"RAM-2", &fx_page_b, 12, 5}, + {"RAM-2", &ram_page_b, 12, 5}, {"LFO", &lfo_page, 13, 6}, From 64a923af1ca489204ada78ac722b4de494fec9d2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 5 Nov 2019 21:01:44 +1100 Subject: [PATCH 225/469] forgot to check-in? --- avr/cores/megacommand/MCL/AuxPages.cpp | 4 ++-- avr/cores/megacommand/MCL/RAMPage.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index 3d7bbbc2d..1cf4422e0 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -41,10 +41,10 @@ fx_param_t fx_reverb_params[8] = { FXPage fx_page_a(&fx_param1, &fx_param2, &fx_param3, &fx_param4, - (fx_param_t*) &fx_echo_params, 8, "ECHO"); + (fx_param_t*) &fx_echo_params, 8, "ECHO", 0); FXPage fx_page_b(&fx_param1, &fx_param2, &fx_param3, &fx_param4, - (fx_param_t*) &fx_reverb_params, 8, "REVERB"); + (fx_param_t*) &fx_reverb_params, 8, "REVERB", 1); LFOPage lfo_page(&(mcl_seq.lfo_tracks[0]), &fx_param1, &fx_param2, &fx_param3, &fx_param4); diff --git a/avr/cores/megacommand/MCL/RAMPage.cpp b/avr/cores/megacommand/MCL/RAMPage.cpp index 9f2b32172..d15d9df9a 100644 --- a/avr/cores/megacommand/MCL/RAMPage.cpp +++ b/avr/cores/megacommand/MCL/RAMPage.cpp @@ -546,7 +546,7 @@ void RAMPage::display() { float remain; auto oldfont = oled_display.getFont(); oled_display.setFont(); - oled_display.setCursor(34, 24); + oled_display.setCursor(28, 24); switch (RAMPage::rec_states[page_id]) { case STATE_QUEUE: oled_display.print(" [Queue]"); From 89aa38438a12bdcad6d7b3cdcb1cd6a3ebe15ed9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 5 Nov 2019 21:46:16 +1100 Subject: [PATCH 226/469] add route icon --- art/sprites/route_icon.png | Bin 0 -> 306 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/route_icon.png diff --git a/art/sprites/route_icon.png b/art/sprites/route_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5167e2dd0cec240757b5d01b139ee4a17deb4c6d GIT binary patch literal 306 zcmV-20nPr2P)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#>`6pHR7ef&Q$Y^HFbGSg{r}6^EX|d1NQ^2@d)xE4jrl|T zTnUVk57v7>N-CIif=XcpgHDzcAgSHL&z>txIzdMCZ43)NmR%XUg3)He1lhPAb01_` zj3Pn6!K*@z>zSl#!-_Pq>pa~e-*MJq1;YlzidYCz5;MjE`eztoVW$<5fFKIdhpk|U zp9GlLWy6Ygkt)yc1h7}%t;U#B>v{D#MVJ5|R0#;G+U!CE0Vm1|h+&SFakoK$-8-o2 zXZ1ngs+){J#Zm7ZK|sMcgU}1yXAR=OdlUa3jT>6p7oyi}FiaftH2?qr07*qoM6N<$ Ef~qNhc>n+a literal 0 HcmV?d00001 From 70ad6b3837b0bb1f796b7cfcd3f5b4db9f0f4e38 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 5 Nov 2019 22:15:42 +0800 Subject: [PATCH 227/469] add icon to route page --- art/sprites/Image.c | 87 ++++++++++++++++++++++ art/sprites/route_icon_2.png | Bin 0 -> 211 bytes art/sprites/route_icon_3.png | Bin 0 -> 228 bytes art/sprites/route_icon_3.xml | 5 ++ avr/cores/megacommand/MCL/MCLGUI.cpp | 94 ++++++++++++------------ avr/cores/megacommand/MCL/MCLGUI.h | 2 + avr/cores/megacommand/MCL/RoutePage.cpp | 16 ++-- 7 files changed, 152 insertions(+), 52 deletions(-) create mode 100644 art/sprites/Image.c create mode 100644 art/sprites/route_icon_2.png create mode 100644 art/sprites/route_icon_3.png create mode 100644 art/sprites/route_icon_3.xml diff --git a/art/sprites/Image.c b/art/sprites/Image.c new file mode 100644 index 000000000..a1902fe19 --- /dev/null +++ b/art/sprites/Image.c @@ -0,0 +1,87 @@ + +/******************************************************************************* +* generated by lcd-image-converter rev.129188f from 2019-07-09 00:20:51 +0500 +* image +* filename: C:/Users/Yatao/Documents/Arduino/hardware/MIDICtrl20_MegaCommand/art/sprites/route_icon_3.xml +* name: Image +* +* preset name: Monochrome +* data block size: 8 bit(s), uint8_t +* RLE compression enabled: no +* conversion type: Monochrome, Diffuse Dither 128 +* split to rows: yes +* bits per pixel: 1 +* +* preprocess: +* main scan direction: top_to_bottom +* line scan direction: forward +* inverse: no +*******************************************************************************/ + +/* + typedef struct { + const uint8_t *data; + uint16_t width; + uint16_t height; + uint8_t dataSize; + } tImage; +*/ +#include + + + +static const uint8_t image_data_Image[75] = { + // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙ + // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙ + // ∙∙∙███∙∙∙∙███∙∙∙∙███∙∙∙∙ + // ∙∙∙███∙∙∙∙███∙∙∙∙███∙∙∙∙ + // ∙∙█████∙∙█████∙∙█████∙∙∙ + // ████████████████████████ + // ████████████████████████ + // ∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙ + // ∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙ + // ∙∙███∙█████████∙∙∙█████∙ + // ∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙ + // ∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙ + // ∙███∙███∙███∙███∙███∙███ + // ∙█∙█∙█∙█∙█∙█∙█∙█∙█∙█∙█∙█ + // ∙███∙███∙███∙███∙███∙███ + // ∙∙█∙∙∙∙∙∙∙█∙∙∙∙∙∙∙█∙∙∙∙∙ + // ∙███∙███∙███∙███∙███∙███ + // ∙█∙█∙█∙███∙█∙█∙███∙███∙█ + // ∙███∙███∙███∙███∙███∙███ + // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙ + // ∙∙∙██∙∙███∙█∙█∙███∙███∙∙ + // ∙∙∙█∙█∙█∙█∙█∙█∙∙█∙∙█∙∙∙∙ + // ∙∙∙█∙█∙█∙█∙█∙█∙∙█∙∙███∙∙ + // ∙∙∙██∙∙█∙█∙█∙█∙∙█∙∙█∙∙∙∙ + // ∙∙∙█∙█∙███∙███∙∙█∙∙███∙∙ + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x1c, 0x38, 0x70, + 0x1c, 0x38, 0x70, + 0x3e, 0x7c, 0xf8, + 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + 0x08, 0x10, 0x20, + 0x08, 0x10, 0x20, + 0x3b, 0xfe, 0x3e, + 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, + 0x77, 0x77, 0x77, + 0x55, 0x55, 0x55, + 0x77, 0x77, 0x77, + 0x20, 0x20, 0x20, + 0x77, 0x77, 0x77, + 0x55, 0xd5, 0xdd, + 0x77, 0x77, 0x77, + 0x00, 0x00, 0x00, + 0x19, 0xd5, 0xdc, + 0x15, 0x54, 0x90, + 0x15, 0x54, 0x9c, + 0x19, 0x54, 0x90, + 0x15, 0xdc, 0x9c +}; +const tImage Image = { image_data_Image, 24, 25, + 8 }; + diff --git a/art/sprites/route_icon_2.png b/art/sprites/route_icon_2.png new file mode 100644 index 0000000000000000000000000000000000000000..dbe11e603a9e679a472e368a792d4fc271735b03 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^5@XdO%(tD literal 0 HcmV?d00001 diff --git a/art/sprites/route_icon_3.png b/art/sprites/route_icon_3.png new file mode 100644 index 0000000000000000000000000000000000000000..dfd289b5b5f92c7b692709997f8d81cff3c7eeca GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^5str|{jZc`8aK zodr44Yq^5&s#KoINq90V;&o)4YEs4PjAt?Ng+i~^${t#JVVC*B$0c81 zTz7r1(C%yhE?SFCioFnj_VvWov+pjyy{*BIQDDy&r87Fc>(vsuJ|3$5^EKn$-BOXe YtT*?6H4Q#53UncZr>mdKI;Vst0Jnu)B>(^b literal 0 HcmV?d00001 diff --git a/art/sprites/route_icon_3.xml b/art/sprites/route_icon_3.xml new file mode 100644 index 000000000..17c44ba82 --- /dev/null +++ b/art/sprites/route_icon_3.xml @@ -0,0 +1,5 @@ + + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAZCAIAAACkSXkKAAAACXBIWXMAABnWAAAZ1gEY0crtAAAAlklEQVQ4jc1UQQ7AIAijZv//MjvoGCoYh5qMw9IVA6URif4WEMTMhUKETJsVSRPdap4kIjSJcGwbrQpT3SR5zGxg5JpY61leJWJ462jmXF9xEuqtHcKl0LpH1g9F+PMe5RNjXmPXo37XvRK71v7ROxaSXdBfzeuRq5vdp9u2gE4BELy0Iswsja8mYQrxwn389Wju3XUU3QXZv/U1U88VAAAAAElFTkSuQmCC + + diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 666540d80..789c2954f 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -73,7 +73,7 @@ void MCLGUI::draw_knob(uint8_t i, const char *title, const char *text) { draw_text_encoder(x, knob_y0, title, text); } -void MCLGUI::draw_knob(uint8_t i, Encoder* enc, const char* title) { +void MCLGUI::draw_knob(uint8_t i, Encoder *enc, const char *title) { uint8_t x = knob_x0 + i * knob_w; draw_light_encoder(x + 6, 6, enc, title); } @@ -104,8 +104,8 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { oled_display.setTextColor(BLACK); auto len = strlen(title_buf) * 4; - oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); - //oled_display.setCursor(s_title_x + 2, s_menu_y + 3); + oled_display.setCursor(s_title_x + (s_title_w - len) / 2, s_menu_y + 3); + // oled_display.setCursor(s_title_x + 2, s_menu_y + 3); oled_display.println(title_buf); oled_display.setTextColor(WHITE); if (!deferred_display) { @@ -598,55 +598,59 @@ void MCLGUI::draw_panel_number(uint8_t number) { // ================ SPRITES ================ -const unsigned char encoder_small_0 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, - 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_0[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0x80, 0x20, 0x4e, 0x40, 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder1', 11x11px -const unsigned char encoder_small_1 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x5c, 0x40, - 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_1[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0x80, 0x20, 0x5c, 0x40, 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder2', 11x11px -const unsigned char encoder_small_2 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x90, 0x20, 0x58, 0x40, - 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_2[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0x90, 0x20, 0x58, 0x40, 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder3', 11x11px -const unsigned char encoder_small_3 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_3[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0xb0, 0x20, 0x58, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder4', 11x11px -const unsigned char encoder_small_4 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_4[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, + 0x20, 0xb0, 0x20, 0x58, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder5', 11x11px -const unsigned char encoder_small_5 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x50, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_5[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, + 0x20, 0xb0, 0x20, 0x50, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder6', 11x11px -const unsigned char encoder_small_6 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_6[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, + 0x20, 0xb0, 0x20, 0x40, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'gateboxlarge', 24x25px -const unsigned char icon_gatebox [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x80, 0x00, 0x7e, 0xe0, 0x01, 0xfe, 0xe0, 0x07, - 0xff, 0x90, 0x03, 0xfe, 0x70, 0x0c, 0xf9, 0xf0, 0x0f, 0x26, 0xf0, 0x0f, 0xdc, 0xf0, 0xef, 0xd3, - 0x37, 0x0f, 0xdf, 0xc0, 0x0f, 0x9f, 0xf0, 0x0e, 0x5f, 0xe0, 0x09, 0xdf, 0x80, 0x07, 0xde, 0x00, - 0x01, 0xd8, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xef, 0x48, 0x21, 0x29, 0x48, 0x2d, - 0xe9, 0x30, 0x25, 0x29, 0x48, 0x3d, 0xef, 0x48, 0x00, 0x00, 0x00 -}; +const unsigned char icon_gatebox[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x80, 0x00, 0x7e, + 0xe0, 0x01, 0xfe, 0xe0, 0x07, 0xff, 0x90, 0x03, 0xfe, 0x70, 0x0c, + 0xf9, 0xf0, 0x0f, 0x26, 0xf0, 0x0f, 0xdc, 0xf0, 0xef, 0xd3, 0x37, + 0x0f, 0xdf, 0xc0, 0x0f, 0x9f, 0xf0, 0x0e, 0x5f, 0xe0, 0x09, 0xdf, + 0x80, 0x07, 0xde, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x3d, 0xef, 0x48, 0x21, 0x29, 0x48, 0x2d, 0xe9, 0x30, + 0x25, 0x29, 0x48, 0x3d, 0xef, 0x48, 0x00, 0x00, 0x00}; // 'rythmecho', 24x25px -const unsigned char icon_rhytmecho [] PROGMEM = { - 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x3f, 0xff, 0xfe, 0x3f, 0xff, 0xfc, 0x00, 0x01, 0xfc, 0x07, - 0xe1, 0xf8, 0x07, 0xe3, 0xf8, 0x0f, 0xc7, 0xf0, 0x0f, 0xff, 0xf0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, - 0x00, 0x3f, 0x7f, 0x80, 0x3f, 0x1f, 0xc0, 0x7e, 0x0f, 0xe0, 0x7e, 0x07, 0xf0, 0x00, 0x00, 0x00, - 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x63, 0x1b, 0x6c, 0x7b, 0x1f, 0x6c, 0x7b, 0x1f, 0x6c, 0x63, - 0x1b, 0x6c, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x00, 0x00, 0x00 -}; +const unsigned char icon_rhytmecho[] PROGMEM = { + 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x3f, 0xff, 0xfe, 0x3f, 0xff, + 0xfc, 0x00, 0x01, 0xfc, 0x07, 0xe1, 0xf8, 0x07, 0xe3, 0xf8, 0x0f, + 0xc7, 0xf0, 0x0f, 0xff, 0xf0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0x00, + 0x3f, 0x7f, 0x80, 0x3f, 0x1f, 0xc0, 0x7e, 0x0f, 0xe0, 0x7e, 0x07, + 0xf0, 0x00, 0x00, 0x00, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x63, + 0x1b, 0x6c, 0x7b, 0x1f, 0x6c, 0x7b, 0x1f, 0x6c, 0x63, 0x1b, 0x6c, + 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x00, 0x00, 0x00}; + +const unsigned char icon_route[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x38, 0x70, 0x1c, 0x38, + 0x70, 0x3e, 0x7c, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, + 0x10, 0x20, 0x08, 0x10, 0x20, 0x3b, 0xfe, 0x3e, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x77, 0x77, 0x77, 0x55, 0x55, 0x55, 0x77, 0x77, + 0x77, 0x20, 0x20, 0x20, 0x77, 0x77, 0x77, 0x55, 0xd5, 0xdd, 0x77, + 0x77, 0x77, 0x00, 0x00, 0x00, 0x19, 0xd5, 0xdc, 0x15, 0x54, 0x90, + 0x15, 0x54, 0x9c, 0x19, 0x54, 0x90, 0x15, 0xdc, 0x9c}; diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 4adc44c61..ea8489ccb 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -161,5 +161,7 @@ extern const unsigned char encoder_small_6 []; extern const unsigned char icon_gatebox []; // 'rythmecho', 24x25px extern const unsigned char icon_rhytmecho []; +// 'route', 24x25px +extern const unsigned char icon_route []; #endif /* MCLGUI_H__ */ diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index 06ebfa040..15914842c 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -181,14 +181,8 @@ void RoutePage::display() { auto *oldfont = oled_display.getFont(); oled_display.clearDisplay(); + oled_display.drawBitmap(0, 0, icon_route, 24, 25, WHITE); - uint8_t step_count = - (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - - (64 * - ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); - mcl_gui.draw_panel_number(step_count); - mcl_gui.draw_panel_status(false, MidiClock.state == 2); - mcl_gui.draw_panel_labels("ROUTE", ""); mcl_gui.draw_knob_frame(); char str_tmp[2] = "0"; @@ -204,6 +198,14 @@ void RoutePage::display() { } mcl_gui.draw_knob(1, "QUANT", Q); + uint8_t step_count = + (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - + (64 * + ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); + + itoa(step_count, Q, 10); + mcl_gui.draw_knob(3, "STEP", Q); + draw_routes(); oled_display.display(); oled_display.setFont(oldfont); From d0ff7fe62b03dfbf287d888e5dd314a98fb61e1f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 5 Nov 2019 22:19:41 +0800 Subject: [PATCH 228/469] cleanup --- art/sprites/Image.c | 87 ------------------------- avr/cores/megacommand/MCL/MixerPage.cpp | 2 + avr/cores/megacommand/MCL/MixerPage.h | 13 ++-- 3 files changed, 9 insertions(+), 93 deletions(-) delete mode 100644 art/sprites/Image.c diff --git a/art/sprites/Image.c b/art/sprites/Image.c deleted file mode 100644 index a1902fe19..000000000 --- a/art/sprites/Image.c +++ /dev/null @@ -1,87 +0,0 @@ - -/******************************************************************************* -* generated by lcd-image-converter rev.129188f from 2019-07-09 00:20:51 +0500 -* image -* filename: C:/Users/Yatao/Documents/Arduino/hardware/MIDICtrl20_MegaCommand/art/sprites/route_icon_3.xml -* name: Image -* -* preset name: Monochrome -* data block size: 8 bit(s), uint8_t -* RLE compression enabled: no -* conversion type: Monochrome, Diffuse Dither 128 -* split to rows: yes -* bits per pixel: 1 -* -* preprocess: -* main scan direction: top_to_bottom -* line scan direction: forward -* inverse: no -*******************************************************************************/ - -/* - typedef struct { - const uint8_t *data; - uint16_t width; - uint16_t height; - uint8_t dataSize; - } tImage; -*/ -#include - - - -static const uint8_t image_data_Image[75] = { - // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙ - // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙ - // ∙∙∙███∙∙∙∙███∙∙∙∙███∙∙∙∙ - // ∙∙∙███∙∙∙∙███∙∙∙∙███∙∙∙∙ - // ∙∙█████∙∙█████∙∙█████∙∙∙ - // ████████████████████████ - // ████████████████████████ - // ∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙ - // ∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙∙█∙∙∙∙∙ - // ∙∙███∙█████████∙∙∙█████∙ - // ∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙ - // ∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙∙∙█∙ - // ∙███∙███∙███∙███∙███∙███ - // ∙█∙█∙█∙█∙█∙█∙█∙█∙█∙█∙█∙█ - // ∙███∙███∙███∙███∙███∙███ - // ∙∙█∙∙∙∙∙∙∙█∙∙∙∙∙∙∙█∙∙∙∙∙ - // ∙███∙███∙███∙███∙███∙███ - // ∙█∙█∙█∙███∙█∙█∙███∙███∙█ - // ∙███∙███∙███∙███∙███∙███ - // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙ - // ∙∙∙██∙∙███∙█∙█∙███∙███∙∙ - // ∙∙∙█∙█∙█∙█∙█∙█∙∙█∙∙█∙∙∙∙ - // ∙∙∙█∙█∙█∙█∙█∙█∙∙█∙∙███∙∙ - // ∙∙∙██∙∙█∙█∙█∙█∙∙█∙∙█∙∙∙∙ - // ∙∙∙█∙█∙███∙███∙∙█∙∙███∙∙ - 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x1c, 0x38, 0x70, - 0x1c, 0x38, 0x70, - 0x3e, 0x7c, 0xf8, - 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, - 0x08, 0x10, 0x20, - 0x08, 0x10, 0x20, - 0x3b, 0xfe, 0x3e, - 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, - 0x77, 0x77, 0x77, - 0x55, 0x55, 0x55, - 0x77, 0x77, 0x77, - 0x20, 0x20, 0x20, - 0x77, 0x77, 0x77, - 0x55, 0xd5, 0xdd, - 0x77, 0x77, 0x77, - 0x00, 0x00, 0x00, - 0x19, 0xd5, 0xdc, - 0x15, 0x54, 0x90, - 0x15, 0x54, 0x9c, - 0x19, 0x54, 0x90, - 0x15, 0xdc, 0x9c -}; -const tImage Image = { image_data_Image, 24, 25, - 8 }; - diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index ac50c8a84..ae5f19882 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -18,6 +18,7 @@ void MixerPage::setup() { oled_display.clearDisplay(); #endif } + void MixerPage::init() { level_pressmode = 0; encoders[0]->cur = 60; @@ -46,6 +47,7 @@ void MixerPage::init() { } #endif } + void MixerPage::cleanup() { // md_exploit.off(); #ifdef OLED_DISPLAY diff --git a/avr/cores/megacommand/MCL/MixerPage.h b/avr/cores/megacommand/MCL/MixerPage.h index 11102fa30..c74426d12 100644 --- a/avr/cores/megacommand/MCL/MixerPage.h +++ b/avr/cores/megacommand/MCL/MixerPage.h @@ -35,16 +35,17 @@ class MixerPage : public LightPage { Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { } - bool handleEvent(gui_event_t *event); void adjust_param(Encoder *enc, uint8_t param); void draw_routes(uint8_t line_number); void draw_levels(); - void display(); - void loop(); void set_level(int curtrack, int value); - void setup(); - void init(); - void cleanup(); + + virtual bool handleEvent(gui_event_t *event); + virtual void display(); + virtual void loop(); + virtual void setup(); + virtual void init(); + virtual void cleanup(); }; #endif /* MIXERPAGE_H__ */ From e4ca6a6a75516b77e457df4ee2d0abd793d1083c Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Wed, 6 Nov 2019 01:41:05 +0800 Subject: [PATCH 229/469] implement mixer page --- art/sprites/mixer_icon.png | Bin 0 -> 202 bytes avr/cores/megacommand/MCL/MCLGUI.cpp | 18 ++ avr/cores/megacommand/MCL/MCLGUI.h | 2 + avr/cores/megacommand/MCL/MixerPage.cpp | 287 ++++++++++------------- avr/cores/megacommand/MCL/MixerPage.h | 6 +- avr/cores/megacommand/MCL/RoutePage.cpp | 6 +- avr/cores/megacommand/MCL/RoutePage.h | 2 + avr/cores/megacommand/MD/MDParams.hh | 1 + design/mixer.png | Bin 0 -> 546 bytes design/mixer.xml | 5 + design/mixer_icon.xml | 5 + {art/sprites => design}/route_icon_3.xml | 0 12 files changed, 170 insertions(+), 162 deletions(-) create mode 100644 art/sprites/mixer_icon.png create mode 100644 design/mixer.png create mode 100644 design/mixer.xml create mode 100644 design/mixer_icon.xml rename {art/sprites => design}/route_icon_3.xml (100%) diff --git a/art/sprites/mixer_icon.png b/art/sprites/mixer_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..34abd25938397408a9fefd71fa65361a2c3b9fd2 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^GC<7F!2~2LbS=ICDb50q$YKTt$!j3YC~@)BTcBW# zr;B5VMep4y2l*NdIGh{b{IB0BC+5kVJX_}N$+ueAY-J(2Cxkb{5|w zu79fL2c6U3og*M!@%c^(Ta3B6;hp1)4}D0!*}&HD@`R^*b9?XH~X=dNF8r($ye7}SAP5Lj}Nh@Xhandler = encoder_level_handle; encoders[1]->handler = encoder_filtf_handle; encoders[2]->handler = encoder_filtw_handle; encoders[3]->handler = encoder_filtq_handle; if (route_page.encoders[0]->cur == 0) { - route_page.encoders[0]->cur = 2; + route_page.encoders[0]->cur = 2; } create_chars_mixer(); #ifdef OLED_DISPLAY @@ -33,18 +54,12 @@ void MixerPage::init() { params[i][c] = MD.kit.params[i][MODEL_FLTF + c]; } } + #ifdef OLED_DISPLAY oled_display.clearDisplay(); - draw_routes(0); - - for (uint8_t i = 0; i < 16; i++) { - uint8_t scaled_level = - (uint8_t)(((float)MD.kit.levels[i] / (float)127) * (float)FADER_LEN); - - oled_display.drawRect(0 + i * 8, 12 + (FADER_LEN - scaled_level), 6, - scaled_level + 1, WHITE); - disp_levels[i] = 0; - } + oled_display.drawBitmap(1, 2, icon_mixer, 28, 15, WHITE); + set_display_mode(MODEL_LEVEL); + initializing = true; #endif } @@ -58,56 +73,6 @@ void MixerPage::cleanup() { midi_events.remove_callbacks(); } - -void MixerPage::draw_routes(uint8_t line_number) { - if (line_number == 0) { - GUI.setLine(GUI.LINE1); - } else { - GUI.setLine(GUI.LINE2); - } - /*Initialise the string with blank steps*/ - char str[17] = "----------------"; - - for (int i = 0; i < 16; i++) { - -#ifdef OLED_DISPLAY - if (note_interface.notes[i] > 0) { - - oled_display.fillRect(0 + i * 8, 2, 6, 6, WHITE); - } - - else if (mcl_cfg.routing[i] == 6) { - - oled_display.fillRect(0 + i * 8, 2, 6, 6, BLACK); - oled_display.drawRect(0 + i * 8, 2, 6, 6, WHITE); - - } - - else { - - oled_display.fillRect(0 + i * 8, 2, 6, 6, BLACK); - oled_display.drawLine(+i * 8, 5, 5 + (i * 8), 5, WHITE); - } - -#else - - str[i] = (char)219; - - if (mcl_cfg.routing[i] == 6) { - - str[i] = (char)'-'; - } - if (note_interface.notes[i] > 0) { - - str[i] = (char)255; - } -#endif - } -#ifndef OLED_DISPLAY - GUI.put_string_at(0, str); -#endif -} - void MixerPage::set_level(int curtrack, int value) { // in_sysex = 1; MD.kit.levels[curtrack] = value; @@ -120,49 +85,12 @@ void MixerPage::set_level(int curtrack, int value) { void MixerPage::loop() {} -void MixerPage::draw_levels() { - GUI.setLine(GUI.LINE2); - uint8_t scaled_level; - uint8_t scaled_level2; - char str[17] = " "; - for (int i = 0; i < 16; i++) { -// if (MD.kit.levels[i] > 120) { scaled_level = 8; } -// else if (MD.kit.levels[i] < 4) { scaled_level = 0; } -#ifdef OLED_DISPLAY - - scaled_level = - (uint8_t)(((float)MD.kit.levels[i] / (float)127) * (float)(FADER_LEN)) + - 1; - - scaled_level2 = - (uint8_t)(((float)disp_levels[i] / (float)127) * (float)(FADER_LEN)) + - 1; - - if (note_interface.notes[i] == 1) { - oled_display.fillRect(0 + i * 8, 13 + (FADER_LEN - scaled_level), 6, - scaled_level, WHITE); - } else { - - oled_display.fillRect(1 + i * 8, 14 + (FADER_LEN - scaled_level), 4, - FADER_LEN - scaled_level2, BLACK); - oled_display.fillRect(1 + i * 8, 13 + (FADER_LEN - scaled_level2), 4, - scaled_level2, WHITE); - } -#else - - scaled_level = (int)(((float)MD.kit.levels[i] / (float)127) * 7); - if (scaled_level == 7) { - str[i] = (char)(255); - } else if (scaled_level > 0) { - str[i] = (char)(scaled_level + 2); - } -#endif - } - GUI.put_string_at(0, str); -} +void MixerPage::draw_levels() {} void encoder_level_handle(Encoder *enc) { + mixer_page.set_display_mode(MODEL_LEVEL); + int dir = enc->getValue() - enc->old; int track_newval; @@ -183,35 +111,38 @@ void encoder_level_handle(Encoder *enc) { } // if ((MD.kit.levels[i] < 127) && (MD.kit.levels[i] > 0)) { mixer_page.set_level(i, track_newval); -#ifdef OLED_DISPLAY - uint8_t scaled_level = ((uint8_t)(((float)MD.kit.levels[i] / (float)127) * - (float)FADER_LEN)); - - oled_display.fillRect(0 + i * 8, 12, 6, FADER_LEN, BLACK); - oled_display.drawRect(0 + i * 8, 12 + (FADER_LEN - scaled_level), 6, - scaled_level + 1, WHITE); - -#endif } } enc->cur = 64 + dir; enc->old = 64; } + void encoder_filtf_handle(Encoder *enc) { mixer_page.adjust_param(enc, MODEL_FLTF); } + void encoder_filtw_handle(Encoder *enc) { mixer_page.adjust_param(enc, MODEL_FLTW); } + void encoder_filtq_handle(Encoder *enc) { mixer_page.adjust_param(enc, MODEL_FLTQ); } + void encoder_lastparam_handle(Encoder *enc) { mixer_page.adjust_param(enc, MD.midi_events.last_md_param); } void MixerPage::adjust_param(Encoder *enc, uint8_t param) { + if(initializing) { + if(param == MODEL_FLTQ) { + initializing = false; + } + } else { + set_display_mode(param); + } + int dir = enc->getValue() - enc->old; int newval; @@ -249,27 +180,79 @@ void MixerPage::adjust_param(Encoder *enc, uint8_t param) { enc->old = 64; } -void MixerPage::display() { - if (!classic_display) { - // oled_display.clearDisplay(); - } #ifndef OLED_DISPLAY +void MixerPage::display() { note_interface.draw_notes(0); if (!classic_display) { LCD.goLine(0); LCD.puts(GUI.lines[0].data); } + GUI.setLine(GUI.LINE2); + uint8_t scaled_level; + uint8_t scaled_level2; + char str[17] = " "; + for (int i = 0; i < 16; i++) { + str[i] = (char)219; -#endif -#ifdef OLED_DISPLAY - // mute_page.draw_mutes(0); -#endif - draw_levels(); -#ifdef OLED_DISPLAY - if (!classic_display) { - oled_display.display(); + if (mcl_cfg.routing[i] == 6) { + + str[i] = (char)'-'; + } + if (note_interface.notes[i] > 0) { + + str[i] = (char)255; + } } -#endif + GUI.put_string_at(0, str); + + uint8_t dec = MidiClock.get_tempo() / FADE_RATE; + for (uint8_t n = 0; n < 16; n++) { + if (disp_levels[n] < dec) { + disp_levels[n] = 0; + } else { + disp_levels[n] -= dec; + } + } +} +#else +void MixerPage::display() { + + auto oldfont = oled_display.getFont(); + mcl_gui.draw_panel_labels("MIXER", info_line2); + mcl_gui.clear_rightpane(); + route_page.draw_routes(); + + uint8_t fader_level; + uint8_t meter_level; + uint8_t fader_x = MCLGUI::seq_x0; + for (int i = 0; i < 16; i++) { + oled_display.fillRect(fader_x + 1, FADER_Y, 3, FADER_LEN, WHITE); + + switch(display_mode) { + case MODEL_LEVEL: + fader_level = MD.kit.levels[i]; + break; + default: + fader_level = MD.kit.params[i][display_mode]; + break; + } + + fader_level = (fader_level / 127.0f) * (FADER_LEN - 2); + meter_level = (disp_levels[i] / 127.0f) * (FADER_LEN - 2); + + if (note_interface.notes[i] != 1) { + // draw meter only if not pressed + oled_display.fillRect(fader_x + 2, FADER_Y + 1, 1, + FADER_LEN - meter_level - 2, BLACK); + } + + // draw fader knob + oled_display.fillRect(fader_x, FADER_Y + FADER_LEN - fader_level - 2, + MCLGUI::seq_w, 2, WHITE); + + fader_x += MCLGUI::seq_w + 1; + } + uint8_t dec = MidiClock.get_tempo() / FADE_RATE; for (uint8_t n = 0; n < 16; n++) { if (disp_levels[n] < dec) { @@ -278,7 +261,12 @@ void MixerPage::display() { disp_levels[n] -= dec; } } + + oled_display.display(); + oled_display.setFont(oldfont); } +#endif + bool MixerPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { uint8_t mask = event->mask; @@ -288,32 +276,16 @@ bool MixerPage::handleEvent(gui_event_t *event) { uint8_t track = event->source - 128; if (track > 16) { - return; + return false; } - if (event->mask == EVENT_BUTTON_PRESSED) { -#ifdef OLED_DISPLAY - - if (note_interface.notes[track] > 0) { - - oled_display.fillRect(0 + track * 8, 2, 6, 6, WHITE); - } - -#endif + if (event->mask == EVENT_BUTTON_PRESSED) { return true; } if (event->mask == EVENT_BUTTON_RELEASED) { +#ifndef OLED_DISPLAY note_interface.draw_notes(0); -#ifdef OLED_DISPLAY - uint8_t i = track; - uint8_t scaled_level = - (uint8_t)(((float)MD.kit.levels[i] / (float)127) * FADER_LEN); - - oled_display.fillRect(0 + i * 8, 12, 6, FADER_LEN, BLACK); - oled_display.drawRect(0 + i * 8, 12 + (FADER_LEN - scaled_level), 6, - scaled_level + 1, WHITE); - #endif if (note_interface.notes_all_off_md()) { @@ -324,22 +296,19 @@ bool MixerPage::handleEvent(gui_event_t *event) { route_page.toggle_routes_batch(true); } note_interface.init_notes(); -#ifdef OLED_DISPLAY - draw_routes(0); -#endif } return true; } } -/* - if (EVENT_PRESSED(event, Buttons.BUTTON4)) { - route_page.toggle_routes_batch(); - note_interface.init_notes(); -#ifdef OLED_DISPLAY - draw_routes(0); -#endif - } -*/ + /* + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + route_page.toggle_routes_batch(); + note_interface.init_notes(); + #ifdef OLED_DISPLAY + route_page.draw_routes(0); + #endif + } + */ if (EVENT_PRESSED(event, Buttons.BUTTON2)) { route_page.update_globals(); md_exploit.off(); @@ -357,7 +326,7 @@ bool MixerPage::handleEvent(gui_event_t *event) { MD.setTrackParam(i, MODEL_FLTF + c, params[i][c]); MD.kit.params[i][MODEL_FLTF + c] = params[i][c]; CLEAR_LOCK(); - } + } } } } @@ -369,8 +338,8 @@ bool MixerPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { if (note_interface.notes_count() == 0) { - route_page.update_globals(); - GUI.setPage(&grid_page); + route_page.update_globals(); + GUI.setPage(&grid_page); } return true; } diff --git a/avr/cores/megacommand/MCL/MixerPage.h b/avr/cores/megacommand/MCL/MixerPage.h index c74426d12..42a200e23 100644 --- a/avr/cores/megacommand/MCL/MixerPage.h +++ b/avr/cores/megacommand/MCL/MixerPage.h @@ -31,14 +31,18 @@ class MixerPage : public LightPage { uint8_t level_pressmode = 0; int8_t disp_levels[16]; uint8_t params[16][3]; + char info_line2[9]; + uint8_t display_mode; + bool initializing = false; MixerPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { } void adjust_param(Encoder *enc, uint8_t param); - void draw_routes(uint8_t line_number); + void draw_levels(); void set_level(int curtrack, int value); + void set_display_mode(uint8_t param); virtual bool handleEvent(gui_event_t *event); virtual void display(); diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index 15914842c..6d5742d62 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -181,7 +181,7 @@ void RoutePage::display() { auto *oldfont = oled_display.getFont(); oled_display.clearDisplay(); - oled_display.drawBitmap(0, 0, icon_route, 24, 25, WHITE); + oled_display.drawBitmap(0, 0, icon_route, 24, 18, WHITE); mcl_gui.draw_knob_frame(); @@ -204,7 +204,9 @@ void RoutePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, Q, 10); - mcl_gui.draw_knob(3, "STEP", Q); + strcpy(info_line2, "STEP "); + strcat(info_line2, Q); + mcl_gui.draw_panel_labels("ROUTE", info_line2); draw_routes(); oled_display.display(); diff --git a/avr/cores/megacommand/MCL/RoutePage.h b/avr/cores/megacommand/MCL/RoutePage.h index 6dcd13096..d72626dfe 100644 --- a/avr/cores/megacommand/MCL/RoutePage.h +++ b/avr/cores/megacommand/MCL/RoutePage.h @@ -12,6 +12,8 @@ class RoutePage : public LightPage { } + char info_line2[9]; + virtual bool handleEvent(gui_event_t *event); virtual void display(); virtual void setup(); diff --git a/avr/cores/megacommand/MD/MDParams.hh b/avr/cores/megacommand/MD/MDParams.hh index 3b80cc873..9d0400c23 100644 --- a/avr/cores/megacommand/MD/MDParams.hh +++ b/avr/cores/megacommand/MD/MDParams.hh @@ -734,6 +734,7 @@ #define MODEL_LFOS 21 #define MODEL_LFOD 22 #define MODEL_LFOM 23 +#define MODEL_LEVEL 33 #define MD_ECHO_TIME 0 #define MD_ECHO_MOD 1 diff --git a/design/mixer.png b/design/mixer.png new file mode 100644 index 0000000000000000000000000000000000000000..4371083d215b86ab1c98d3ea309e01ec71cbc89e GIT binary patch literal 546 zcmV+-0^R+IP)52H+yj?0fUn#j0{)dVjRJ(@K;w~KNSliS4hA2B(h9h*(ncVY zLFV5X#6mxP{)w>BRRO}4+@~ToNL6RFo7y=J>80Cfdzvf2W~k;_mDqxW@PGo^xl+5y z=D3~~q6$b^NjU|yGaU!9$JVC21j>Z@{uLERwAam;RZa`r?FFHhg$Snt#LmN=9>rF* z5M0gx4I&O5wm!EY{wq)2yUIZ`;aXLN%%?4R28g9pUgeq3akbcUxo5Jt|_Pr + + iVBORw0KGgoAAAANSUhEUgAAAIAAAAAgCAIAAABVQOdyAAAACXBIWXMAABnWAAAZ1gEY0crtAAAB1ElEQVRoge2Z25aDIAxFg8v//2XngQ7GBOQ0XETKfuiy3BoSDhBLtFj8Osdx8GfxtbBqcc9ORM65VHWVqhCPUL5KQslGtwvWV/EFHp6zvTg6WqvEcypA7yHOueriWAggBdB/MHhIbOKYGD533AOXM4Cv3CAC/pUzjQKE40DjkV5Im4sCDgYfgtIKuK/SRiCTQebfGcRCP33+ibDRVQHCldynIjZIVWe0m9o5Duml22h7mivAZqjmWZXYgoTwgALauVK7yea4nhaiCkhZSTkFtDMdB7HH1qa8BMqE+f4VFUe0182YVU4L2+0FoZaFCKcCxM7DnR7FVrUQXM4ADSIOHbY+tyD+K0/du8pBM+FaVYXMpy15BoilVF7lmc9xtdieNqA+7wr23s7cFpfF+fgoIJvC8AP2vSfegJxbUDZ95dnZe2Mw2ou/MwDCGu/laD7yXu8PSPIQ9t5Ppbt91k67Vxr6+SlcSIPDko9++taiPDP0ANMbn7wf7UOvAAA095EIsJZOtISuajOPM37J7p9u9h/duXxp66MlethEA6PLv21jtsfwW9k2yVsQ769ft/XhFzYxNA/wjHBxnoz9q9a1liTytxHSy9ZmRHtEfhj9V1KUL6rwB3iUplg/yA4yAAAAAElFTkSuQmCC + + diff --git a/design/mixer_icon.xml b/design/mixer_icon.xml new file mode 100644 index 000000000..9a0c14823 --- /dev/null +++ b/design/mixer_icon.xml @@ -0,0 +1,5 @@ + + + iVBORw0KGgoAAAANSUhEUgAAABwAAAAPCAIAAAB4LTj2AAAACXBIWXMAABnWAAAZ1gEY0crtAAAAfElEQVQ4jd2UwQ6AMAhDgez/f7keFkkDY5sc7cnttaMaVQDIKwBh2UOmqlKojYyHBE3ERfx6n1LOL7tsEBt489x0Blw3NzHcx6PC8GPZgAaA6chJR3neAYV8XjYe98+aZt02reqwYbmZU+Zufreq7pvjShR+DW109UV9RQ8/t+PCB2Yq1wAAAABJRU5ErkJggg== + + diff --git a/art/sprites/route_icon_3.xml b/design/route_icon_3.xml similarity index 100% rename from art/sprites/route_icon_3.xml rename to design/route_icon_3.xml From 3ad57ec6e9a7c8339dcefa8e15e8378000dcacf6 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 7 Nov 2019 23:17:16 +0800 Subject: [PATCH 230/469] mixer visual adjustments --- avr/cores/megacommand/MCL/MixerPage.cpp | 76 ++++++++++++++----------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index 8e4359a19..227861f79 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -8,20 +8,20 @@ void MixerPage::set_display_mode(uint8_t param) { display_mode = param; switch (param) { - case MODEL_FLTF: - strcpy(info_line2, "FLTF"); - break; - case MODEL_FLTW: - strcpy(info_line2, "FLTW"); - break; - case MODEL_FLTQ: - strcpy(info_line2, "FLTQ"); - break; - case MODEL_LEVEL: - default: - display_mode = MODEL_LEVEL; - strcpy(info_line2, "VOLUME"); - break; + case MODEL_FLTF: + strcpy(info_line2, "FLTF"); + break; + case MODEL_FLTW: + strcpy(info_line2, "FLTW"); + break; + case MODEL_FLTQ: + strcpy(info_line2, "FLTQ"); + break; + case MODEL_LEVEL: + default: + display_mode = MODEL_LEVEL; + strcpy(info_line2, "VOLUME"); + break; } } @@ -135,8 +135,8 @@ void encoder_lastparam_handle(Encoder *enc) { void MixerPage::adjust_param(Encoder *enc, uint8_t param) { - if(initializing) { - if(param == MODEL_FLTQ) { + if (initializing) { + if (param == MODEL_FLTQ) { initializing = false; } } else { @@ -226,29 +226,37 @@ void MixerPage::display() { uint8_t meter_level; uint8_t fader_x = MCLGUI::seq_x0; for (int i = 0; i < 16; i++) { - oled_display.fillRect(fader_x + 1, FADER_Y, 3, FADER_LEN, WHITE); - - switch(display_mode) { - case MODEL_LEVEL: - fader_level = MD.kit.levels[i]; - break; - default: - fader_level = MD.kit.params[i][display_mode]; - break; + + if (display_mode == MODEL_LEVEL) { + fader_level = MD.kit.levels[i]; + } else { + fader_level = MD.kit.params[i][display_mode]; } - fader_level = (fader_level / 127.0f) * (FADER_LEN - 2); + fader_level = (fader_level / 127.0f) * (FADER_LEN - 3); meter_level = (disp_levels[i] / 127.0f) * (FADER_LEN - 2); - if (note_interface.notes[i] != 1) { - // draw meter only if not pressed - oled_display.fillRect(fader_x + 2, FADER_Y + 1, 1, - FADER_LEN - meter_level - 2, BLACK); - } + if (display_mode == MODEL_LEVEL) { + oled_display.fillRect(fader_x, FADER_Y + FADER_LEN - fader_level - 2, 5, + fader_level, WHITE); - // draw fader knob - oled_display.fillRect(fader_x, FADER_Y + FADER_LEN - fader_level - 2, - MCLGUI::seq_w, 2, WHITE); + if (note_interface.notes[i] != 1) { + // draw meter only if not pressed + oled_display.fillRect(fader_x + 1, + FADER_Y + FADER_LEN - meter_level - 1, 3, + meter_level, BLACK); + } + } else { + oled_display.fillRect(fader_x + 1, FADER_Y, 3, FADER_LEN, WHITE); + if (note_interface.notes[i] != 1) { + // draw meter only if not pressed + oled_display.fillRect(fader_x + 2, FADER_Y + 1, 1, + FADER_LEN - meter_level - 2, BLACK); + } + // draw fader knob + oled_display.fillRect(fader_x, FADER_Y + FADER_LEN - fader_level - 2, + MCLGUI::seq_w, 2, WHITE); + } fader_x += MCLGUI::seq_w + 1; } From 4cb27969cfec1a827969315b2177edb1beb95188 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 8 Nov 2019 21:30:45 +1100 Subject: [PATCH 231/469] Improve LFO page label, and change page ordering --- avr/cores/megacommand/MCL/LFOPage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 75dc39648..fdee0f22d 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -10,8 +10,8 @@ #define INTERPOLATE #define DIV_1_127 .0079 -#define LFO_DESTINATION 0 -#define LFO_SETTINGS 1 +#define LFO_DESTINATION 1 +#define LFO_SETTINGS 0 void LFOPage::setup() { // lfo_track = &mcl_seq.lfo_tracks[0]; @@ -299,9 +299,9 @@ void LFOPage::display() { const char* info2; if (page_mode) { - info1 = "LFO A"; + info1 = "LFO>DST"; } else { - info1 = "LFO B"; + info1 = "LFO>MOD"; } switch (lfo_track->mode) { From 538d0be82bf38d8bd80dd2b4cc83a2ce480094fc Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 8 Nov 2019 22:40:49 +1100 Subject: [PATCH 232/469] Make MidiActivePeering a GUI Task() --- avr/cores/megacommand/MCL/GridPage.cpp | 1 - avr/cores/megacommand/MCL/MCL.cpp | 1 + avr/cores/megacommand/MCL/MidiActivePeering.cpp | 5 ++++- avr/cores/megacommand/MCL/MidiActivePeering.h | 10 ++++++++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 87b26203b..75b36708d 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -37,7 +37,6 @@ void GridPage::cleanup() { } void GridPage::loop() { - midi_active_peering.check(); int8_t diff, new_val; #ifdef OLED_DISPLAY if (show_slot_menu) { diff --git a/avr/cores/megacommand/MCL/MCL.cpp b/avr/cores/megacommand/MCL/MCL.cpp index 79a3ce6e9..e1d55a225 100644 --- a/avr/cores/megacommand/MCL/MCL.cpp +++ b/avr/cores/megacommand/MCL/MCL.cpp @@ -52,6 +52,7 @@ void MCL::setup() { midi_setup.cfg_ports(); GUI.addTask(&grid_task); + GUI.addTask(&midi_active_peering); if (mcl_cfg.display_mirror == 1) { #ifndef DEBUGMODE diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.cpp b/avr/cores/megacommand/MCL/MidiActivePeering.cpp index 6f61514e3..021295993 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.cpp +++ b/avr/cores/megacommand/MCL/MidiActivePeering.cpp @@ -1,5 +1,6 @@ //#include "MidiActivePeering.h" #include "MCL.h" +#include "MidiActivePeering.h" uint8_t MidiActivePeering::get_device(uint8_t port) { if (port == UART1_PORT) { @@ -113,7 +114,7 @@ void MidiActivePeering::a4_setup() { #endif } } -void MidiActivePeering::check() { +void MidiActivePeering::run() { char str[16]; uint8_t uart1_device = MidiUart.device.get_id(); @@ -149,3 +150,5 @@ void MidiActivePeering::check() { } #endif } + +MidiActivePeering midi_active_peering; diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.h b/avr/cores/megacommand/MCL/MidiActivePeering.h index a5424226d..0dafca574 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.h +++ b/avr/cores/megacommand/MCL/MidiActivePeering.h @@ -4,15 +4,21 @@ #define MIDIACTIVEPEERING_H__ #include "MidiID.hh" +#include "Task.hh" #define UART1_PORT 1 #define UART2_PORT 2 -class MidiActivePeering { +class MidiActivePeering : public Task { public: + MidiActivePeering(uint16_t _interval = 0) : Task(_interval) { setup(_interval); } + + virtual void setup(uint16_t _interval = 0) { interval = _interval; } + + virtual void run(); + virtual void destroy() {}; void md_setup(); void a4_setup(); - void check(); uint8_t get_device(uint8_t port); }; From d21d7246d5fd6ca52d1e74e208d44e28e34a96c0 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 8 Nov 2019 22:48:44 +1100 Subject: [PATCH 233/469] Enable high display refresh for new project creation --- avr/cores/megacommand/MCL/Project.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/Project.cpp b/avr/cores/megacommand/MCL/Project.cpp index 27d9062fd..6aa672027 100644 --- a/avr/cores/megacommand/MCL/Project.cpp +++ b/avr/cores/megacommand/MCL/Project.cpp @@ -138,9 +138,9 @@ bool Project::new_project(const char *projectname) { for (int32_t i = 0; i < GRID_LENGTH; i++) { #ifdef OLED_DISPLAY - if (i % 16 == 0) { +// if (i % 16 == 0) { mcl_gui.draw_progress("Initializing project", i, GRID_LENGTH); - } + // } #endif if (i % 2 == 0) { if (ledstatus == 0) { From 6de7037b9ae5edb4839600ef0bdcb974ca4a68b1 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 01:39:10 +0800 Subject: [PATCH 234/469] revert mixer design; SeqPtcPage improvements --- avr/cores/megacommand/MCL/MixerPage.cpp | 121 ++++++++++++++++++----- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 87 +++++++++------- 2 files changed, 148 insertions(+), 60 deletions(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index 227861f79..4a9bc2987 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -1,8 +1,7 @@ #include "MixerPage.h" #include "MCL.h" -#define FADER_Y 3 -#define FADER_LEN 21 +#define FADER_LEN 16 #define FADE_RATE 16 void MixerPage::set_display_mode(uint8_t param) { @@ -25,6 +24,32 @@ void MixerPage::set_display_mode(uint8_t param) { } } +#ifdef OLED_DISPLAY +static void oled_draw_routing() { + for (int i = 0; i < 16; ++i) { + // draw routing + if (note_interface.notes[i] > 0) { + + oled_display.fillRect(0 + i * 8, 2, 6, 6, WHITE); + } + + else if (mcl_cfg.routing[i] == 6) { + + oled_display.fillRect(0 + i * 8, 2, 6, 6, BLACK); + oled_display.drawRect(0 + i * 8, 2, 6, 6, WHITE); + + } + + else { + + oled_display.fillRect(0 + i * 8, 2, 6, 6, BLACK); + oled_display.drawLine(+i * 8, 5, 5 + (i * 8), 5, WHITE); + } + } +} + +#endif + void MixerPage::setup() { encoders[0]->handler = encoder_level_handle; encoders[1]->handler = encoder_filtf_handle; @@ -57,9 +82,17 @@ void MixerPage::init() { #ifdef OLED_DISPLAY oled_display.clearDisplay(); - oled_display.drawBitmap(1, 2, icon_mixer, 28, 15, WHITE); + oled_draw_routing(); set_display_mode(MODEL_LEVEL); initializing = true; + for (uint8_t i = 0; i < 16; i++) { + uint8_t scaled_level = + (uint8_t)(((float)MD.kit.levels[i] / (float)127) * (float)FADER_LEN); + + oled_display.drawRect(0 + i * 8, 12 + (FADER_LEN - scaled_level), 6, + scaled_level + 1, WHITE); + disp_levels[i] = 0; + } #endif } @@ -89,6 +122,8 @@ void MixerPage::draw_levels() {} void encoder_level_handle(Encoder *enc) { + bool redraw_frame = (mixer_page.display_mode != MODEL_LEVEL); + mixer_page.set_display_mode(MODEL_LEVEL); int dir = enc->getValue() - enc->old; @@ -112,6 +147,16 @@ void encoder_level_handle(Encoder *enc) { // if ((MD.kit.levels[i] < 127) && (MD.kit.levels[i] > 0)) { mixer_page.set_level(i, track_newval); } + + if (note_interface.notes[i] == 1 || redraw_frame) { +#ifdef OLED_DISPLAY + uint8_t scaled_level = (MD.kit.levels[i] / 127.0f) * FADER_LEN; + + oled_display.fillRect(0 + i * 8, 12, 6, FADER_LEN, BLACK); + oled_display.drawRect(0 + i * 8, 12 + (FADER_LEN - scaled_level), 6, + scaled_level + 1, WHITE); +#endif + } } enc->cur = 64 + dir; enc->old = 64; @@ -174,6 +219,14 @@ void MixerPage::adjust_param(Encoder *enc, uint8_t param) { MD.setTrackParam(i, param, newval); MD.kit.params[i][param] = newval; CLEAR_LOCK(); + +#ifdef OLED_DISPLAY + uint8_t scaled_level = (newval / 127.0f) * FADER_LEN; + + oled_display.fillRect(0 + i * 8, 12, 6, FADER_LEN, BLACK); + oled_display.fillRect(0 + i * 8, 12 + (FADER_LEN - scaled_level), 6, + scaled_level + 1, WHITE); +#endif } } enc->cur = 64 + dir; @@ -215,16 +268,14 @@ void MixerPage::display() { } } #else + void MixerPage::display() { auto oldfont = oled_display.getFont(); - mcl_gui.draw_panel_labels("MIXER", info_line2); - mcl_gui.clear_rightpane(); - route_page.draw_routes(); uint8_t fader_level; uint8_t meter_level; - uint8_t fader_x = MCLGUI::seq_x0; + uint8_t fader_x = 0; for (int i = 0; i < 16; i++) { if (display_mode == MODEL_LEVEL) { @@ -233,32 +284,32 @@ void MixerPage::display() { fader_level = MD.kit.params[i][display_mode]; } - fader_level = (fader_level / 127.0f) * (FADER_LEN - 3); - meter_level = (disp_levels[i] / 127.0f) * (FADER_LEN - 2); + fader_level = (fader_level / 127.0f) * FADER_LEN + 1; + meter_level = (disp_levels[i] / 127.0f) * FADER_LEN + 1; if (display_mode == MODEL_LEVEL) { - oled_display.fillRect(fader_x, FADER_Y + FADER_LEN - fader_level - 2, 5, - fader_level, WHITE); - if (note_interface.notes[i] != 1) { + if (note_interface.notes[i] == 1) { + oled_display.fillRect(fader_x, 13 + (FADER_LEN - fader_level), 6, + fader_level, WHITE); + } else { + // draw meter only if not pressed - oled_display.fillRect(fader_x + 1, - FADER_Y + FADER_LEN - meter_level - 1, 3, - meter_level, BLACK); + oled_display.fillRect(fader_x + 1, 14 + (FADER_LEN - fader_level), 4, + FADER_LEN - meter_level, BLACK); + oled_display.fillRect(fader_x + 1, 13 + (FADER_LEN - meter_level), 4, + meter_level, WHITE); } } else { - oled_display.fillRect(fader_x + 1, FADER_Y, 3, FADER_LEN, WHITE); - if (note_interface.notes[i] != 1) { - // draw meter only if not pressed - oled_display.fillRect(fader_x + 2, FADER_Y + 1, 1, - FADER_LEN - meter_level - 2, BLACK); - } - // draw fader knob - oled_display.fillRect(fader_x, FADER_Y + FADER_LEN - fader_level - 2, - MCLGUI::seq_w, 2, WHITE); + meter_level = min(fader_level, meter_level); + oled_display.fillRect(0 + i * 8, 12, 6, FADER_LEN, BLACK); + oled_display.fillRect(fader_x, 13 + (FADER_LEN - fader_level), 6, + fader_level, WHITE); + oled_display.fillRect(fader_x + 1, 14 + (FADER_LEN - meter_level), 4, + meter_level - 1, BLACK); } - fader_x += MCLGUI::seq_w + 1; + fader_x += 8; } uint8_t dec = MidiClock.get_tempo() / FADE_RATE; @@ -286,14 +337,29 @@ bool MixerPage::handleEvent(gui_event_t *event) { if (track > 16) { return false; } - if (event->mask == EVENT_BUTTON_PRESSED) { +#ifdef OLED_DISPLAY + + if (note_interface.notes[track] > 0) { + + oled_display.fillRect(0 + track * 8, 2, 6, 6, WHITE); + } + +#endif + return true; } if (event->mask == EVENT_BUTTON_RELEASED) { #ifndef OLED_DISPLAY note_interface.draw_notes(0); +#else + uint8_t i = track; + uint8_t scaled_level = (MD.kit.levels[i] / 127.0f) * FADER_LEN; + oled_display.fillRect(0 + i * 8, 12, 6, FADER_LEN, BLACK); + oled_display.drawRect(0 + i * 8, 12 + (FADER_LEN - scaled_level), 6, + scaled_level + 1, WHITE); + #endif if (note_interface.notes_all_off_md()) { @@ -304,6 +370,9 @@ bool MixerPage::handleEvent(gui_event_t *event) { route_page.toggle_routes_batch(true); } note_interface.init_notes(); +#ifdef OLED_DISPLAY + oled_draw_routing(); +#endif } return true; } diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 30e07c66d..905cb7e1c 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -4,6 +4,47 @@ #define MIDI_LOCAL_MODE 0 #define NUM_KEYS 32 +scale_t *scales[16]{ + &chromaticScale, &ionianScale, + //&dorianScale, + &phrygianScale, + //&lydianScale, + //&mixolydianScale, + //&aeolianScale, + //&locrianScale, + &harmonicMinorScale, &melodicMinorScale, + //&lydianDominantScale, + //&wholeToneScale, + //&wholeHalfStepScale, + //&halfWholeStepScale, + &majorPentatonicScale, &minorPentatonicScale, &suspendedPentatonicScale, + &inSenScale, &bluesScale, + //&majorBebopScale, + //&dominantBebopScale, + //&minorBebopScale, + &majorArp, &minorArp, &majorMaj7Arp, &majorMin7Arp, &minorMin7Arp, + //&minorMaj7Arp, + &majorMaj7Arp9, + //&majorMaj7ArpMin9, + //&majorMin7Arp9, + //&majorMin7ArpMin9, + //&minorMin7Arp9, + //&minorMin7ArpMin9, + //&minorMaj7Arp9, + //&minorMaj7ArpMin9 +}; + +typedef char scale_name_t[4]; + +const scale_name_t scale_names[] PROGMEM = { + "---", "ION", + "PHR", + "mHA", "mME", + "MPE", "mPE", "sPE", + "ISS", "BLU", + "MAJ", "MIN", "MM7", "Mm7", "mm7", "M79", +}; + void SeqPtcPage::setup() { SeqPage::setup(); init_poly(); @@ -137,6 +178,11 @@ void SeqPtcPage::loop() { mcl_seq.ext_tracks[last_ext_track].buffer_notesoff(); } #endif + + if (encoders[0]->hasChanged() || encoders[1]->hasChanged() || encoders[2]->hasChanged() || encoders[3]->hasChanged()) { + queue_redraw(); + } + if (last_midi_state != MidiClock.state) { last_midi_state = MidiClock.state; redisplay = true; @@ -250,7 +296,8 @@ void SeqPtcPage::display() { // draw LEN if (midi_device == DEVICE_MD) { - draw_knob(2, encoders[2], "LEN"); + itoa(encoders[2]->getValue(), buf1, 10); + draw_knob(2, "LEN", buf1); } #ifdef EXT_TRACKS else { @@ -262,7 +309,7 @@ void SeqPtcPage::display() { #endif // draw SCALE - itoa(encoders[3]->getValue(), buf1, 10); + m_strncpy_p(buf1, scale_names[encoders[3]->getValue()], 4); draw_knob(3, "SCA", buf1); // draw TI keyboard @@ -410,7 +457,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { } // TI events if (EVENT_RELEASED(event, Buttons.BUTTON1)) { - redisplay = true; + seq_ptc_page.queue_redraw(); recording = !recording; return true; } @@ -448,7 +495,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { } } #endif - redisplay = true; + seq_ptc_page.queue_redraw(); return true; } @@ -472,11 +519,12 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { mcl_seq.ext_tracks[last_ext_track].clear_track(); } #endif + seq_ptc_page.queue_redraw(); return true; } if (SeqPage::handleEvent(event)) { - redisplay = true; + seq_ptc_page.queue_redraw(); return true; } @@ -647,32 +695,3 @@ void SeqPtcMidiEvents::remove_callbacks() { state = false; } -scale_t *scales[16]{ - &chromaticScale, &ionianScale, - //&dorianScale, - &phrygianScale, - //&lydianScale, - //&mixolydianScale, - //&aeolianScale, - //&locrianScale, - &harmonicMinorScale, &melodicMinorScale, - //&lydianDominantScale, - //&wholeToneScale, - //&wholeHalfStepScale, - //&halfWholeStepScale, - &majorPentatonicScale, &minorPentatonicScale, &suspendedPentatonicScale, - &inSenScale, &bluesScale, - //&majorBebopScale, - //&dominantBebopScale, - //&minorBebopScale, - &majorArp, &minorArp, &majorMaj7Arp, &majorMin7Arp, &minorMin7Arp, - //&minorMaj7Arp, - &majorMaj7Arp9, - //&majorMaj7ArpMin9, - //&majorMin7Arp9, - //&majorMin7ArpMin9, - //&minorMin7Arp9, - //&minorMin7ArpMin9, - //&minorMaj7Arp9, - //&minorMaj7ArpMin9 -}; From d88d6b090186f8ecefe9f3235425d90893c13da7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 10:19:01 +1100 Subject: [PATCH 235/469] improve progress bar gfx by using circular shifting --- avr/cores/megacommand/MCL/MCLGUI.cpp | 121 +++++++++++++++------------ 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 666540d80..a0860406b 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -73,7 +73,7 @@ void MCLGUI::draw_knob(uint8_t i, const char *title, const char *text) { draw_text_encoder(x, knob_y0, title, text); } -void MCLGUI::draw_knob(uint8_t i, Encoder* enc, const char* title) { +void MCLGUI::draw_knob(uint8_t i, Encoder *enc, const char *title) { uint8_t x = knob_x0 + i * knob_w; draw_light_encoder(x + 6, 6, enc, title); } @@ -104,8 +104,8 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { oled_display.setTextColor(BLACK); auto len = strlen(title_buf) * 4; - oled_display.setCursor(s_title_x + (s_title_w - len) / 2 , s_menu_y + 3); - //oled_display.setCursor(s_title_x + 2, s_menu_y + 3); + oled_display.setCursor(s_title_x + (s_title_w - len) / 2, s_menu_y + 3); + // oled_display.setCursor(s_title_x + 2, s_menu_y + 3); oled_display.println(title_buf); oled_display.setTextColor(WHITE); if (!deferred_display) { @@ -125,7 +125,7 @@ static constexpr uint8_t s_progress_y = 16; static constexpr uint8_t s_progress_w = 64; static constexpr uint8_t s_progress_h = 5; -static uint8_t s_progress_cookie = 0; +static uint8_t s_progress_cookie = 0b00110011; void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, bool deferred_display) { @@ -139,14 +139,29 @@ void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, // draw the progress oled_display.fillRect(s_progress_x + 1, s_progress_y + 1, progx - s_progress_x + 1, s_progress_h - 2, WHITE); - s_progress_cookie = (s_progress_cookie + 1) % 3; - // draw the '///////' pattern, note the cookie - for (uint8_t i = s_progress_cookie + s_progress_x + 1; i < progx; i += 3) { - oled_display.drawLine(i, s_progress_y + s_progress_h - 2, i + 2, - s_progress_y + 1, BLACK); + uint8_t shift = 1; + + // draw the '///////' pattern, using circular shifting + uint8_t x = 0; + + uint8_t bitmask = s_progress_cookie; + uint8_t temp_bitmask = s_progress_cookie; + + for (uint8_t i = s_progress_x + 1; i <= progx; i += 1) { + + for (uint8_t n = 0; n < s_progress_h - 2; n++) { + uint8_t a = s_progress_h - 2 - n; + + if (IS_BIT_SET(temp_bitmask, a)) { + oled_display.drawPixel(i, s_progress_y + 1 + n, BLACK); + } + } + temp_bitmask = (temp_bitmask >> shift) | (temp_bitmask << (8 - shift)); } + s_progress_cookie = (bitmask >> shift) | (bitmask << (8 - shift)); + oled_display.drawRect(s_progress_x, s_progress_y, s_progress_w, s_progress_h, WHITE); if (!deferred_display) { @@ -435,14 +450,19 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(x + 1, y + 1, seq_w - 2, trig_h - 2, WHITE); + oled_display.fillRect(x, y, seq_w, trig_h, WHITE); } else if (!in_range) { // don't draw } else { if (IS_BIT_SET64(pattern_mask, i + offset) && ((i + offset != step_count) || (MidiClock.state != 2))) { /*If the bit is set, there is a trigger at this position. */ - oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + oled_display.drawRect(x, y, seq_w, trig_h, WHITE); + oled_display.drawPixel(x + 1, y + 1, WHITE); + oled_display.drawPixel(x + 3, y + 1, WHITE); + oled_display.drawPixel(x + 1, y + 3, WHITE); + oled_display.drawPixel(x + 3, y + 3, WHITE); + oled_display.drawPixel(x + 2, y + 2, WHITE); } else { oled_display.drawRect(x, y, seq_w, trig_h, WHITE); } @@ -598,55 +618,50 @@ void MCLGUI::draw_panel_number(uint8_t number) { // ================ SPRITES ================ -const unsigned char encoder_small_0 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x4e, 0x40, - 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_0[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0x80, 0x20, 0x4e, 0x40, 0x4e, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder1', 11x11px -const unsigned char encoder_small_1 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x5c, 0x40, - 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_1[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0x80, 0x20, 0x5c, 0x40, 0x4c, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder2', 11x11px -const unsigned char encoder_small_2 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0x90, 0x20, 0x58, 0x40, - 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_2[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0x90, 0x20, 0x58, 0x40, 0x48, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder3', 11x11px -const unsigned char encoder_small_3 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_3[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0x80, + 0x20, 0xb0, 0x20, 0x58, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder4', 11x11px -const unsigned char encoder_small_4 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x58, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_4[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, + 0x20, 0xb0, 0x20, 0x58, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder5', 11x11px -const unsigned char encoder_small_5 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x50, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_5[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x20, 0xb0, + 0x20, 0xb0, 0x20, 0x50, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'encoder6', 11x11px -const unsigned char encoder_small_6 [] PROGMEM = { - 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, - 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00 -}; +const unsigned char encoder_small_6[] PROGMEM = { + 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, + 0x20, 0xb0, 0x20, 0x40, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'gateboxlarge', 24x25px -const unsigned char icon_gatebox [] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x80, 0x00, 0x7e, 0xe0, 0x01, 0xfe, 0xe0, 0x07, - 0xff, 0x90, 0x03, 0xfe, 0x70, 0x0c, 0xf9, 0xf0, 0x0f, 0x26, 0xf0, 0x0f, 0xdc, 0xf0, 0xef, 0xd3, - 0x37, 0x0f, 0xdf, 0xc0, 0x0f, 0x9f, 0xf0, 0x0e, 0x5f, 0xe0, 0x09, 0xdf, 0x80, 0x07, 0xde, 0x00, - 0x01, 0xd8, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xef, 0x48, 0x21, 0x29, 0x48, 0x2d, - 0xe9, 0x30, 0x25, 0x29, 0x48, 0x3d, 0xef, 0x48, 0x00, 0x00, 0x00 -}; +const unsigned char icon_gatebox[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x80, 0x00, 0x7e, + 0xe0, 0x01, 0xfe, 0xe0, 0x07, 0xff, 0x90, 0x03, 0xfe, 0x70, 0x0c, + 0xf9, 0xf0, 0x0f, 0x26, 0xf0, 0x0f, 0xdc, 0xf0, 0xef, 0xd3, 0x37, + 0x0f, 0xdf, 0xc0, 0x0f, 0x9f, 0xf0, 0x0e, 0x5f, 0xe0, 0x09, 0xdf, + 0x80, 0x07, 0xde, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x3d, 0xef, 0x48, 0x21, 0x29, 0x48, 0x2d, 0xe9, 0x30, + 0x25, 0x29, 0x48, 0x3d, 0xef, 0x48, 0x00, 0x00, 0x00}; // 'rythmecho', 24x25px -const unsigned char icon_rhytmecho [] PROGMEM = { - 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x3f, 0xff, 0xfe, 0x3f, 0xff, 0xfc, 0x00, 0x01, 0xfc, 0x07, - 0xe1, 0xf8, 0x07, 0xe3, 0xf8, 0x0f, 0xc7, 0xf0, 0x0f, 0xff, 0xf0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, - 0x00, 0x3f, 0x7f, 0x80, 0x3f, 0x1f, 0xc0, 0x7e, 0x0f, 0xe0, 0x7e, 0x07, 0xf0, 0x00, 0x00, 0x00, - 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x63, 0x1b, 0x6c, 0x7b, 0x1f, 0x6c, 0x7b, 0x1f, 0x6c, 0x63, - 0x1b, 0x6c, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x00, 0x00, 0x00 -}; +const unsigned char icon_rhytmecho[] PROGMEM = { + 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x3f, 0xff, 0xfe, 0x3f, 0xff, + 0xfc, 0x00, 0x01, 0xfc, 0x07, 0xe1, 0xf8, 0x07, 0xe3, 0xf8, 0x0f, + 0xc7, 0xf0, 0x0f, 0xff, 0xf0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0x00, + 0x3f, 0x7f, 0x80, 0x3f, 0x1f, 0xc0, 0x7e, 0x0f, 0xe0, 0x7e, 0x07, + 0xf0, 0x00, 0x00, 0x00, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x63, + 0x1b, 0x6c, 0x7b, 0x1f, 0x6c, 0x7b, 0x1f, 0x6c, 0x63, 0x1b, 0x6c, + 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x00, 0x00, 0x00}; From 19e47bcd618782cdc97b2a310443a1a78a02a504 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 11:29:33 +1100 Subject: [PATCH 236/469] Revert changes to draw_trig introduced in previous commit --- avr/cores/megacommand/MCL/MCLGUI.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index a0860406b..745b23a07 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -450,20 +450,24 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + oled_display.fillRect(x + 1, y + 1, seq_w - 2, trig_h - 2, WHITE); + //oled_display.fillRect(x, y, seq_w, trig_h, WHITE); } else if (!in_range) { // don't draw } else { if (IS_BIT_SET64(pattern_mask, i + offset) && ((i + offset != step_count) || (MidiClock.state != 2))) { /*If the bit is set, there is a trigger at this position. */ + oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + /* oled_display.drawRect(x, y, seq_w, trig_h, WHITE); oled_display.drawPixel(x + 1, y + 1, WHITE); oled_display.drawPixel(x + 3, y + 1, WHITE); oled_display.drawPixel(x + 1, y + 3, WHITE); oled_display.drawPixel(x + 3, y + 3, WHITE); oled_display.drawPixel(x + 2, y + 2, WHITE); - } else { + */ + } else { oled_display.drawRect(x, y, seq_w, trig_h, WHITE); } } From b3389260f8d4701e22455725ae6f737e4996498b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 12:06:09 +1100 Subject: [PATCH 237/469] fix display color on md_rev.png --- art/sprites/trimetric/md_rev.png | Bin 527 -> 522 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/art/sprites/trimetric/md_rev.png b/art/sprites/trimetric/md_rev.png index 6782777c90c673cf845ddd2f00418a92740eba72..0c3ec8a697a90f080e1276600d6d21b4aa7282f8 100644 GIT binary patch delta 483 zcmV<90UZ911d0TZF@MBKL_t(Y4Yihmj)O1^LjmpoKN*hK-9sjWP8+P!md0@$Lpy2X zFbvE3`N-GC5aewv?zTPbaXv)SqOb%M| z?2#)D2R;j|8D!s(&X)p2MbGGRspl~HV&+{Vk~spGnE6pm6Inlc0J-S0e>oZoY2ZjC zhp?}r*6q!QNN)D-j7Wk&v!=jPECT#)SZ1+Yeuzxrj2?`P2!%4H|M3>yEV_kg9*M@~(g7*Rehg7mnM0s-T0cVMuH!yH> zpQ2f~{EKi}tsrg{k?RSPf8MmW>oJ4c5#M$Rc1K{RHA0TVl?|ye#0Uhc< z1Ag>QCtMc6}DD$2Y}ln_DS3sq0xct zQ`kXy3FQ#4t`{Q+V#c?D0WqVdTH&Uz?N^g}`6e80o%^FXu{+}aA517ZETj88zG%6$ Z{R5*+f)f6!HJShb002ovPDHLkV1is!=vx2) delta 488 zcmVypKN-dgd+2Z>RR^_H9G}mZgJjai zVHlS6_mvpOaafk6{aE*6>NWq{bJKaAcM}-u#aE168R(gL)X3E_XuzX7`!bIh=^0>i zj|u~FtC?Fyu0-<-)~tV7XaxT$8&-6yOL9F-?h+dp-Kh_yW{~I?S?uIT{*i zKoJKwneAB(C@RNUdEn}ZSs#oDG60dE7n3*AU=^vg^JGKqGcFu%%*xrW2_)9$M)<>A zlarrya##~RE`NQE9li(+f;X3-5ZN`HG;2*ZX+&=e5b7D{A<9{%r=w_K+ e=kdkJt>rJ};*k$W2`CK!0000 Date: Sat, 9 Nov 2019 13:38:13 +1100 Subject: [PATCH 238/469] Improve Active Peering GFX, show progress bar --- avr/cores/megacommand/MCL/MCLGUI.cpp | 71 ++++++---- avr/cores/megacommand/MCL/MCLGUI.h | 123 ++++++++++++------ .../megacommand/MCL/MidiActivePeering.cpp | 52 ++++++-- avr/cores/megacommand/MCL/MidiActivePeering.h | 3 + 4 files changed, 171 insertions(+), 78 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 745b23a07..9f6db612f 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -94,7 +94,7 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { // draw menu body oled_display.fillRect(s_menu_x - 1, s_menu_y - 2, s_menu_w + 2, h + 2, BLACK); oled_display.drawRect(s_menu_x, s_menu_y, s_menu_w, h, WHITE); - oled_display.fillRect(s_menu_x + 1, s_menu_y - 1, s_menu_w - 2, 6, WHITE); + oled_display.fillRect(s_menu_x + 1, s_menu_y - 1, s_menu_w - 2, 5, WHITE); // draw the title '____/**********\____' part oled_display.drawRect(s_title_x, s_menu_y - 4, s_title_w, 4, BLACK); @@ -120,25 +120,24 @@ void MCLGUI::clear_popup(uint8_t h) { oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, h - 5, BLACK); } -static constexpr uint8_t s_progress_x = 31; -static constexpr uint8_t s_progress_y = 16; -static constexpr uint8_t s_progress_w = 64; -static constexpr uint8_t s_progress_h = 5; - -static uint8_t s_progress_cookie = 0b00110011; - void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, - bool deferred_display) { + bool deferred_display, uint8_t x_pos, + uint8_t y_pos) { + draw_popup(msg, true); + draw_progress_bar(cur, _max, deferred_display, x_pos, y_pos); +} +void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, + uint8_t x_pos, uint8_t y_pos) { - oled_display.fillRect(s_progress_x + 1, s_progress_y + 1, s_progress_w - 2, + oled_display.fillRect(x_pos + 1, y_pos + 1, s_progress_w - 2, s_progress_h - 2, BLACK); float prog = (float)cur / (float)_max; - auto progx = (uint8_t)(s_progress_x + 1 + prog * (s_progress_w - 2)); + auto progx = (uint8_t)(x_pos + 1 + prog * (s_progress_w - 2)); // draw the progress - oled_display.fillRect(s_progress_x + 1, s_progress_y + 1, - progx - s_progress_x + 1, s_progress_h - 2, WHITE); + oled_display.fillRect(x_pos + 1, y_pos + 1, progx - x_pos + 1, + s_progress_h - 2, WHITE); uint8_t shift = 1; @@ -148,22 +147,22 @@ void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, uint8_t bitmask = s_progress_cookie; uint8_t temp_bitmask = s_progress_cookie; - for (uint8_t i = s_progress_x + 1; i <= progx; i += 1) { + for (uint8_t i = x_pos + 1; i <= progx; i += 1) { for (uint8_t n = 0; n < s_progress_h - 2; n++) { - uint8_t a = s_progress_h - 2 - n; + uint8_t a = s_progress_h - 2 - n; - if (IS_BIT_SET(temp_bitmask, a)) { - oled_display.drawPixel(i, s_progress_y + 1 + n, BLACK); + if (IS_BIT_SET(temp_bitmask, a)) { + oled_display.drawPixel(i, y_pos + 1 + n, BLACK); } } - temp_bitmask = (temp_bitmask >> shift) | (temp_bitmask << (8 - shift)); + temp_bitmask = (temp_bitmask >> shift) | (temp_bitmask << (8 - shift)); } s_progress_cookie = (bitmask >> shift) | (bitmask << (8 - shift)); - oled_display.drawRect(s_progress_x, s_progress_y, s_progress_w, s_progress_h, - WHITE); + oled_display.drawRect(x_pos, y_pos, s_progress_w, s_progress_h, WHITE); + if (!deferred_display) { oled_display.display(); } @@ -451,7 +450,7 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, if (note_interface.notes[i] == 1) { // TI feedback oled_display.fillRect(x + 1, y + 1, seq_w - 2, trig_h - 2, WHITE); - //oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + // oled_display.fillRect(x, y, seq_w, trig_h, WHITE); } else if (!in_range) { // don't draw } else { @@ -467,7 +466,7 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, oled_display.drawPixel(x + 3, y + 3, WHITE); oled_display.drawPixel(x + 2, y + 2, WHITE); */ - } else { + } else { oled_display.drawRect(x, y, seq_w, trig_h, WHITE); } } @@ -669,3 +668,31 @@ const unsigned char icon_rhytmecho[] PROGMEM = { 0xf0, 0x00, 0x00, 0x00, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x63, 0x1b, 0x6c, 0x7b, 0x1f, 0x6c, 0x7b, 0x1f, 0x6c, 0x63, 0x1b, 0x6c, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x00, 0x00, 0x00}; + +// 'md_rev', 34x24px +const unsigned char icon_md[] PROGMEM = { + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x00, 0x00, 0x00, + 0x06, 0xb0, 0x00, 0x00, 0x00, 0x1a, 0xe8, 0x00, 0x00, 0x00, 0x6b, 0xac, + 0x00, 0x00, 0x01, 0xee, 0xbe, 0x00, 0x00, 0x07, 0x3a, 0xe7, 0x00, 0x00, + 0x1c, 0x1b, 0x9c, 0x80, 0x00, 0x70, 0x0e, 0x70, 0x80, 0x01, 0xc0, 0x1f, + 0xc3, 0x40, 0x07, 0x80, 0x6f, 0x0c, 0xc0, 0x1c, 0xc1, 0x9c, 0x33, 0xc0, + 0x6b, 0x66, 0x70, 0xcf, 0x00, 0x6c, 0xfd, 0xc3, 0x3c, 0x00, 0xbf, 0xff, + 0x0c, 0xf0, 0x00, 0xd3, 0xcc, 0x33, 0xc0, 0x00, 0xef, 0x30, 0xcf, 0x00, + 0x00, 0x74, 0xc3, 0x3c, 0x00, 0x00, 0x3b, 0x0c, 0xf0, 0x00, 0x00, 0x1d, + 0x33, 0xc0, 0x00, 0x00, 0x0e, 0xcf, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, + 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00}; + +// 'analog4_rev', 34x24px +const unsigned char icon_a4[] PROGMEM = { + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, + 0x06, 0xb0, 0x00, 0x00, 0x00, 0x1a, 0xd8, 0x00, 0x00, 0x00, 0x6b, 0xac, + 0x00, 0x00, 0x01, 0xae, 0xb6, 0x00, 0x00, 0x06, 0xba, 0xff, 0x00, 0x00, + 0x1e, 0xeb, 0x8c, 0x80, 0x00, 0x73, 0xae, 0x30, 0x80, 0x01, 0xc1, 0xb8, + 0xc3, 0x40, 0x07, 0x03, 0xe3, 0x0c, 0xc0, 0x1a, 0x0d, 0xec, 0x33, 0xc0, + 0x6b, 0x37, 0xf0, 0xcf, 0x00, 0x6f, 0xdc, 0xc3, 0x3c, 0x00, 0xbc, 0xf3, + 0x0c, 0xf0, 0x00, 0xd3, 0xcc, 0x33, 0xc0, 0x00, 0xef, 0xf0, 0xcf, 0x00, + 0x00, 0x76, 0xc3, 0x3c, 0x00, 0x00, 0x3b, 0x0c, 0xf0, 0x00, 0x00, 0x1d, + 0x33, 0xc0, 0x00, 0x00, 0x0e, 0xcf, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, + 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00}; + +MCLGUI mcl_gui; diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 5ea03f877..aabedbe10 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -3,28 +3,41 @@ #ifndef MCLGUI_H__ #define MCLGUI_H__ -#include "TextInputPage.h" #include "QuestionDialogPage.h" +#include "TextInputPage.h" #define SHOW_VALUE_TIMEOUT 2000 class MCLGUI { public: + uint8_t s_progress_cookie = 0b00110011; // fills dst buffer with input text. ensures that: // 1. dst is null-terminated - // 2. dst has no trailing spaces + // 2. dst has no trailing spaces bool wait_for_input(char *dst, const char *title, uint8_t len); void draw_vertical_dashline(uint8_t x, uint8_t from = 1, uint8_t to = 32); void draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to); void draw_horizontal_arrow(uint8_t x, uint8_t y, uint8_t w); - bool wait_for_confirm(const char *title, const char* text); - void draw_infobox(const char* line1, const char* line2, const int line2_offset = 0); + bool wait_for_confirm(const char *title, const char *text); + void draw_infobox(const char *line1, const char *line2, + const int line2_offset = 0); void draw_vertical_separator(uint8_t x); - void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current); + void draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, + uint8_t n_current); /// Clear the content area of a popup void clear_popup(uint8_t h = 0); - void draw_popup(const char* title, bool deferred_display = false, uint8_t h = 0); - void draw_progress(const char* msg, uint8_t cur, uint8_t _max, bool deferred_display = false); + void draw_popup(const char *title, bool deferred_display = false, + uint8_t h = 0); + + void draw_progress_bar(uint8_t cur, uint8_t _max, + bool deferred_display = true, + uint8_t x_pos = s_progress_x, + uint8_t y_pos = s_progress_y); + + void draw_progress(const char *msg, uint8_t cur, uint8_t _max, + bool deferred_display = false, + uint8_t x_pos = s_progress_x, + uint8_t y_pos = s_progress_y); void clear_leftpane(); void clear_rightpane(); @@ -34,24 +47,34 @@ class MCLGUI { bool show_encoder_value(Encoder *encoder); - void draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char* value); - void draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); - void draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); - void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, const char *name); - void draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value); - void draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask); - void draw_trigs(uint8_t x, uint8_t y, uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length); - void draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, uint8_t ext_trackid, bool show_current_step); - void draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step); - - void draw_panel_toggle(const char* s1, const char* s2, bool s1_active); - void draw_panel_labels(const char* info1, const char* info2); + void draw_text_encoder(uint8_t x, uint8_t y, const char *name, + const char *value); + void draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, + const char *name); + void draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, + bool show_value); + void draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, + const char *name); + void draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, + bool show_value); + void draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, + uint8_t note_height, uint8_t num_of_notes, + uint64_t note_mask); + void draw_trigs(uint8_t x, uint8_t y, uint8_t offset, uint64_t pattern_mask, + uint8_t step_count, uint8_t length); + void draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, uint8_t ext_trackid, + bool show_current_step); + void draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, + uint8_t step_count, uint8_t length, bool show_current_step); + + void draw_panel_toggle(const char *s1, const char *s2, bool s1_active); + void draw_panel_labels(const char *info1, const char *info2); void draw_panel_status(bool recording, bool playing); void draw_panel_number(uint8_t number); void draw_knob_frame(); - void draw_knob(uint8_t i, const char* title, const char* text); - void draw_knob(uint8_t i, Encoder* enc, const char* name); + void draw_knob(uint8_t i, const char *title, const char *text); + void draw_knob(uint8_t i, Encoder *enc, const char *name); static constexpr uint8_t seq_w = 5; static constexpr uint8_t led_h = 3; @@ -104,58 +127,74 @@ class MCLGUI { static constexpr uint8_t knob_xend = 127; static constexpr uint8_t knob_y0 = 1; static constexpr uint8_t knob_y = 20; + + static constexpr uint8_t s_progress_x = 31; + static constexpr uint8_t s_progress_y = 16; + static constexpr uint8_t s_progress_w = 64; + static constexpr uint8_t s_progress_h = 5; }; extern MCLGUI mcl_gui; /* // 'encoder_medium_1', 15x15px const unsigned char encoder_medium_0 [] PROGMEM = { - 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, - 0x80, 0x02, 0x80, 0x02, 0x40, 0x04, 0x43, 0x84, 0x23, 0x88, 0x18, 0x30, 0x07, 0xc0 + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, +0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x40, 0x04, 0x43, 0x84, 0x23, +0x88, 0x18, 0x30, 0x07, 0xc0 }; // 'encoder_medium_2', 15x15px const unsigned char encoder_medium_1 [] PROGMEM = { - 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, - 0x80, 0x02, 0x80, 0x02, 0x40, 0x04, 0x47, 0x04, 0x23, 0x08, 0x18, 0x30, 0x07, 0xc0 + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, +0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x40, 0x04, 0x47, 0x04, 0x23, +0x08, 0x18, 0x30, 0x07, 0xc0 }; // 'encoder_medium_3', 15x15px const unsigned char encoder_medium_2 [] PROGMEM = { - 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, - 0x80, 0x02, 0x80, 0x02, 0x48, 0x04, 0x46, 0x04, 0x26, 0x08, 0x18, 0x30, 0x07, 0xc0 + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, +0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x48, 0x04, 0x46, 0x04, 0x26, +0x08, 0x18, 0x30, 0x07, 0xc0 }; // 'encoder_medium_4', 15x15px const unsigned char encoder_medium_3 [] PROGMEM = { - 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, - 0x80, 0x02, 0x90, 0x02, 0x58, 0x04, 0x4c, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0 + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, +0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x90, 0x02, 0x58, 0x04, 0x4c, 0x04, 0x20, +0x08, 0x18, 0x30, 0x07, 0xc0 }; // 'encoder_medium_5', 15x15px const unsigned char encoder_medium_4 [] PROGMEM = { - 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, - 0xb0, 0x02, 0xb0, 0x02, 0x50, 0x04, 0x40, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0 + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, +0x80, 0x02, 0x80, 0x02, 0xb0, 0x02, 0xb0, 0x02, 0x50, 0x04, 0x40, 0x04, 0x20, +0x08, 0x18, 0x30, 0x07, 0xc0 }; // 'encoder_medium_6', 15x15px const unsigned char encoder_medium_5 [] PROGMEM = { - 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, 0x80, 0x02, 0xa0, 0x02, - 0xb0, 0x02, 0xb0, 0x02, 0x40, 0x04, 0x40, 0x04, 0x20, 0x08, 0x18, 0x30, 0x07, 0xc0 + 0x07, 0xc0, 0x18, 0x30, 0x20, 0x08, 0x40, 0x04, 0x40, 0x04, 0x80, 0x02, +0x80, 0x02, 0xa0, 0x02, 0xb0, 0x02, 0xb0, 0x02, 0x40, 0x04, 0x40, 0x04, 0x20, +0x08, 0x18, 0x30, 0x07, 0xc0 }; */ // 'encoder0', 11x11px -extern const unsigned char encoder_small_0 []; +extern const unsigned char encoder_small_0[]; // 'encoder1', 11x11px -extern const unsigned char encoder_small_1 []; +extern const unsigned char encoder_small_1[]; // 'encoder2', 11x11px -extern const unsigned char encoder_small_2 []; +extern const unsigned char encoder_small_2[]; // 'encoder3', 11x11px -extern const unsigned char encoder_small_3 []; +extern const unsigned char encoder_small_3[]; // 'encoder4', 11x11px -extern const unsigned char encoder_small_4 []; +extern const unsigned char encoder_small_4[]; // 'encoder5', 11x11px -extern const unsigned char encoder_small_5 []; +extern const unsigned char encoder_small_5[]; // 'encoder6', 11x11px -extern const unsigned char encoder_small_6 []; +extern const unsigned char encoder_small_6[]; // 'gatebox', 24x25px -extern const unsigned char icon_gatebox []; +extern const unsigned char icon_gatebox[]; // 'rythmecho', 24x25px -extern const unsigned char icon_rhytmecho []; +extern const unsigned char icon_rhytmecho[]; +// 'md_rev', 34x24px +extern const unsigned char icon_md[]; +// 'a4_rev', 34x24px +extern const unsigned char icon_a4[]; + #endif /* MCLGUI_H__ */ diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.cpp b/avr/cores/megacommand/MCL/MidiActivePeering.cpp index 021295993..a3392a27d 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.cpp +++ b/avr/cores/megacommand/MCL/MidiActivePeering.cpp @@ -16,15 +16,32 @@ uint8_t MidiActivePeering::get_device(uint8_t port) { return 255; } +void MidiActivePeering::prepare_display() { + oled_display.clearDisplay(); + oled_display.setFont(); + oled_display.setCursor(60, 10); + oled_display.println("Peering..."); + +} + +void MidiActivePeering::delay_progress(uint16_t clock_) { + uint16_t myclock = slowclock; + while (clock_diff(myclock, slowclock) < clock_) { + mcl_gui.draw_progress_bar(60, 60, false, 60, 25); + } +} + void MidiActivePeering::md_setup() { DEBUG_PRINT_FN(); MidiIDSysexListener.setup(&Midi); MidiUart.set_speed((uint32_t)31250, 1); -#ifdef OLED_DISPLAY - oled_display.clearDisplay(); - oled_display.setFont(); -#endif +#ifdef OLED_DISPLAY + auto oldfont = oled_display.getFont(); + prepare_display(); + oled_display.drawBitmap(14, 8, icon_md, 34, 42, WHITE); + oled_display.display(); +#else GUI.clearLines(); GUI.setLine(GUI.LINE1); GUI.put_string_at_fill(0, "Peering..."); @@ -32,14 +49,15 @@ void MidiActivePeering::md_setup() { LCD.puts(GUI.lines[0].data); LCD.goLine(1); LCD.puts(GUI.lines[1].data); -#ifdef OLED_DISPLAY - oled_display.display(); #endif // Hack to prevent unnecessary delay on MC boot MD.connected = false; + uint16_t myclock = slowclock; + if ((slowclock > 3000) || (MidiClock.div16th_counter > 4)) { - delay(4600); + delay_progress(4600); } + for (uint8_t x = 0; x < 3 && MD.connected == false; x++) { if (MidiUart.device.getBlockingId(DEVICE_MD, UART1_PORT, CALLBACK_TIMEOUT)) { @@ -47,7 +65,9 @@ void MidiActivePeering::md_setup() { turbo_light.set_speed(turbo_light.lookup_speed(mcl_cfg.uart1_turbo), 1); // wait 300 ms, shoul be enought time to allow midiclock tempo to be // calculated before proceeding. - delay(400); + myclock = slowclock; + + delay_progress(400); md_exploit.rec_global = 1; @@ -57,6 +77,7 @@ void MidiActivePeering::md_setup() { MD.getCurrentTrack(CALLBACK_TIMEOUT); for (uint8_t x = 0; x < 2; x++) { for (uint8_t y = 0; y < 16; y++) { + mcl_gui.draw_progress_bar(60, 60, false, 60, 25); MD.setStatus(0x22, y); } } @@ -76,14 +97,18 @@ void MidiActivePeering::md_setup() { } MidiIDSysexListener.cleanup(); + oled_display.setFont(oldfont); } void MidiActivePeering::a4_setup() { DEBUG_PRINT_FN(); #ifdef OLED_DISPLAY - oled_display.clearDisplay(); - oled_display.setFont(); -#endif + + auto oldfont = oled_display.getFont(); + prepare_display(); + oled_display.drawBitmap(14, 8, icon_a4, 34, 42, WHITE); + oled_display.display(); +#else GUI.clearLines(); GUI.setLine(GUI.LINE1); GUI.put_string_at_fill(0, "Peering..."); @@ -91,12 +116,10 @@ void MidiActivePeering::a4_setup() { LCD.puts(GUI.lines[0].data); LCD.goLine(1); LCD.puts(GUI.lines[1].data); -#ifdef OLED_DISPLAY - oled_display.display(); #endif MidiUart.set_speed(31250, 2); for (uint8_t x = 0; x < 3 && Analog4.connected == false; x++) { - delay(300); + delay_progress(300); if (Analog4.getBlockingSettings(0)) { MidiUart2.device.set_id(DEVICE_A4); #ifdef OLED_DISPLAY @@ -113,6 +136,7 @@ void MidiActivePeering::a4_setup() { GUI.flash_strings_fill("MIDI DEVICE", "CONNECTED"); #endif } + oled_display.setFont(oldfont); } void MidiActivePeering::run() { char str[16]; diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.h b/avr/cores/megacommand/MCL/MidiActivePeering.h index 0dafca574..047ce4ae7 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.h +++ b/avr/cores/megacommand/MCL/MidiActivePeering.h @@ -17,6 +17,9 @@ class MidiActivePeering : public Task { virtual void run(); virtual void destroy() {}; + void prepare_display(); + void delay_progress(uint16_t clock_); + void md_setup(); void a4_setup(); uint8_t get_device(uint8_t port); From 770c44a0f628c15afd2e4672ccefa76f782db484 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 14:09:50 +1100 Subject: [PATCH 239/469] Improve SDDrive progress bar, by drawing in realtime --- avr/cores/megacommand/MCL/SDDrivePage.cpp | 13 +++++++++++++ avr/cores/megacommand/MCL/SDDrivePage.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index bee4076e7..fae43e739 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -27,6 +27,13 @@ void SDDrivePage::init() { FileBrowserPage::init(); } +void SDDrivePage::display() { + FileBrowserPage::display(); + if (progress_max != 0) { + mcl_gui.draw_progress_bar(progress_i, progress_max, false); + } +} + void SDDrivePage::save_snapshot() { DEBUG_PRINT_FN(); @@ -53,20 +60,26 @@ void SDDrivePage::save_snapshot() { return; } // Globals + progress_max = 8; for (int i = 0; i < 8; ++i) { + progress_i = i; mcl_gui.draw_progress("Saving global", i, 8); MD.getBlockingGlobal(i); mcl_sd.write_data(&MD.global, sizeof(MD.global), &file); } // Patterns + progress_max = 128; for (int i = 0; i < 128; ++i) { + progress_i = i; mcl_gui.draw_progress("Saving pattern", i, 128); DEBUG_PRINT(i); MD.getBlockingPattern(i); mcl_sd.write_data(&MD.pattern, sizeof(MD.pattern), &file); } // Kits + progress_max = 64; for (int i = 0; i < 64; ++i) { + progress_i = 64; mcl_gui.draw_progress("Saving kit", i, 64); MD.getBlockingKit(i); mcl_sd.write_data(&MD.kit, sizeof(MD.kit), &file); diff --git a/avr/cores/megacommand/MCL/SDDrivePage.h b/avr/cores/megacommand/MCL/SDDrivePage.h index 0c17bc008..705825aa4 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.h +++ b/avr/cores/megacommand/MCL/SDDrivePage.h @@ -12,8 +12,11 @@ class SDDrivePage : public FileBrowserPage { Encoder *e4 = NULL) : FileBrowserPage(e1, e2, e3, e4) { } + uint8_t progress_i; + uint8_t progress_max; void init(); void setup(); + void display(); void save_snapshot(); void load_snapshot(); virtual void on_select(const char*); From faa784fe3edd51ad616d08fa2648d900d1b78b01 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 14:14:05 +1100 Subject: [PATCH 240/469] Fix progress on SDDrivePage load --- avr/cores/megacommand/MCL/SDDrivePage.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index fae43e739..b442af600 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -90,9 +90,11 @@ void SDDrivePage::save_snapshot() { // mcl_sd.write_data(&MD.song, sizeof(MD.song), &file); // <--- ?? //} // Save complete + progress_max = 0; file.close(); gfx.alert("File Saved", temp_entry); } + } void SDDrivePage::load_snapshot() { @@ -116,7 +118,9 @@ void SDDrivePage::load_snapshot() { grid_page.prepare(); // Globals + progress_max = 8; for (int i = 0; i < 8; ++i) { + progress_i = i; mcl_gui.draw_progress("Loading global", i, 8); if (!mcl_sd.read_data(&MD.global, sizeof(MD.global), &file)) { goto load_error; @@ -128,7 +132,9 @@ void SDDrivePage::load_snapshot() { } } // Patterns + progress_max = 128; for (int i = 0; i < 128; ++i) { + progress_i = i; mcl_gui.draw_progress("Loading pattern", i, 128); if (!mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file)) { goto load_error; @@ -137,7 +143,9 @@ void SDDrivePage::load_snapshot() { MD.pattern.toSysex(); } // Kits + progress_max = 64; for (int i = 0; i < 64; ++i) { + progress_i = i; mcl_gui.draw_progress("Loading kit", i, 64); if (!mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file)) { goto load_error; @@ -146,10 +154,12 @@ void SDDrivePage::load_snapshot() { MD.kit.toSysex(); } // Load complete + progress_max = 0; file.close(); gfx.alert("Loaded", "Snapshot"); return; load_error: + progress_max = 0; file.close(); gfx.alert("Snapshot loading failed!", "SD card read failure"); return; From 1f1cff8176d2bbcec6c7c80d4f520fa0216275e9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 15:25:25 +1100 Subject: [PATCH 241/469] Bug Fix: copy select would extend past grid boundaries --- avr/cores/megacommand/MCL/GridPage.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 75b36708d..74d49138f 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -60,8 +60,6 @@ void GridPage::loop() { } } */ - encoders[2]->cur = 1; - encoders[3]->cur = 1; if (encoders[0]->hasChanged()) { diff = encoders[0]->cur - encoders[0]->old; new_val = cur_col + diff; @@ -95,6 +93,10 @@ void GridPage::loop() { volatile uint8_t *ptr; write_cfg = true; } + encoders[2]->cur = 1; + encoders[3]->cur = 1; + ((MCLEncoder*)encoders[2])->max = GRID_WIDTH - getCol(); + ((MCLEncoder*)encoders[3])->max = GRID_LENGTH - getRow(); #else cur_col = encoders[0]->cur; @@ -397,7 +399,9 @@ void GridPage::display_grid() { uint8_t col_shift = 0; if (show_slot_menu) { if (cur_col + encoders[2]->cur > MAX_VISIBLE_COLS - 1) { + col_shift = cur_col + encoders[2]->cur - MAX_VISIBLE_COLS; + } if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { From 34b0cbbdfda3100e52ffa82eba162767e927d32a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 15:31:20 +1100 Subject: [PATCH 242/469] Bug Fix: progress bar could extend beyond max --- avr/cores/megacommand/MCL/MCLGUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 9f6db612f..5696012e3 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -136,7 +136,7 @@ void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, float prog = (float)cur / (float)_max; auto progx = (uint8_t)(x_pos + 1 + prog * (s_progress_w - 2)); // draw the progress - oled_display.fillRect(x_pos + 1, y_pos + 1, progx - x_pos + 1, + oled_display.fillRect(x_pos + 1, y_pos + 1, progx - x_pos - 1, s_progress_h - 2, WHITE); uint8_t shift = 1; From d82d91f3ae3d96ec84feb59d936a181db4614d25 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 15:37:32 +1100 Subject: [PATCH 243/469] new project heading too long, would not be displayed --- avr/cores/megacommand/MCL/Project.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/Project.cpp b/avr/cores/megacommand/MCL/Project.cpp index 6aa672027..47a49b614 100644 --- a/avr/cores/megacommand/MCL/Project.cpp +++ b/avr/cores/megacommand/MCL/Project.cpp @@ -139,7 +139,7 @@ bool Project::new_project(const char *projectname) { #ifdef OLED_DISPLAY // if (i % 16 == 0) { - mcl_gui.draw_progress("Initializing project", i, GRID_LENGTH); + mcl_gui.draw_progress("INITIALIZING", i, GRID_LENGTH); // } #endif if (i % 2 == 0) { From a49ff79dbffbd61048b91c12663624cd3378039b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 15:41:28 +1100 Subject: [PATCH 244/469] Encoders were one pixel off centre --- avr/cores/megacommand/MCL/MCLGUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 5696012e3..51669fc2c 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -75,7 +75,7 @@ void MCLGUI::draw_knob(uint8_t i, const char *title, const char *text) { void MCLGUI::draw_knob(uint8_t i, Encoder *enc, const char *title) { uint8_t x = knob_x0 + i * knob_w; - draw_light_encoder(x + 6, 6, enc, title); + draw_light_encoder(x + 7, 6, enc, title); } static char title_buf[16]; From 7a27e2cd9a139cee522e60dbff1f6d6accde0730 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 16:02:52 +1100 Subject: [PATCH 245/469] Improve detection of track with no locks/lfo/display modulation --- avr/cores/megacommand/MCL/MDExploit.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/MDExploit.cpp b/avr/cores/megacommand/MCL/MDExploit.cpp index 0c9a62986..8675ccee8 100644 --- a/avr/cores/megacommand/MCL/MDExploit.cpp +++ b/avr/cores/megacommand/MCL/MDExploit.cpp @@ -1,6 +1,8 @@ /* Copyright 2018, Justin Mammarella jmamma@gmail.com */ -#include "MDExploit.h" +#include "LFOSeqTrack.h" #include "MCL.h" +#include "MCLSeq.h" +#include "MDExploit.h" uint8_t last_md_track = 0; void MDExploit::setup() {} @@ -105,13 +107,23 @@ bool MDExploit::on(bool switch_tracks) { MD.clear_all_windows(); delay(20); if ((switch_tracks)) { - uint8_t n = NUM_MD_TRACKS - 1; + uint8_t n = 0; track_with_nolocks = 255; - while (n && (track_with_nolocks == 255)) { - if (mcl_seq.md_tracks[n].lock_mask == 0) { - track_with_nolocks = n; + while (n < NUM_MD_TRACKS && (track_with_nolocks == 255)) { + if ((MD.kit.models[n] > CTR_DX_MODEL) || (MD.kit.models[n] < MID_MODEL)) { + + if (mcl_seq.md_tracks[n].lock_mask == 0) { + track_with_nolocks = n; + for (uint8_t a = 0; a < mcl_seq.num_lfo_tracks; a++) { + for (uint8_t b = 0; b < NUM_LFO_PARAMS; b++) { + if (mcl_seq.lfo_tracks[a].params[b].dest - 1 == n) { + track_with_nolocks = 255; + } + } + } + } } - n--; + n++; } if (track_with_nolocks == 255) { track_with_nolocks = 15; From c6928c21ccf2a4ac42fa3027b6b6284f4d67eede Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 17:04:17 +1100 Subject: [PATCH 246/469] If user switches track and track == track_with_no_locks, find another track_with_no_locks by toggling exploit --- avr/cores/megacommand/MCL/SeqPage.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 023610b69..bb3ae5371 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -43,7 +43,13 @@ void SeqPage::cleanup() { void SeqPage::select_track(uint8_t device, uint8_t track) { if (device == DEVICE_MD) { + last_md_track = track; + if (track == md_exploit.track_with_nolocks) { + md_exploit.off(); + note_interface.state = true; + md_exploit.on(); + } #ifdef EXT_TRACK if (GUI.currentPage() == &seq_extstep_page) { GUI.setPage(&seq_step_page); From aa79cbacfcf63e17d8947fa3e09f5712c9438db9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 17:11:09 +1100 Subject: [PATCH 247/469] Fix range of track_with_no_locks --- avr/cores/megacommand/MCL/MDExploit.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/avr/cores/megacommand/MCL/MDExploit.cpp b/avr/cores/megacommand/MCL/MDExploit.cpp index 8675ccee8..cafde5b53 100644 --- a/avr/cores/megacommand/MCL/MDExploit.cpp +++ b/avr/cores/megacommand/MCL/MDExploit.cpp @@ -107,23 +107,26 @@ bool MDExploit::on(bool switch_tracks) { MD.clear_all_windows(); delay(20); if ((switch_tracks)) { - uint8_t n = 0; + uint8_t n = NUM_MD_TRACKS; track_with_nolocks = 255; - while (n < NUM_MD_TRACKS && (track_with_nolocks == 255)) { - if ((MD.kit.models[n] > CTR_DX_MODEL) || (MD.kit.models[n] < MID_MODEL)) { - - if (mcl_seq.md_tracks[n].lock_mask == 0) { - track_with_nolocks = n; - for (uint8_t a = 0; a < mcl_seq.num_lfo_tracks; a++) { - for (uint8_t b = 0; b < NUM_LFO_PARAMS; b++) { - if (mcl_seq.lfo_tracks[a].params[b].dest - 1 == n) { - track_with_nolocks = 255; + while (n > 0 && (track_with_nolocks == 255)) { + n--; + if (n != last_md_track) { + if ((MD.kit.models[n] > CTR_DX_MODEL) || + (MD.kit.models[n] < MID_MODEL)) { + + if (mcl_seq.md_tracks[n].lock_mask == 0) { + track_with_nolocks = n; + for (uint8_t a = 0; a < mcl_seq.num_lfo_tracks; a++) { + for (uint8_t b = 0; b < NUM_LFO_PARAMS; b++) { + if (mcl_seq.lfo_tracks[a].params[b].dest - 1 == n) { + track_with_nolocks = 255; + } } } } } } - n++; } if (track_with_nolocks == 255) { track_with_nolocks = 15; From 72d0959391e4f031807dd0171e8f901eb10ebaa9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 17:41:23 +1100 Subject: [PATCH 248/469] Some improvements to TomThumb --- .../megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp index 4d1787b69..d0e5878ed 100644 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp @@ -39,7 +39,7 @@ const uint8_t TomThumbBitmaps[] PROGMEM = { 0x40, 0xA0, 0xE0, 0x80, 0x60, /* 0x40 at */ 0x40, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x41 A */ 0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /* 0x42 B */ - 0x60, 0x80, 0x80, 0x80, 0x60, /* 0x43 C */ + 0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x43 C */ //Edited 0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x44 D */ 0xE0, 0x80, 0xE0, 0x80, 0xE0, /* 0x45 E */ 0xE0, 0x80, 0xE0, 0x80, 0x80, /* 0x46 F */ @@ -54,14 +54,14 @@ const uint8_t TomThumbBitmaps[] PROGMEM = { 0x40, 0xA0, 0xA0, 0xA0, 0x40, /* 0x4F O */ 0xC0, 0xA0, 0xC0, 0x80, 0x80, /* 0x50 P */ 0x40, 0xA0, 0xA0, 0xE0, 0x60, /* 0x51 Q */ - 0xC0, 0xA0, 0xE0, 0xC0, 0xA0, /* 0x52 R */ - 0x60, 0x80, 0x40, 0x20, 0xC0, /* 0x53 S */ + 0xE0, 0xA0, 0xE0, 0xC0, 0xA0, /* 0x52 R */ + 0xE0, 0x80, 0xE0, 0x20, 0xE0, /* 0x53 S */ //Edited 0xE0, 0x40, 0x40, 0x40, 0x40, /* 0x54 T */ 0xA0, 0xA0, 0xA0, 0xA0, 0x60, /* 0x55 U */ 0xA0, 0xA0, 0xA0, 0x40, 0x40, /* 0x56 V */ 0xA0, 0xA0, 0xE0, 0xE0, 0xA0, /* 0x57 W */ 0xA0, 0xA0, 0x40, 0xA0, 0xA0, /* 0x58 X */ - 0xA0, 0xA0, 0x40, 0x40, 0x40, /* 0x59 Y */ + 0xA0, 0xA0, 0xE0, 0x20, 0xE0, /* 0x59 Y */ 0xE0, 0x20, 0x40, 0x80, 0xE0, /* 0x5A Z */ 0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x5B bracketleft */ 0x80, 0x40, 0x20, /* 0x5C backslash */ From 12d24e708d508b50516971660d4f6d27f834ded7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 9 Nov 2019 17:45:41 +1100 Subject: [PATCH 249/469] revert chance to 'c' --- avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp index d0e5878ed..551896fa5 100644 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Fonts/TomThumb.cpp @@ -39,7 +39,7 @@ const uint8_t TomThumbBitmaps[] PROGMEM = { 0x40, 0xA0, 0xE0, 0x80, 0x60, /* 0x40 at */ 0x40, 0xA0, 0xE0, 0xA0, 0xA0, /* 0x41 A */ 0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /* 0x42 B */ - 0xE0, 0x80, 0x80, 0x80, 0xE0, /* 0x43 C */ //Edited + 0x60, 0x80, 0x80, 0x80, 0x60, /* 0x43 C */ 0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /* 0x44 D */ 0xE0, 0x80, 0xE0, 0x80, 0xE0, /* 0x45 E */ 0xE0, 0x80, 0xE0, 0x80, 0x80, /* 0x46 F */ From 6b80e1ae7b3950e035ba6dd75359d36bf07397e0 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 16:08:58 +0800 Subject: [PATCH 250/469] revert icon_route --- avr/cores/megacommand/MCL/MCLGUI.cpp | 15 +++++++-------- avr/cores/megacommand/MCL/RoutePage.cpp | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index eee272225..31713d21b 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -655,15 +655,14 @@ const unsigned char icon_rhytmecho[] PROGMEM = { 0x1b, 0x6c, 0x7b, 0x1f, 0x6c, 0x7b, 0x1f, 0x6c, 0x63, 0x1b, 0x6c, 0x7b, 0xdb, 0x7c, 0x7b, 0xdb, 0x7c, 0x00, 0x00, 0x00}; -// 'route', 24x25px +// 'route', 24x16px const unsigned char icon_route[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x38, 0x70, 0x1c, 0x38, - 0x70, 0x3e, 0x7c, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, - 0x10, 0x20, 0x08, 0x10, 0x20, 0x3b, 0xfe, 0x3e, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x77, 0x77, 0x77, 0x55, 0x55, 0x55, 0x77, 0x77, - 0x77, 0x20, 0x20, 0x20, 0x77, 0x77, 0x77, 0x55, 0xd5, 0xdd, 0x77, - 0x77, 0x77, 0x00, 0x00, 0x00, 0x19, 0xd5, 0xdc, 0x15, 0x54, 0x90, - 0x15, 0x54, 0x9c, 0x19, 0x54, 0x90, 0x15, 0xdc, 0x9c}; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf1, 0xf8, 0x07, + 0xe3, 0xf0, 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x1f, 0x8f, + 0xc0, 0x0f, 0x07, 0x80, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x07, 0x80, 0x19, 0x8c, 0xc0, 0x0f, 0x07, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; // 'mixer', 28x15px const unsigned char icon_mixer[] PROGMEM = { diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index 6d5742d62..8771f2a0b 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -181,7 +181,7 @@ void RoutePage::display() { auto *oldfont = oled_display.getFont(); oled_display.clearDisplay(); - oled_display.drawBitmap(0, 0, icon_route, 24, 18, WHITE); + oled_display.drawBitmap(0, 0, icon_route, 24, 16, WHITE); mcl_gui.draw_knob_frame(); From 316d3e4ab60ecd16654cd265538c7cf99ed2ba19 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 16:14:07 +0800 Subject: [PATCH 251/469] revert PageSelectPage changes --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 139 ++++++++++--------- avr/cores/megacommand/MCL/PageSelectPage.h | 34 ++--- 2 files changed, 92 insertions(+), 81 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 9c2765163..788d63fc0 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -1,53 +1,16 @@ #include "MCL.h" #include "PageSelectPage.h" -#include -struct PageCategory { - char Name[16]; - uint8_t PageCount; - uint8_t FirstPage; -}; - -struct PageSelectEntry { - char Name[16]; - LightPage *Page; - uint8_t PageNumber; // same as trig id - uint8_t CategoryId; -}; - -const PageCategory Categories[] PROGMEM = { - {"GRID", 1, 0}, {"SEQ", 4, 1}, {"MIX", 3, 5}, {"SOUND", 2, 8}, - {"FX", 2, 10}, {"RAM", 2, 12}, {"LFO", 1, 14}, {"CONFIG", 1, 15}, -}; - -const PageSelectEntry Entries[] PROGMEM = { - {"GRID", &grid_page, 0xFF, 0}, - - {"STEPS", &seq_step_page, 0, 1}, - {"RECORD", &seq_rtrk_page, 1, 1}, - {"LOCKS", &seq_param_page[0], 2, 1}, - {"CHROMA", &seq_ptc_page, 3, 1}, - - {"MIXER", &mixer_page, 4, 2}, - {"ROUTE", &route_page, 5, 2}, - {"LOUDNESS", &loudness_page, 6, 2}, - - {"WAV DESIGNER", &wd.pages[0], 7, 3}, - {"SOUND MANAGER", &sound_browser, 8, 3}, - - {"DELAY", &fx_page_a, 9, 4}, - {"REVERB", &fx_page_b, 10, 4}, - - {"RAM-1", &ram_page_a, 11, 5}, - {"RAM-2", &ram_page_b, 12, 5}, - - {"LFO", &lfo_page, 13, 6}, - - {"CONFIG", &system_page, 0xFF, 7}, -}; - -constexpr uint8_t n_category = sizeof(Categories) / sizeof(PageCategory); -constexpr uint8_t n_entry = sizeof(Entries) / sizeof(PageSelectEntry); +#define MIX_PAGE 0 +#define ROUTE_PAGE 1 +#define RAM_PAGE_A 14 +#define RAM_PAGE_B 15 +#define WAVD_PAGE 8 +#define SOUND 7 +#define FX_PAGE_A 10 +#define FX_PAGE_B 11 +#define LOUDNESS 9 +#define LFO_PAGE 6 void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -57,21 +20,74 @@ void PageSelectPage::init() { md_exploit.on(); note_interface.state = true; } -void PageSelectPage::cleanup() { note_interface.init_notes(); } +void PageSelectPage::cleanup() { + note_interface.init_notes(); +} LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { - for (uint8_t i = 0; i < n_entry; ++i) { - if (page_number == pgm_read_byte(&Entries[i].PageNumber)) { - if (str) { - m_strncpy_p(str, (PGM_P) & (Entries[i].Name), 16); - } - return pgm_read_word(&Entries[i].Page); - } - } - if (str) { - strncpy(str, "----", 5); + LightPage *r_page = NULL; + switch (page_number) { + case MIX_PAGE: + if (str) + strncpy(str, "MIX", 4); + r_page = &mixer_page; + break; + case ROUTE_PAGE: + if (str) + strncpy(str, "ROUTE", 6); + r_page = &route_page; + break; + case RAM_PAGE_A: + if (str) + strncpy(str, "RAM 1", 6); + r_page = &ram_page_a; + break; + case RAM_PAGE_B: + if (str) + strncpy(str, "RAM 2", 6); + r_page = &ram_page_b; + break; + case FX_PAGE_A: + if (str) + strncpy(str, "DELAY", 8); + r_page = &fx_page_a; + break; + case FX_PAGE_B: + if (str) + strncpy(str, "REVERB", 8); + r_page = &fx_page_b; + break; + case LFO_PAGE: + if (str) + strncpy(str, "LFO", 8); + r_page = &lfo_page; + break; +#ifdef WAV_DESIGNER + case WAVD_PAGE: + if (str) + strncpy(str, "WAV DESIGNER", 13); + r_page = wd.last_page; + break; +#endif +#ifdef SOUND_PAGE + case SOUND: + if (str) + strncpy(str, "SOUND", 6); + r_page = &sound_browser; + break; +#endif +#ifdef LOUDNESS_PAGE + case LOUDNESS: + if (str) + strncpy(str, "LOUDNESS",9); + r_page = &loudness_page; + break; +#endif + default: + if (str) + strncpy(str, "----", 5); } - return NULL; + return r_page; } uint8_t PageSelectPage::get_nextpage_down() { @@ -91,7 +107,6 @@ uint8_t PageSelectPage::get_nextpage_up() { } return page_select; } - void PageSelectPage::loop() { MCLEncoder *enc_ = &enc1; // largest_sine_peak = 1.0 / 16.00; @@ -110,9 +125,9 @@ void PageSelectPage::loop() { } void PageSelectPage::display() { -#ifdef OLED_DISPLAY + #ifdef OLED_DISPLAY oled_display.clearDisplay(); -#endif + #endif GUI.setLine(GUI.LINE1); char str[16]; get_page(page_select, str); diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index aba5cc90f..aa1e92636 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -6,25 +6,21 @@ #include "GUI.h" class PageSelectPage : public LightPage { -public: - MCLEncoder enc1; - uint8_t page_select; - PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, - Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4) { - encoders[0] = &enc1; - } - - virtual void display(); - virtual void setup(); - virtual void init(); - virtual void loop(); - virtual void cleanup(); - virtual bool handleEvent(gui_event_t *event); - - LightPage *get_page(uint8_t page_number, char *str = NULL); - uint8_t get_nextpage_down(); - uint8_t get_nextpage_up(); + public: + MCLEncoder enc1; + uint8_t page_select; + PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage( e1, e2, e3 ,e4) { + encoders[0] = &enc1; + } + void display(); + void setup(); + void init(); + void loop(); + void cleanup(); + LightPage *get_page(uint8_t page_number, char *str = NULL); + uint8_t get_nextpage_down(); + uint8_t get_nextpage_up(); + virtual bool handleEvent(gui_event_t *event); }; extern PageSelectPage page_select_page; From 1c31d6c432938feb9f8aab26d30eb752b6094131 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 16:16:13 +0800 Subject: [PATCH 252/469] PageSelectPage wip --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 139 +++++++++---------- avr/cores/megacommand/MCL/PageSelectPage.h | 34 +++-- 2 files changed, 81 insertions(+), 92 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 788d63fc0..9c2765163 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -1,16 +1,53 @@ #include "MCL.h" #include "PageSelectPage.h" +#include -#define MIX_PAGE 0 -#define ROUTE_PAGE 1 -#define RAM_PAGE_A 14 -#define RAM_PAGE_B 15 -#define WAVD_PAGE 8 -#define SOUND 7 -#define FX_PAGE_A 10 -#define FX_PAGE_B 11 -#define LOUDNESS 9 -#define LFO_PAGE 6 +struct PageCategory { + char Name[16]; + uint8_t PageCount; + uint8_t FirstPage; +}; + +struct PageSelectEntry { + char Name[16]; + LightPage *Page; + uint8_t PageNumber; // same as trig id + uint8_t CategoryId; +}; + +const PageCategory Categories[] PROGMEM = { + {"GRID", 1, 0}, {"SEQ", 4, 1}, {"MIX", 3, 5}, {"SOUND", 2, 8}, + {"FX", 2, 10}, {"RAM", 2, 12}, {"LFO", 1, 14}, {"CONFIG", 1, 15}, +}; + +const PageSelectEntry Entries[] PROGMEM = { + {"GRID", &grid_page, 0xFF, 0}, + + {"STEPS", &seq_step_page, 0, 1}, + {"RECORD", &seq_rtrk_page, 1, 1}, + {"LOCKS", &seq_param_page[0], 2, 1}, + {"CHROMA", &seq_ptc_page, 3, 1}, + + {"MIXER", &mixer_page, 4, 2}, + {"ROUTE", &route_page, 5, 2}, + {"LOUDNESS", &loudness_page, 6, 2}, + + {"WAV DESIGNER", &wd.pages[0], 7, 3}, + {"SOUND MANAGER", &sound_browser, 8, 3}, + + {"DELAY", &fx_page_a, 9, 4}, + {"REVERB", &fx_page_b, 10, 4}, + + {"RAM-1", &ram_page_a, 11, 5}, + {"RAM-2", &ram_page_b, 12, 5}, + + {"LFO", &lfo_page, 13, 6}, + + {"CONFIG", &system_page, 0xFF, 7}, +}; + +constexpr uint8_t n_category = sizeof(Categories) / sizeof(PageCategory); +constexpr uint8_t n_entry = sizeof(Entries) / sizeof(PageSelectEntry); void PageSelectPage::setup() {} void PageSelectPage::init() { @@ -20,74 +57,21 @@ void PageSelectPage::init() { md_exploit.on(); note_interface.state = true; } -void PageSelectPage::cleanup() { - note_interface.init_notes(); -} +void PageSelectPage::cleanup() { note_interface.init_notes(); } LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { - LightPage *r_page = NULL; - switch (page_number) { - case MIX_PAGE: - if (str) - strncpy(str, "MIX", 4); - r_page = &mixer_page; - break; - case ROUTE_PAGE: - if (str) - strncpy(str, "ROUTE", 6); - r_page = &route_page; - break; - case RAM_PAGE_A: - if (str) - strncpy(str, "RAM 1", 6); - r_page = &ram_page_a; - break; - case RAM_PAGE_B: - if (str) - strncpy(str, "RAM 2", 6); - r_page = &ram_page_b; - break; - case FX_PAGE_A: - if (str) - strncpy(str, "DELAY", 8); - r_page = &fx_page_a; - break; - case FX_PAGE_B: - if (str) - strncpy(str, "REVERB", 8); - r_page = &fx_page_b; - break; - case LFO_PAGE: - if (str) - strncpy(str, "LFO", 8); - r_page = &lfo_page; - break; -#ifdef WAV_DESIGNER - case WAVD_PAGE: - if (str) - strncpy(str, "WAV DESIGNER", 13); - r_page = wd.last_page; - break; -#endif -#ifdef SOUND_PAGE - case SOUND: - if (str) - strncpy(str, "SOUND", 6); - r_page = &sound_browser; - break; -#endif -#ifdef LOUDNESS_PAGE - case LOUDNESS: - if (str) - strncpy(str, "LOUDNESS",9); - r_page = &loudness_page; - break; -#endif - default: - if (str) - strncpy(str, "----", 5); + for (uint8_t i = 0; i < n_entry; ++i) { + if (page_number == pgm_read_byte(&Entries[i].PageNumber)) { + if (str) { + m_strncpy_p(str, (PGM_P) & (Entries[i].Name), 16); + } + return pgm_read_word(&Entries[i].Page); + } + } + if (str) { + strncpy(str, "----", 5); } - return r_page; + return NULL; } uint8_t PageSelectPage::get_nextpage_down() { @@ -107,6 +91,7 @@ uint8_t PageSelectPage::get_nextpage_up() { } return page_select; } + void PageSelectPage::loop() { MCLEncoder *enc_ = &enc1; // largest_sine_peak = 1.0 / 16.00; @@ -125,9 +110,9 @@ void PageSelectPage::loop() { } void PageSelectPage::display() { - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY oled_display.clearDisplay(); - #endif +#endif GUI.setLine(GUI.LINE1); char str[16]; get_page(page_select, str); diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index aa1e92636..aba5cc90f 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -6,21 +6,25 @@ #include "GUI.h" class PageSelectPage : public LightPage { - public: - MCLEncoder enc1; - uint8_t page_select; - PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage( e1, e2, e3 ,e4) { - encoders[0] = &enc1; - } - void display(); - void setup(); - void init(); - void loop(); - void cleanup(); - LightPage *get_page(uint8_t page_number, char *str = NULL); - uint8_t get_nextpage_down(); - uint8_t get_nextpage_up(); - virtual bool handleEvent(gui_event_t *event); +public: + MCLEncoder enc1; + uint8_t page_select; + PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, + Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) { + encoders[0] = &enc1; + } + + virtual void display(); + virtual void setup(); + virtual void init(); + virtual void loop(); + virtual void cleanup(); + virtual bool handleEvent(gui_event_t *event); + + LightPage *get_page(uint8_t page_number, char *str = NULL); + uint8_t get_nextpage_down(); + uint8_t get_nextpage_up(); }; extern PageSelectPage page_select_page; From 6207310944dc341a3425b786eebb605dc47c7b99 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 18:45:51 +0800 Subject: [PATCH 253/469] fix #68 --- avr/cores/megacommand/MCL/SDDrivePage.cpp | 285 +++++++++++++--------- 1 file changed, 173 insertions(+), 112 deletions(-) diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index b442af600..106f2d359 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -28,142 +28,203 @@ void SDDrivePage::init() { } void SDDrivePage::display() { - FileBrowserPage::display(); - if (progress_max != 0) { - mcl_gui.draw_progress_bar(progress_i, progress_max, false); - } + FileBrowserPage::display(); + if (progress_max != 0) { + mcl_gui.draw_progress_bar(progress_i, progress_max, false); + } } void SDDrivePage::save_snapshot() { DEBUG_PRINT_FN(); char entry_name[] = " "; + bool error_is_md = false; - if (mcl_gui.wait_for_input(entry_name, "Snapshot Name", 8)) { - if (file.isOpen()) { - file.close(); - } + if (!MD.connected) { + gfx.alert("Error", "MD is not connected"); + return; + } + + if (!mcl_gui.wait_for_input(entry_name, "Snapshot Name", 8)) { + return; + } + + if (file.isOpen()) { + file.close(); + } + + MidiUart.sendRaw(MIDI_STOP); + MidiClock.handleImmediateMidiStop(); + // Stop everything + grid_page.prepare(); + + char temp_entry[16]; + strcpy(temp_entry, entry_name); + strcat(temp_entry, c_snapshot_suffix); - MidiUart.sendRaw(MIDI_STOP); - MidiClock.handleImmediateMidiStop(); - // Stop everything - grid_page.prepare(); - - char temp_entry[16]; - strcpy(temp_entry, entry_name); - strcat(temp_entry, c_snapshot_suffix); - DEBUG_PRINTLN("creating new snapshot:"); - DEBUG_PRINTLN(temp_entry); - if (!file.open(temp_entry, O_WRITE | O_CREAT)) { - DEBUG_PRINTLN("error openning"); - gfx.alert("Error", "Cannot open file for write"); + // File exists? + if (file.open(temp_entry, O_READ)) { + file.close(); + char msg[24] = {'\0'}; + strcpy(msg, "Overwrite "); + strcat(msg, entry_name); + strcat(msg, "?"); + + if (!mcl_gui.wait_for_confirm("File exists", msg)) { return; } - // Globals - progress_max = 8; - for (int i = 0; i < 8; ++i) { - progress_i = i; - mcl_gui.draw_progress("Saving global", i, 8); - MD.getBlockingGlobal(i); - mcl_sd.write_data(&MD.global, sizeof(MD.global), &file); + } + + DEBUG_PRINTLN("creating new snapshot:"); + DEBUG_PRINTLN(temp_entry); + if (!file.open(temp_entry, O_WRITE | O_CREAT)) { + DEBUG_PRINTLN("error openning"); + error_is_md = false; + goto save_error; + } + // Globals + progress_max = 8; + for (int i = 0; i < 8; ++i) { + progress_i = i; + mcl_gui.draw_progress("Saving global", i, 8); + if (!MD.getBlockingGlobal(i)) { + error_is_md = true; + goto save_error; + } + if (!mcl_sd.write_data(&MD.global, sizeof(MD.global), &file)) { + error_is_md = false; + goto save_error; } - // Patterns - progress_max = 128; - for (int i = 0; i < 128; ++i) { - progress_i = i; - mcl_gui.draw_progress("Saving pattern", i, 128); - DEBUG_PRINT(i); - MD.getBlockingPattern(i); - mcl_sd.write_data(&MD.pattern, sizeof(MD.pattern), &file); + } + // Patterns + progress_max = 128; + for (int i = 0; i < 128; ++i) { + progress_i = i; + mcl_gui.draw_progress("Saving pattern", i, 128); + DEBUG_PRINT(i); + if (!MD.getBlockingPattern(i)) { + error_is_md = true; + goto save_error; } - // Kits - progress_max = 64; - for (int i = 0; i < 64; ++i) { - progress_i = 64; - mcl_gui.draw_progress("Saving kit", i, 64); - MD.getBlockingKit(i); - mcl_sd.write_data(&MD.kit, sizeof(MD.kit), &file); + if (!mcl_sd.write_data(&MD.pattern, sizeof(MD.pattern), &file)) { + error_is_md = false; + goto save_error; } - // Songs - // for(int i=0;i<64;++i){ - // MD.getBlockingSong(i); - // mcl_sd.write_data(&MD.song, sizeof(MD.song), &file); // <--- ?? - //} - // Save complete - progress_max = 0; - file.close(); - gfx.alert("File Saved", temp_entry); } - + // Kits + progress_max = 64; + for (int i = 0; i < 64; ++i) { + progress_i = 64; + mcl_gui.draw_progress("Saving kit", i, 64); + if (!MD.getBlockingKit(i)) { + error_is_md = true; + goto save_error; + } + if (!mcl_sd.write_data(&MD.kit, sizeof(MD.kit), &file)) { + error_is_md = false; + goto save_error; + } + } + // Songs + // for(int i=0;i<64;++i){ + // MD.getBlockingSong(i); + // mcl_sd.write_data(&MD.song, sizeof(MD.song), &file); // <--- ?? + //} + // Save complete + progress_max = 0; + file.close(); + gfx.alert("File Saved", temp_entry); + return; + +save_error: + progress_max = 0; + file.close(); + if (error_is_md) { + gfx.alert("File not saved", "MD read error!"); + } else { + gfx.alert("File not saved", "SD card write error!"); + } } void SDDrivePage::load_snapshot() { DEBUG_PRINT_FN(); - if (file.isOpen()) { - char temp_entry[16]; - file.getName(temp_entry, 16); - file.close(); - DEBUG_PRINTLN("loading snapshot"); - DEBUG_PRINTLN(temp_entry); - if (!file.open(temp_entry, O_READ)) { - DEBUG_PRINTLN("error openning"); - gfx.alert("Error", "Cannot open file for read"); - return; - } - MidiUart.sendRaw(MIDI_STOP); - MidiClock.handleImmediateMidiStop(); - // Stop everything - grid_page.prepare(); - - // Globals - progress_max = 8; - for (int i = 0; i < 8; ++i) { - progress_i = i; - mcl_gui.draw_progress("Loading global", i, 8); - if (!mcl_sd.read_data(&MD.global, sizeof(MD.global), &file)) { - goto load_error; - } - mcl_actions.md_setsysex_recpos(2, i); - { - ElektronDataToSysexEncoder encoder(&MidiUart); - MD.global.toSysex(encoder); - } + if (!file.isOpen()) { + return; + } + + if (!MD.connected) { + gfx.alert("Error", "MD is not connected"); + return; + } + + if (!mcl_gui.wait_for_confirm("Load snapshot", "Overwrite MD data?")) { + return; + } + + char temp_entry[16]; + file.getName(temp_entry, 16); + file.close(); + DEBUG_PRINTLN("loading snapshot"); + DEBUG_PRINTLN(temp_entry); + if (!file.open(temp_entry, O_READ)) { + DEBUG_PRINTLN("error openning"); + gfx.alert("Error", "Cannot open file for read"); + return; + } + + MidiUart.sendRaw(MIDI_STOP); + MidiClock.handleImmediateMidiStop(); + // Stop everything + grid_page.prepare(); + + // Globals + progress_max = 8; + for (int i = 0; i < 8; ++i) { + progress_i = i; + mcl_gui.draw_progress("Loading global", i, 8); + if (!mcl_sd.read_data(&MD.global, sizeof(MD.global), &file)) { + goto load_error; + } + mcl_actions.md_setsysex_recpos(2, i); + { + ElektronDataToSysexEncoder encoder(&MidiUart); + MD.global.toSysex(encoder); } - // Patterns - progress_max = 128; - for (int i = 0; i < 128; ++i) { - progress_i = i; - mcl_gui.draw_progress("Loading pattern", i, 128); - if (!mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file)) { - goto load_error; - } - mcl_actions.md_setsysex_recpos(8, i); - MD.pattern.toSysex(); + } + // Patterns + progress_max = 128; + for (int i = 0; i < 128; ++i) { + progress_i = i; + mcl_gui.draw_progress("Loading pattern", i, 128); + if (!mcl_sd.read_data(&MD.pattern, sizeof(MD.pattern), &file)) { + goto load_error; } - // Kits - progress_max = 64; - for (int i = 0; i < 64; ++i) { - progress_i = i; - mcl_gui.draw_progress("Loading kit", i, 64); - if (!mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file)) { - goto load_error; - } - mcl_actions.md_setsysex_recpos(4, i); - MD.kit.toSysex(); + mcl_actions.md_setsysex_recpos(8, i); + MD.pattern.toSysex(); + } + // Kits + progress_max = 64; + for (int i = 0; i < 64; ++i) { + progress_i = i; + mcl_gui.draw_progress("Loading kit", i, 64); + if (!mcl_sd.read_data(&MD.kit, sizeof(MD.kit), &file)) { + goto load_error; } - // Load complete - progress_max = 0; - file.close(); - gfx.alert("Loaded", "Snapshot"); - return; - load_error: - progress_max = 0; - file.close(); - gfx.alert("Snapshot loading failed!", "SD card read failure"); - return; + mcl_actions.md_setsysex_recpos(4, i); + MD.kit.toSysex(); } + // Load complete + progress_max = 0; + file.close(); + gfx.alert("Loaded", "Snapshot"); + return; +load_error: + progress_max = 0; + file.close(); + gfx.alert("Snapshot loading failed!", "SD card read failure"); + return; } void SDDrivePage::on_new() { From 81695686b77b0deba45374a7ed13b119a19fd035 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 18:58:53 +0800 Subject: [PATCH 254/469] SeqPtcPage: queue redraw events only when responsing to the midi data --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 905cb7e1c..ddeac8681 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -570,7 +570,6 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { uint8_t pitch = seq_ptc_page.calc_pitch(note_num); uint8_t scaled_pitch = pitch - (pitch / 24) * 24; SET_BIT64(seq_ptc_page.note_mask, scaled_pitch); - seq_ptc_page.queue_redraw(); // matches control channel, or MIDI2 is OMNI? // then route midi message to MD @@ -578,6 +577,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { seq_ptc_page.trig_md_fromext(pitch); SeqPage::midi_device = midi_active_peering.get_device(UART1_PORT); + seq_ptc_page.queue_redraw(); return; } @@ -595,6 +595,7 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { if ((seq_ptc_page.recording) && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteon(pitch, msg[2]); } + seq_ptc_page.queue_redraw(); #endif } @@ -614,11 +615,11 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { uint8_t pitch = seq_ptc_page.calc_pitch(note_num); uint8_t scaled_pitch = pitch - (pitch / 24) * 24; CLEAR_BIT64(seq_ptc_page.note_mask, scaled_pitch); - seq_ptc_page.queue_redraw(); if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { seq_ptc_page.clear_trig_fromext(pitch); + seq_ptc_page.queue_redraw(); return; } #ifdef EXT_TRACKS @@ -633,6 +634,7 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { if (seq_ptc_page.recording && (MidiClock.state == 2)) { mcl_seq.ext_tracks[channel].record_ext_track_noteoff(pitch, msg[2]); } + seq_ptc_page.queue_redraw(); #endif } From 6ef30b7c479cfaf66e8085ea94a34ee9db8c4add Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 19:43:01 +0800 Subject: [PATCH 255/469] only call queue_redraw if the TI event is actually handled --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index ddeac8681..ecc5e8398 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -427,9 +427,6 @@ void SeqPtcPage::queue_redraw() { bool SeqPtcPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { - // deferred trigger redraw to update TI keyboard feedback. - queue_redraw(); - uint8_t mask = event->mask; uint8_t port = event->port; uint8_t device = midi_active_peering.get_device(port); @@ -453,6 +450,9 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { CLEAR_BIT(note_mask, pitch); } + // deferred trigger redraw to update TI keyboard feedback. + queue_redraw(); + return true; } // TI events From ded8c09e5df8fbb1d8f0980beba314ec3d70bda5 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 19:50:24 +0800 Subject: [PATCH 256/469] let's debug that timer --- avr/cores/megacommand/MCL/SeqPage.cpp | 2 -- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 8 ++++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index b81022cdd..7cd1fe23c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -130,7 +130,6 @@ bool SeqPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.ENCODER2)) { GUI.setPage(&seq_rtrk_page); - return false; } if (EVENT_PRESSED(event, Buttons.ENCODER3)) { @@ -141,7 +140,6 @@ bool SeqPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.ENCODER4)) { GUI.setPage(&seq_ptc_page); - return false; } } diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index ecc5e8398..81cc83ebd 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -188,6 +188,11 @@ void SeqPtcPage::loop() { redisplay = true; } + oled_display.clearDisplay(); + oled_display.setCursor(0,0); + oled_display.print(deferred_timer); + oled_display.display(); + if (deferred_timer > 0) { if (--deferred_timer == 0) { redisplay = true; @@ -493,9 +498,9 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { } else { mcl_seq.ext_tracks[last_ext_track].resolution = 1; } + seq_ptc_page.queue_redraw(); } #endif - seq_ptc_page.queue_redraw(); return true; } @@ -519,7 +524,6 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { mcl_seq.ext_tracks[last_ext_track].clear_track(); } #endif - seq_ptc_page.queue_redraw(); return true; } From 51870ef8bc6428d853cc27295810f5dedaedca21 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 20:26:01 +0800 Subject: [PATCH 257/469] deferred timer is true 100ms now --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 14 ++++---------- avr/cores/megacommand/MCL/SeqPtcPage.h | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 81cc83ebd..8d1de7eec 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -188,15 +188,9 @@ void SeqPtcPage::loop() { redisplay = true; } - oled_display.clearDisplay(); - oled_display.setCursor(0,0); - oled_display.print(deferred_timer); - oled_display.display(); - - if (deferred_timer > 0) { - if (--deferred_timer == 0) { - redisplay = true; - } + if (deferred_timer != 0 && clock_diff(deferred_timer, slowclock) > render_defer_time) { + deferred_timer = 0; + redisplay = true; } } @@ -426,7 +420,7 @@ void SeqPtcPage::trig_md_fromext(uint8_t note_num) { } void SeqPtcPage::queue_redraw() { - deferred_timer = render_defer_time; + deferred_timer = slowclock; } bool SeqPtcPage::handleEvent(gui_event_t *event) { diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index ebac41134..661bb2638 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -33,8 +33,8 @@ class SeqPtcPage : public SeqPage { uint8_t last_midi_state = 0; int8_t poly_notes[MAX_POLY_NOTES]; uint64_t note_mask = 0; - uint8_t deferred_timer = 0; - const uint8_t render_defer_time = 200; + uint16_t deferred_timer = 0; + const uint8_t render_defer_time = 100; SeqPtcMidiEvents midi_events; SeqPtcPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, From 615c306fb1394656ab69cabbf18fcdbb97bd25fe Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 20:33:57 +0800 Subject: [PATCH 258/469] 50ms --- avr/cores/megacommand/MCL/SeqPtcPage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index 661bb2638..c69f750e3 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -34,7 +34,7 @@ class SeqPtcPage : public SeqPage { int8_t poly_notes[MAX_POLY_NOTES]; uint64_t note_mask = 0; uint16_t deferred_timer = 0; - const uint8_t render_defer_time = 100; + const uint8_t render_defer_time = 50; SeqPtcMidiEvents midi_events; SeqPtcPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, From 369993165c82e92c007c74dc8d9eff838301277b Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 20:41:16 +0800 Subject: [PATCH 259/469] remove page index for real time pages --- avr/cores/megacommand/MCL/SeqPage.cpp | 4 +++- avr/cores/megacommand/MCL/SeqPage.h | 1 + avr/cores/megacommand/MCL/SeqPtcPage.cpp | 1 + avr/cores/megacommand/MCL/SeqRlckPage.cpp | 1 + avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 1 + 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 7cd1fe23c..5557c74e6 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -599,7 +599,9 @@ void SeqPage::display() { // draw stop/play/rec state mcl_gui.draw_panel_status(recording, MidiClock.state == 2); - draw_page_index(); + if (display_page_index) { + draw_page_index(); + } // draw info lines mcl_gui.draw_panel_labels(info1, info2); diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index f0d664581..300673294 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -29,6 +29,7 @@ class SeqPage : public LightPage { static bool show_track_menu; bool recording = false; + bool display_page_index = true; char info1[8] = { '\0' }; char info2[8] = { '\0' }; uint8_t timeout_values[4] = { 0 }; // 255 == highlight diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 8d1de7eec..fd793aeaa 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -133,6 +133,7 @@ void SeqPtcPage::config() { strncat(info1, buf, len1); strcpy(info2, "CHROMAT"); + display_page_index = false; } void ptc_pattern_len_handler(Encoder *enc) { diff --git a/avr/cores/megacommand/MCL/SeqRlckPage.cpp b/avr/cores/megacommand/MCL/SeqRlckPage.cpp index d0faae528..224f6128a 100644 --- a/avr/cores/megacommand/MCL/SeqRlckPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRlckPage.cpp @@ -18,6 +18,7 @@ void SeqRlckPage::config() { strncat(info1, buf, len1); strcpy(info2, "RLCK"); + display_page_index = false; } void SeqRlckPage::init() { diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 8cd5bda52..94a99f4d6 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -19,6 +19,7 @@ void SeqRtrkPage::config() { strncat(info1, buf, len1); strcpy(info2, "RTRK"); + display_page_index = false; } void SeqRtrkPage::init() { From 1fd7c0289f5098c7f0cbda856e5dc06b99cbd40f Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 9 Nov 2019 20:59:38 +0800 Subject: [PATCH 260/469] mixer: flt param display adjustments --- avr/cores/megacommand/MCL/MixerPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index 4a9bc2987..4876caa2f 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -306,7 +306,7 @@ void MixerPage::display() { oled_display.fillRect(fader_x, 13 + (FADER_LEN - fader_level), 6, fader_level, WHITE); oled_display.fillRect(fader_x + 1, 14 + (FADER_LEN - meter_level), 4, - meter_level - 1, BLACK); + meter_level - 2, BLACK); } fader_x += 8; From 264d4c09f66f19cd77ab93fbdad8cd512d4bfc80 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 10 Nov 2019 20:02:19 +1100 Subject: [PATCH 261/469] Remove uneccesary blocking delay when MD is not connected --- avr/cores/megacommand/MCL/GridPage.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 74d49138f..fec3a32f9 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -1,5 +1,5 @@ -#include "GridPage.h" #include "GUI.h" +#include "GridPage.h" #include "GridPages.h" #include "MCL.h" void GridPage::init() { @@ -95,8 +95,8 @@ void GridPage::loop() { } encoders[2]->cur = 1; encoders[3]->cur = 1; - ((MCLEncoder*)encoders[2])->max = GRID_WIDTH - getCol(); - ((MCLEncoder*)encoders[3])->max = GRID_LENGTH - getRow(); + ((MCLEncoder *)encoders[2])->max = GRID_WIDTH - getCol(); + ((MCLEncoder *)encoders[3])->max = GRID_LENGTH - getRow(); #else cur_col = encoders[0]->cur; @@ -401,7 +401,6 @@ void GridPage::display_grid() { if (cur_col + encoders[2]->cur > MAX_VISIBLE_COLS - 1) { col_shift = cur_col + encoders[2]->cur - MAX_VISIBLE_COLS; - } if (cur_row + encoders[3]->cur > MAX_VISIBLE_ROWS - 1) { @@ -586,14 +585,16 @@ void GridPage::display() { } void GridPage::prepare() { - MD.getCurrentTrack(CALLBACK_TIMEOUT); - MD.currentKit = MD.getCurrentKit(CALLBACK_TIMEOUT); - if ((mcl_cfg.auto_save == 1)) { - MD.saveCurrentKit(MD.currentKit); - MD.getBlockingKit(MD.currentKit); - if (MidiClock.state == 2) { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].update_kit_params(); + if (MD.connected) { + MD.getCurrentTrack(CALLBACK_TIMEOUT); + MD.currentKit = MD.getCurrentKit(CALLBACK_TIMEOUT); + if ((mcl_cfg.auto_save == 1)) { + MD.saveCurrentKit(MD.currentKit); + MD.getBlockingKit(MD.currentKit); + if (MidiClock.state == 2) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].update_kit_params(); + } } } } From bafc4990ffc3f9f2da36807632b282a3b918b99d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 10 Nov 2019 20:04:06 +1100 Subject: [PATCH 262/469] add screen wipe to splash screen --- avr/cores/megacommand/MCL/MCLGfx.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index 3605a7097..14dd954e8 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -67,8 +67,14 @@ void MCLGfx::splashscreen() { } oled_display.clearDisplay(); */ oled_display.display(); + delay(750); + + for (uint8_t a = 0; a < 32; a++) { + oled_display.drawLine(35, a, BITMAP_MCL_LOGO_W + 35 + 30, a, BLACK); + oled_display.drawLine(35, 32 - a, BITMAP_MCL_LOGO_W + 35 + 30, 32 - a, BLACK); + oled_display.display(); + } - delay(800); #else DEBUG_PRINTLN("HD44780 enabled"); From f1d90906d0994d5976e4f0cdedbea6775cef6c8f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 10 Nov 2019 20:07:44 +1100 Subject: [PATCH 263/469] Add speed control for draw progress bar (currently set to default) --- avr/cores/megacommand/MCL/MCLGUI.cpp | 6 ++++-- avr/cores/megacommand/MCL/MCLGUI.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 6f6ed8fec..51c9189c6 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -158,9 +158,11 @@ void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, } temp_bitmask = (temp_bitmask >> shift) | (temp_bitmask << (8 - shift)); } - + if (s_progress_count == s_progress_speed) { s_progress_cookie = (bitmask >> shift) | (bitmask << (8 - shift)); - + s_progress_count = 0; + } + s_progress_count++; oled_display.drawRect(x_pos, y_pos, s_progress_w, s_progress_h, WHITE); if (!deferred_display) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index c95eb7106..f89791a6e 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -11,6 +11,7 @@ class MCLGUI { public: uint8_t s_progress_cookie = 0b00110011; + uint8_t s_progress_count = 0; // fills dst buffer with input text. ensures that: // 1. dst is null-terminated // 2. dst has no trailing spaces @@ -136,6 +137,8 @@ class MCLGUI { static constexpr uint8_t s_progress_y = 16; static constexpr uint8_t s_progress_w = 64; static constexpr uint8_t s_progress_h = 5; + + static constexpr uint8_t s_progress_speed = 1; }; extern MCLGUI mcl_gui; From aefdb906d9842cd5b5078959b19c517b5c27f198 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 10 Nov 2019 20:59:01 +1100 Subject: [PATCH 264/469] Allow locks on track 16 --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 80721e4b8..2b4ebf01e 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -237,10 +237,8 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { // GUI.setPage(&seq_extstep_page); return true; } - // track 16 should not have locks - if (last_md_track < 15) { - show_pitch = true; - } + show_pitch = true; + if (step >= active_track.length) { return true; } From 5de5546201b78f8fed92fe82f4e563e8cc74dac1 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 12 Nov 2019 21:19:29 +1100 Subject: [PATCH 265/469] Decrese progress bar scroll speed --- avr/cores/megacommand/MCL/MCLGUI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index f89791a6e..1fb1b581e 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -138,7 +138,7 @@ class MCLGUI { static constexpr uint8_t s_progress_w = 64; static constexpr uint8_t s_progress_h = 5; - static constexpr uint8_t s_progress_speed = 1; + static constexpr uint8_t s_progress_speed = 2; }; extern MCLGUI mcl_gui; From c1146da97c3539b99c918197f40cab07fd56c7f7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 12 Nov 2019 21:55:09 +1100 Subject: [PATCH 266/469] Proposed layout for PageSelect --- avr/cores/megacommand/MCL/GridPage.cpp | 2 ++ avr/cores/megacommand/MCL/MixerPage.cpp | 8 ++--- avr/cores/megacommand/MCL/PageSelectPage.cpp | 32 ++++++++++---------- avr/cores/megacommand/MCL/RoutePage.cpp | 3 +- avr/cores/megacommand/MCL/SeqPage.cpp | 15 +++++++-- avr/cores/megacommand/MCL/SeqStepPage.cpp | 7 +++-- 6 files changed, 40 insertions(+), 27 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index fec3a32f9..4d5219ad1 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -825,6 +825,7 @@ bool GridPage::handleEvent(gui_event_t *event) { md_exploit.ignore_last_track_once = true; } +/* if (EVENT_PRESSED(event, Buttons.ENCODER1)) { seq_step_page.isSetup = false; prepare(); @@ -853,6 +854,7 @@ bool GridPage::handleEvent(gui_event_t *event) { return true; } +*/ if ((EVENT_PRESSED(event, Buttons.BUTTON1) && BUTTON_DOWN(Buttons.BUTTON4)) || (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON1))) { diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index 4876caa2f..a9f471940 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -414,10 +414,10 @@ bool MixerPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { - if (note_interface.notes_count() == 0) { - route_page.update_globals(); - GUI.setPage(&grid_page); - } +// if (note_interface.notes_count() == 0) { +// route_page.update_globals(); +// GUI.setPage(&grid_page); +// } return true; } diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 9c2765163..47cf8e8f1 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -20,28 +20,28 @@ const PageCategory Categories[] PROGMEM = { {"FX", 2, 10}, {"RAM", 2, 12}, {"LFO", 1, 14}, {"CONFIG", 1, 15}, }; -const PageSelectEntry Entries[] PROGMEM = { - {"GRID", &grid_page, 0xFF, 0}, +const PageSelectEntry Entries[] PROGMEM = { + {"GRID", &grid_page, 0, 0}, + {"MIXER", &mixer_page, 1, 2}, + {"ROUTE", &route_page, 2, 2}, + {"LFO", &lfo_page, 3, 6}, - {"STEPS", &seq_step_page, 0, 1}, - {"RECORD", &seq_rtrk_page, 1, 1}, - {"LOCKS", &seq_param_page[0], 2, 1}, - {"CHROMA", &seq_ptc_page, 3, 1}, + {"STEP EDIT", &seq_step_page, 4, 1}, + {"RECORD", &seq_rtrk_page, 5, 1}, + {"LOCKS", &seq_param_page[0], 6, 1}, + {"CHROMA", &seq_ptc_page, 7, 1}, - {"MIXER", &mixer_page, 4, 2}, - {"ROUTE", &route_page, 5, 2}, - {"LOUDNESS", &loudness_page, 6, 2}, - {"WAV DESIGNER", &wd.pages[0], 7, 3}, - {"SOUND MANAGER", &sound_browser, 8, 3}, + {"WAV DESIGNER", &wd.pages[0], 8, 3}, + {"SOUND MANAGER", &sound_browser, 7, 3}, + {"LOUDNESS", &loudness_page, 9, 2}, - {"DELAY", &fx_page_a, 9, 4}, - {"REVERB", &fx_page_b, 10, 4}, + {"DELAY", &fx_page_a, 12, 4}, + {"REVERB", &fx_page_b, 13, 4}, - {"RAM-1", &ram_page_a, 11, 5}, - {"RAM-2", &ram_page_b, 12, 5}, + {"RAM-1", &ram_page_a, 14, 5}, + {"RAM-2", &ram_page_b, 15, 5}, - {"LFO", &lfo_page, 13, 6}, {"CONFIG", &system_page, 0xFF, 7}, }; diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index 8771f2a0b..87dda5ed7 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -255,7 +255,7 @@ bool RoutePage::handleEvent(gui_event_t *event) { GUI.setPage(&page_select_page); return true; } - +/* if (EVENT_PRESSED(event, Buttons.ENCODER1) || EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || @@ -265,5 +265,6 @@ bool RoutePage::handleEvent(gui_event_t *event) { return true; } +*/ return false; } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 5557c74e6..43f8c1464 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -123,6 +123,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { } // end TI events // if no TI button pressed, enable page switching. +/* if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { if (EVENT_PRESSED(event, Buttons.ENCODER1)) { GUI.setPage(&seq_step_page); @@ -143,9 +144,9 @@ bool SeqPage::handleEvent(gui_event_t *event) { return false; } } - +*/ // A not-ignored BUTTON2 release event triggers sequence page select - if (EVENT_RELEASED(event, Buttons.BUTTON2)) { + if (EVENT_RELEASED(event, Buttons.BUTTON3)) { if (ignore_button_release != 2) { ignore_button_release = 255; page_select += 1; @@ -157,6 +158,14 @@ bool SeqPage::handleEvent(gui_event_t *event) { return false; } + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + route_page.update_globals(); + md_exploit.off(); + md_exploit.on(); + GUI.setPage(&page_select_page); + } + + /* // SHIFT2 changes ENC4 to track select if (EVENT_PRESSED(event, Buttons.BUTTON3)) { encoders[3] = &trackselect_enc; @@ -171,7 +180,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { } else if (EVENT_RELEASED(event, Buttons.BUTTON3)) { encoders[3] = &seq_param4; } - +*/ /* #ifdef OLED_DISPLAY diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 80721e4b8..7f46e9c31 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -339,9 +339,9 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { } // end TI events if (EVENT_PRESSED(event, Buttons.ENCODER1)) { - if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { - GUI.setPage(&grid_page); - } +// if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { +// GUI.setPage(&grid_page); +// } return true; } @@ -362,6 +362,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { return true; } #endif + return false; } From a4c1ae58ee19d3abf3ade70d5beb7f217422395e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 12 Nov 2019 22:07:04 +1100 Subject: [PATCH 267/469] Disable encoder button exit on seq pages --- avr/cores/megacommand/MCL/SeqParamPage.cpp | 3 ++- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 4 ++-- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 9a5d5e5db..b46ada37e 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -245,12 +245,13 @@ if (utiming == 0) { } return true; } - if (EVENT_PRESSED(event, Buttons.ENCODER3)) { +/* if (EVENT_PRESSED(event, Buttons.ENCODER3)) { if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { GUI.setPage(&grid_page); } return true; } +*/ if (EVENT_PRESSED(event, Buttons.BUTTON4)) { mcl_seq.md_tracks[last_md_track].clear_locks(); return true; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index fd793aeaa..bb505b965 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -461,12 +461,12 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { recording = !recording; return true; } - +/* if (EVENT_PRESSED(event, Buttons.ENCODER4)) { GUI.setPage(&grid_page); return true; } - +*/ if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { if (midi_device == DEVICE_MD) { diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 94a99f4d6..e1d96b156 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -133,11 +133,11 @@ bool SeqRtrkPage::handleEvent(gui_event_t *event) { return true; } - if (EVENT_PRESSED(event, Buttons.ENCODER2)) { - md_exploit.off(); - GUI.setPage(&grid_page); - return true; - } +// if (EVENT_PRESSED(event, Buttons.ENCODER2)) { +// md_exploit.off(); +// GUI.setPage(&grid_page); +// return true; +// } if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { From 6043a8e8a6e1a132ddc2f6b26afbe24630b0eb97 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 12 Nov 2019 22:08:10 +1100 Subject: [PATCH 268/469] fix numbering of pages --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 47cf8e8f1..94b447d6a 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -32,9 +32,9 @@ const PageSelectEntry Entries[] PROGMEM = { {"CHROMA", &seq_ptc_page, 7, 1}, - {"WAV DESIGNER", &wd.pages[0], 8, 3}, - {"SOUND MANAGER", &sound_browser, 7, 3}, - {"LOUDNESS", &loudness_page, 9, 2}, + {"SOUND MANAGER", &sound_browser, 8, 3}, + {"WAV DESIGNER", &wd.pages[0], 9, 3}, + {"LOUDNESS", &loudness_page, 10, 2}, {"DELAY", &fx_page_a, 12, 4}, {"REVERB", &fx_page_b, 13, 4}, From f4fbbba8fb03b0bffe1e6ef57dd611a75e5bd5e4 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 12 Nov 2019 22:13:41 +1100 Subject: [PATCH 269/469] Disable track select, track length can be set with shift2 --- avr/cores/megacommand/MCL/SeqPage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 43f8c1464..346540ce0 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -81,7 +81,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { // TI + SHIFT1: adjust track seq length. // Ignore SHIFT1 release event so it won't trigger // a seq page select action. - if (BUTTON_DOWN(Buttons.BUTTON2)) { + if (BUTTON_DOWN(Buttons.BUTTON3)) { // calculate the intended seq length. uint8_t step = track; step += 1 + page_select * 16; @@ -113,10 +113,10 @@ bool SeqPage::handleEvent(gui_event_t *event) { } // TI + SHIFT2 = select track. - if (BUTTON_DOWN(Buttons.BUTTON3)) { + /* if (BUTTON_DOWN(Buttons.BUTTON3)) { select_track(device, track); return true; - } + }*/ // notify derived class about unhandled TI event return false; From 33e26e3e915b35e499b6673d47e7273d1ba2cb65 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 12 Nov 2019 22:20:34 +1100 Subject: [PATCH 270/469] Disable encoder exit on lfopage --- avr/cores/megacommand/MCL/LFOPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index fdee0f22d..62c594b85 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -390,12 +390,13 @@ bool LFOPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_RELEASED) { return true; } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || +/* if (EVENT_PRESSED(event, Buttons.ENCODER1) || EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { GUI.setPage(&grid_page); } +*/ if (EVENT_PRESSED(event, Buttons.BUTTON1)) { page_mode = !(page_mode); update_encoders(); From 7a858ad22e9641cc87fa4c61fbf23023237053f4 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Wed, 13 Nov 2019 01:45:46 +0800 Subject: [PATCH 271/469] PageSelectPage: PageCategory is now effective. --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 155 ++++++++++++------- avr/cores/megacommand/MCL/PageSelectPage.h | 3 +- 2 files changed, 104 insertions(+), 54 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 94b447d6a..15688fb11 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -3,7 +3,7 @@ #include struct PageCategory { - char Name[16]; + char Name[8]; uint8_t PageCount; uint8_t FirstPage; }; @@ -16,39 +16,84 @@ struct PageSelectEntry { }; const PageCategory Categories[] PROGMEM = { - {"GRID", 1, 0}, {"SEQ", 4, 1}, {"MIX", 3, 5}, {"SOUND", 2, 8}, - {"FX", 2, 10}, {"RAM", 2, 12}, {"LFO", 1, 14}, {"CONFIG", 1, 15}, + {"MAIN", 4, 0}, + {"SEQ", 4, 4}, + {"SND", 3, 8}, + {"AUX", 4, 12}, }; -const PageSelectEntry Entries[] PROGMEM = { +const PageSelectEntry Entries[] PROGMEM = { {"GRID", &grid_page, 0, 0}, - {"MIXER", &mixer_page, 1, 2}, - {"ROUTE", &route_page, 2, 2}, - {"LFO", &lfo_page, 3, 6}, + {"MIXER", &mixer_page, 1, 0}, + {"ROUTE", &route_page, 2, 0}, + {"LFO", &lfo_page, 3, 0}, {"STEP EDIT", &seq_step_page, 4, 1}, {"RECORD", &seq_rtrk_page, 5, 1}, {"LOCKS", &seq_param_page[0], 6, 1}, {"CHROMA", &seq_ptc_page, 7, 1}, - - {"SOUND MANAGER", &sound_browser, 8, 3}, - {"WAV DESIGNER", &wd.pages[0], 9, 3}, + {"SOUND MANAGER", &sound_browser, 8, 2}, + {"WAV DESIGNER", &wd.pages[0], 9, 2}, {"LOUDNESS", &loudness_page, 10, 2}, - {"DELAY", &fx_page_a, 12, 4}, - {"REVERB", &fx_page_b, 13, 4}, - - {"RAM-1", &ram_page_a, 14, 5}, - {"RAM-2", &ram_page_b, 15, 5}, - - - {"CONFIG", &system_page, 0xFF, 7}, + {"DELAY", &fx_page_a, 12, 3}, + {"REVERB", &fx_page_b, 13, 3}, + {"RAM-1", &ram_page_a, 14, 3}, + {"RAM-2", &ram_page_b, 15, 3}, }; constexpr uint8_t n_category = sizeof(Categories) / sizeof(PageCategory); constexpr uint8_t n_entry = sizeof(Entries) / sizeof(PageSelectEntry); +static uint8_t get_pageidx(uint8_t page_number) { + uint8_t i = 0; + for (; i < n_entry; ++i) { + if (page_number == pgm_read_byte(&Entries[i].PageNumber)) { + break; + } + } + return i; +} + +static LightPage *get_page(uint8_t page_number, char *str) { + uint8_t pageidx = get_pageidx(page_number); + if (pageidx < n_entry) { + if (str) { + m_strncpy_p(str, (PGM_P) & (Entries[pageidx].Name), 16); + } + return pgm_read_word(&Entries[pageidx].Page); + } else { + if (str) { + strncpy(str, "----", 5); + } + return NULL; + } +} + +static void get_category_name(uint8_t page_number, char *str) { + uint8_t pageidx, catidx; + + pageidx= get_pageidx(page_number); + if (pageidx >= n_entry) { + goto get_category_name_fail; + } + catidx = pgm_read_byte(&Entries[pageidx].CategoryId); + if (catidx >= n_category) { + goto get_category_name_fail; + } + if (str) { + m_strncpy_p(str, (PGM_P) & (Categories[catidx].Name), 16); + } + return; + +get_category_name_fail: + if (str) { + strncpy(str, "----", 5); + } + return; +} + void PageSelectPage::setup() {} void PageSelectPage::init() { #ifdef OLED_DISPLAY @@ -59,24 +104,9 @@ void PageSelectPage::init() { } void PageSelectPage::cleanup() { note_interface.init_notes(); } -LightPage *PageSelectPage::get_page(uint8_t page_number, char *str) { - for (uint8_t i = 0; i < n_entry; ++i) { - if (page_number == pgm_read_byte(&Entries[i].PageNumber)) { - if (str) { - m_strncpy_p(str, (PGM_P) & (Entries[i].Name), 16); - } - return pgm_read_word(&Entries[i].Page); - } - } - if (str) { - strncpy(str, "----", 5); - } - return NULL; -} - uint8_t PageSelectPage::get_nextpage_down() { for (int8_t i = page_select - 1; i >= 0; i--) { - if (get_page(i)) { + if (get_page(i, nullptr)) { return i; } } @@ -85,18 +115,28 @@ uint8_t PageSelectPage::get_nextpage_down() { uint8_t PageSelectPage::get_nextpage_up() { for (uint8_t i = page_select + 1; i < 16; i++) { - if (get_page(i)) { + if (get_page(i, nullptr)) { return i; } } return page_select; } +uint8_t PageSelectPage::get_category_page(uint8_t offset) { + auto page_id = get_pageidx(page_select); + auto cat_id = pgm_read_byte(&Entries[page_id].CategoryId); + auto cat_start = pgm_read_byte(&Categories[cat_id].FirstPage); + auto cat_size = pgm_read_byte(&Categories[cat_id].PageCount); + if (offset >= cat_size) { + return page_select; + } else { + return cat_start + offset; + } +} + void PageSelectPage::loop() { MCLEncoder *enc_ = &enc1; // largest_sine_peak = 1.0 / 16.00; - int dir = 0; - int16_t newval; int8_t diff = enc_->cur - enc_->old; if ((diff > 0) && (page_select < 16)) { page_select = get_nextpage_up(); @@ -115,10 +155,12 @@ void PageSelectPage::display() { #endif GUI.setLine(GUI.LINE1); char str[16]; - get_page(page_select, str); - LightPage *temp = NULL; GUI.put_string_at_fill(0, "Page Select:"); + get_category_name(page_select, str); + GUI.put_string_at(12, str); + GUI.setLine(GUI.LINE2); + get_page(page_select, str); GUI.put_string_at_fill(0, str); } @@ -129,23 +171,18 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { uint8_t device = midi_active_peering.get_device(port); uint8_t track = event->source - 128; - // note interface presses are treated as musical notes here - if (event->mask == EVENT_BUTTON_PRESSED) { + // note interface presses select corresponding page + if (mask == EVENT_BUTTON_PRESSED) { if (device != DEVICE_MD) { return false; } page_select = track; return true; } - if (event->mask == EVENT_BUTTON_RELEASED) { + if (mask == EVENT_BUTTON_RELEASED) { if (device != DEVICE_MD) { - return true; + return false; } - // LightPage *p; - // p = get_page(page_select); - // if (p) - // GUI.setPage(p); - return true; } @@ -153,7 +190,7 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON2)) { LightPage *p; - p = get_page(page_select); + p = get_page(page_select, nullptr); if (p) { GUI.setPage(p); } else { @@ -162,12 +199,24 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { } return true; } - if (EVENT_RELEASED(event, Buttons.ENCODER1) || - EVENT_RELEASED(event, Buttons.ENCODER2) || - EVENT_RELEASED(event, Buttons.ENCODER3) || - EVENT_RELEASED(event, Buttons.ENCODER1)) { + + if (EVENT_RELEASED(event, Buttons.ENCODER1)) { + page_select = get_category_page(0); + return true; + } + if (EVENT_RELEASED(event, Buttons.ENCODER2)) { + page_select = get_category_page(1); + return true; + } + if (EVENT_RELEASED(event, Buttons.ENCODER3)) { + page_select = get_category_page(2); return true; } + if (EVENT_RELEASED(event, Buttons.ENCODER4)) { + page_select = get_category_page(3); + return true; + } + return false; } diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index aba5cc90f..98993db8d 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -22,9 +22,10 @@ class PageSelectPage : public LightPage { virtual void cleanup(); virtual bool handleEvent(gui_event_t *event); - LightPage *get_page(uint8_t page_number, char *str = NULL); uint8_t get_nextpage_down(); uint8_t get_nextpage_up(); + // get a page in the current category. + uint8_t get_category_page(uint8_t offset); }; extern PageSelectPage page_select_page; From 5a64230cc0d12980f5ceab4a75c149c946eb7759 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Wed, 13 Nov 2019 02:06:43 +0800 Subject: [PATCH 272/469] PageSelectPage: use ENC2 to jump between categories --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 33 +++++++++++++++++++- avr/cores/megacommand/MCL/PageSelectPage.h | 8 +++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 15688fb11..2366fd08a 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -122,6 +122,26 @@ uint8_t PageSelectPage::get_nextpage_up() { return page_select; } +uint8_t PageSelectPage::get_nextpage_catdown() { + auto page_id = get_pageidx(page_select); + auto cat_id = pgm_read_byte(&Entries[page_id].CategoryId); + if (cat_id > 0) { + return pgm_read_byte(&Categories[cat_id - 1].FirstPage); + } else { + return page_select; + } +} + +uint8_t PageSelectPage::get_nextpage_catup() { + auto page_id = get_pageidx(page_select); + auto cat_id = pgm_read_byte(&Entries[page_id].CategoryId); + if (cat_id < n_category - 1) { + return pgm_read_byte(&Categories[cat_id + 1].FirstPage); + } else { + return page_select; + } +} + uint8_t PageSelectPage::get_category_page(uint8_t offset) { auto page_id = get_pageidx(page_select); auto cat_id = pgm_read_byte(&Entries[page_id].CategoryId); @@ -136,7 +156,6 @@ uint8_t PageSelectPage::get_category_page(uint8_t offset) { void PageSelectPage::loop() { MCLEncoder *enc_ = &enc1; - // largest_sine_peak = 1.0 / 16.00; int8_t diff = enc_->cur - enc_->old; if ((diff > 0) && (page_select < 16)) { page_select = get_nextpage_up(); @@ -147,6 +166,18 @@ void PageSelectPage::loop() { enc_->cur = 64 + diff; enc_->old = 64; + + enc_ = &enc2; + diff = enc_->cur - enc_->old; + if ((diff > 0) && (page_select < 16)) { + page_select = get_nextpage_catup(); + } + if ((diff < 0) && (page_select > 0)) { + page_select = get_nextpage_catdown(); + } + + enc_->cur = 64 + diff; + enc_->old = 64; } void PageSelectPage::display() { diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index 98993db8d..001f47e18 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -8,11 +8,13 @@ class PageSelectPage : public LightPage { public: MCLEncoder enc1; + MCLEncoder enc2; uint8_t page_select; PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { encoders[0] = &enc1; + encoders[1] = &enc2; } virtual void display(); @@ -22,10 +24,12 @@ class PageSelectPage : public LightPage { virtual void cleanup(); virtual bool handleEvent(gui_event_t *event); - uint8_t get_nextpage_down(); - uint8_t get_nextpage_up(); // get a page in the current category. uint8_t get_category_page(uint8_t offset); + uint8_t get_nextpage_down(); + uint8_t get_nextpage_up(); + uint8_t get_nextpage_catup(); + uint8_t get_nextpage_catdown(); }; extern PageSelectPage page_select_page; From b6de6411407a34f4bae38ea43b457d0946e94669 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 13 Nov 2019 19:59:54 +1100 Subject: [PATCH 273/469] Tentative fix for LFO page switching messing with params --- avr/cores/megacommand/MCL/FXPage.cpp | 14 ++++++++++++++ avr/cores/megacommand/MCL/LFOPage.cpp | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index a5679e3b3..49a10cf95 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -74,6 +74,20 @@ void FXPage::loop() { } #endif MD.sendFXParam(fx_param, encoders[i]->cur, fx_type); + switch(fx_type) { + case MD_FX_ECHO: + MD.kit.delay[fx_param] = encoders[i]->cur; + break; + case MD_FX_DYN: + MD.kit.dynamics[fx_param] = encoders[i]->cur; + break; + case MD_FX_REV: + MD.kit.reverb[fx_param] = encoders[i]->cur; + break; + case MD_FX_EQ: + MD.kit.eq[fx_param] = encoders[i]->cur; + break; + } } } } diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index fdee0f22d..b8569dbf8 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -48,6 +48,19 @@ void LFOPage::update_encoders() { ((MCLEncoder *)encoders[2])->max = NUM_MD_TRACKS + 4; encoders[3]->cur = lfo_track->params[1].param; ((MCLEncoder *)encoders[3])->max = 23; + + if (encoders[0]->cur > NUM_MD_TRACKS) { + ((MCLEncoder *)encoders[1])->max = 7; + } else { + ((MCLEncoder *)encoders[1])->max = 23; + } + + if (encoders[2]->cur > NUM_MD_TRACKS) { + ((MCLEncoder *)encoders[3])->max = 7; + } else { + ((MCLEncoder *)encoders[3])->max = 23; + } + } if (page_mode == LFO_SETTINGS) { encoders[0]->cur = waveform; @@ -62,7 +75,7 @@ void LFOPage::update_encoders() { encoders[3]->cur = lfo_track->params[1].depth; ((MCLEncoder *)encoders[3])->max = 127; } - loop(); +// loop(); for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { encoders[i]->old = encoders[i]->cur; From 89608b7fe7ef5b4c10623c1a340293cc92f99415 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 13 Nov 2019 20:53:20 +1100 Subject: [PATCH 274/469] Port display from classic to oled. Update button mapping. Encoder declaration change --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 43 +++++++++++++++----- avr/cores/megacommand/MCL/PageSelectPage.h | 9 ++-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 2366fd08a..f8a71343a 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -101,6 +101,7 @@ void PageSelectPage::init() { #endif md_exploit.on(); note_interface.state = true; + classic_display = false; } void PageSelectPage::cleanup() { note_interface.init_notes(); } @@ -155,7 +156,7 @@ uint8_t PageSelectPage::get_category_page(uint8_t offset) { } void PageSelectPage::loop() { - MCLEncoder *enc_ = &enc1; + MCLEncoder *enc_ = encoders[0]; int8_t diff = enc_->cur - enc_->old; if ((diff > 0) && (page_select < 16)) { page_select = get_nextpage_up(); @@ -167,7 +168,7 @@ void PageSelectPage::loop() { enc_->cur = 64 + diff; enc_->old = 64; - enc_ = &enc2; + enc_ = encoders[1]; diff = enc_->cur - enc_->old; if ((diff > 0) && (page_select < 16)) { page_select = get_nextpage_catup(); @@ -183,7 +184,18 @@ void PageSelectPage::loop() { void PageSelectPage::display() { #ifdef OLED_DISPLAY oled_display.clearDisplay(); -#endif + char str[16]; + oled_display.setCursor(0,0); + oled_display.print("Page Select:"); + get_category_name(page_select, str); + oled_display.print(str); + + oled_display.setCursor(0,15); + get_page(page_select, str); + oled_display.print(str); + oled_display.display(); + +#else GUI.setLine(GUI.LINE1); char str[16]; GUI.put_string_at_fill(0, "Page Select:"); @@ -193,6 +205,7 @@ void PageSelectPage::display() { GUI.setLine(GUI.LINE2); get_page(page_select, str); GUI.put_string_at_fill(0, str); +#endif } bool PageSelectPage::handleEvent(gui_event_t *event) { @@ -222,28 +235,34 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { if (EVENT_RELEASED(event, Buttons.BUTTON2)) { LightPage *p; p = get_page(page_select, nullptr); - if (p) { - GUI.setPage(p); - } else { + if (BUTTON_DOWN(Buttons.BUTTON1) || (!p)) { md_exploit.off(); GUI.setPage(&grid_page); + } else { + GUI.setPage(p); } return true; } - if (EVENT_RELEASED(event, Buttons.ENCODER1)) { + if (EVENT_RELEASED(event, Buttons.BUTTON1)) { + md_exploit.off(); + GUI.setPage(&grid_page); + return true; + } + + if (EVENT_PRESSED(event, Buttons.ENCODER1)) { page_select = get_category_page(0); return true; } - if (EVENT_RELEASED(event, Buttons.ENCODER2)) { + if (EVENT_PRESSED(event, Buttons.ENCODER2)) { page_select = get_category_page(1); return true; } - if (EVENT_RELEASED(event, Buttons.ENCODER3)) { + if (EVENT_PRESSED(event, Buttons.ENCODER3)) { page_select = get_category_page(2); return true; } - if (EVENT_RELEASED(event, Buttons.ENCODER4)) { + if (EVENT_PRESSED(event, Buttons.ENCODER4)) { page_select = get_category_page(3); return true; } @@ -251,4 +270,6 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { return false; } -PageSelectPage page_select_page; +MCLEncoder page_select_param1(0,127); +MCLEncoder page_select_param2(0,127); +PageSelectPage page_select_page(&page_select_param1, &page_select_param2); diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index 001f47e18..56a45d5be 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -5,16 +5,17 @@ #include "GUI.h" +extern MCLEncoder page_select_param1; +extern MCLEncoder page_select_param2; + class PageSelectPage : public LightPage { public: - MCLEncoder enc1; - MCLEncoder enc2; uint8_t page_select; PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { - encoders[0] = &enc1; - encoders[1] = &enc2; + encoders[0] = e1; + encoders[1] = e2; } virtual void display(); From c489d15c9de092734dcd59e760aa423a1910cf31 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 14 Nov 2019 12:22:12 +1100 Subject: [PATCH 275/469] Fix overflow by limiting fillRect width --- .../megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index 2ef717b25..9793af2c1 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -184,7 +184,6 @@ void Adafruit_SSD1305::drawFastVLine(int16_t x, int16_t y, int16_t h, if (y + h > SSD1305_LCDHEIGHT) { h = SSD1305_LCDHEIGHT - y; } - // check rotation, move pixel around if necessary* // MegaCommand rotation y = SSD1305_LCDHEIGHT - y - h; @@ -259,6 +258,9 @@ void Adafruit_SSD1305::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, h = SSD1305_LCDHEIGHT - y; } + if (x + w > SSD1305_LCDWIDTH) { + w = SSD1305_LCDHEIGHT - x; + } // check rotation, move pixel around if necessary* // MegaCommand rotation y = SSD1305_LCDHEIGHT - y - h; From 3042952c621bbd9f5c85c6623be4e1ba82b5498f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 14 Nov 2019 14:54:23 +1100 Subject: [PATCH 276/469] Change button layout for WavDesigner --- avr/cores/megacommand/MCL/OscMixerPage.cpp | 60 ++++++++------------- avr/cores/megacommand/MCL/OscPage.cpp | 63 ++++++---------------- avr/cores/megacommand/MCL/OscPage.h | 2 +- avr/cores/megacommand/MCL/WavDesigner.h | 15 ++++++ 4 files changed, 55 insertions(+), 85 deletions(-) diff --git a/avr/cores/megacommand/MCL/OscMixerPage.cpp b/avr/cores/megacommand/MCL/OscMixerPage.cpp index 4c92eef89..622899cc3 100644 --- a/avr/cores/megacommand/MCL/OscMixerPage.cpp +++ b/avr/cores/megacommand/MCL/OscMixerPage.cpp @@ -1,9 +1,9 @@ +#include "DSP.h" #include "MCL.h" -#include "OscPage.h" +#include "OSC.h" #include "OscMixerPage.h" +#include "OscPage.h" #include "WavDesigner.h" -#include "DSP.h" -#include "OSC.h" void OscMixerPage::setup() {} @@ -20,60 +20,44 @@ void OscMixerPage::cleanup() {} bool OscMixerPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON4)) { GUI.setLine(GUI.LINE1); - GUI.put_string_at(0,"Render.."); + GUI.put_string_at(0, "Render.."); LCD.goLine(0); LCD.puts(GUI.lines[0].data); - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY oled_display.display(); - #endif +#endif wd.render(); - GUI.put_string_at(0,"Sending.."); + GUI.put_string_at(0, "Sending.."); LCD.goLine(0); LCD.puts(GUI.lines[0].data); - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY oled_display.display(); - #endif - // if (MD.connected) { - // MD.rec_sample(); +#endif + // if (MD.connected) { + // MD.rec_sample(); //} - // delay(250); - //in_sysex = 1; + // delay(250); + // in_sysex = 1; wd.send(); - //in_sysex = 0; - // delay(100); - // MD.press_no_button(); - // MD.clear_all_windows_quick(); + // in_sysex = 0; + // delay(100); + // MD.press_no_button(); + // MD.clear_all_windows_quick(); return true; } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { - MD.preview_sample(encoders[3]->cur + 1); - return true; - } - if (EVENT_PRESSED(event, Buttons.BUTTON2)) { - GUI.setPage(&page_select_page); + wd.load_next_page(id); return true; } - if (EVENT_PRESSED(event, Buttons.ENCODER1)) { - GUI.setPage(&(wd.pages[0])); - - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER2)) { - GUI.setPage(&(wd.pages[1])); - - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER3)) { - GUI.setPage(&(wd.pages[2])); - + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + MD.preview_sample(encoders[3]->cur + 1); return true; } - if (EVENT_PRESSED(event, Buttons.ENCODER4)) { - GUI.setPage(&grid_page); + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + GUI.setPage(&page_select_page); return true; } - return false; } diff --git a/avr/cores/megacommand/MCL/OscPage.cpp b/avr/cores/megacommand/MCL/OscPage.cpp index a30c967fc..f82148dae 100644 --- a/avr/cores/megacommand/MCL/OscPage.cpp +++ b/avr/cores/megacommand/MCL/OscPage.cpp @@ -1,6 +1,6 @@ #include "MCL.h" -#include "OscPage.h" #include "Osc.h" +#include "OscPage.h" #include "WavDesigner.h" void osc_mod_handler(Encoder *enc) {} @@ -38,10 +38,7 @@ void OscPage::init() { void OscPage::cleanup() { DEBUG_PRINT_FN(); } bool OscPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON1)) { - osc_waveform++; - if (osc_waveform > 5) { - osc_waveform = 0; - } + wd.load_next_page(id); return true; } @@ -49,43 +46,11 @@ bool OscPage::handleEvent(gui_event_t *event) { GUI.setPage(&page_select_page); return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON4)) { - // md_exploit.off(); - // wd.render(); - // wd.send(); - // exploit_delay_clock = slowclock; - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER1)) { - if (id == 0) { - GUI.setPage(&grid_page); - } else { - GUI.setPage(&(wd.pages[0])); - } - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER2)) { - if (id == 1) { - GUI.setPage(&grid_page); - } else { - GUI.setPage(&(wd.pages[1])); - } - - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER3)) { - if (id == 2) { - GUI.setPage(&grid_page); - } else { - GUI.setPage(&(wd.pages[2])); - } - - return true; - } - if (EVENT_PRESSED(event, Buttons.ENCODER4)) { - if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { - GUI.setPage(&(wd.mixer)); + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + osc_waveform++; + if (osc_waveform > 5) { + osc_waveform = 0; } return true; } @@ -103,6 +68,13 @@ void OscPage::calc_largest_sine_peak() { void OscPage::loop() { MCLEncoder *enc_ = &enc4; // largest_sine_peak = 1.0 / 16.00; + if (encoders[0]->hasChanged()) { + show_freq = false; + } + if (encoders[1]->hasChanged()) { + show_freq = true; + } + calc_largest_sine_peak(); int dir = 0; int16_t newval; @@ -151,11 +123,10 @@ void OscPage::loop() { void OscPage::display() { // oled_display.clearDisplay(); if (!classic_display) { - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY oled_display.fillRect(0, 0, 64, 32, BLACK); - #endif - } - else { +#endif + } else { GUI.setLine(GUI.LINE2); GUI.put_string_at(0, " "); } @@ -208,7 +179,7 @@ void OscPage::display() { } GUI.setLine(GUI.LINE1); - if (BUTTON_DOWN(Buttons.BUTTON3)) { + if (show_freq) { float freq = get_freq(); float upper = floor(freq / 1000); float lower = floor(freq - (upper * 1000)); diff --git a/avr/cores/megacommand/MCL/OscPage.h b/avr/cores/megacommand/MCL/OscPage.h index 1883881ab..e4d4ed956 100644 --- a/avr/cores/megacommand/MCL/OscPage.h +++ b/avr/cores/megacommand/MCL/OscPage.h @@ -16,12 +16,12 @@ void osc_mod_handler(Encoder *enc); class OscPage : public LightPage { public: - // Static variables shared amongst derived objects uint8_t id; MCLEncoder enc1; MCLEncoder enc2; MCLEncoder enc3; MCLEncoder enc4; + bool show_freq = false; uint8_t osc_waveform; uint8_t sample_number = 0; diff --git a/avr/cores/megacommand/MCL/WavDesigner.h b/avr/cores/megacommand/MCL/WavDesigner.h index ee25681d6..471256bb3 100644 --- a/avr/cores/megacommand/MCL/WavDesigner.h +++ b/avr/cores/megacommand/MCL/WavDesigner.h @@ -8,6 +8,8 @@ #include "OscPage.h" #include "OscMixerPage.h" +#define MIXER_ID 4 +#define NUM_OSC 3 class WavDesigner { public: OscPage pages[3]; @@ -19,10 +21,23 @@ class WavDesigner { for (uint8_t i = 0; i < 3; i++) { pages[i].id = i; } + mixer.id = MIXER_ID; pages[0].osc_waveform = 1; mixer.enc4.cur = 0; last_page = &(pages[0]); } + void load_next_page(uint8_t id) { + if (id < NUM_OSC - 1) { + GUI.setPage(&pages[id + 1]); + } + else if (id == MIXER_ID) { + GUI.setPage(&pages[0]); + } + else { + GUI.setPage(&mixer); + } + + } bool render(); bool send(); }; From 07546d87ee92533951b6b3bc6eeddd3a0254e660 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 14 Nov 2019 16:14:37 +1100 Subject: [PATCH 277/469] Fix button layout for loudness page --- avr/cores/megacommand/MCL/LoudnessPage.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/MCL/LoudnessPage.cpp b/avr/cores/megacommand/MCL/LoudnessPage.cpp index 62b3fbdb1..a9ee06da0 100644 --- a/avr/cores/megacommand/MCL/LoudnessPage.cpp +++ b/avr/cores/megacommand/MCL/LoudnessPage.cpp @@ -309,24 +309,19 @@ bool LoudnessPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_RELEASED) { return true; } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { scale_vol((float)encoders[0]->cur / (float)100); encoders[0]->cur = 100; return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON4)) { - float inc = check_loudness(); - encoders[0]->cur = inc * 100; - } +// if (EVENT_PRESSED(event, Buttons.BUTTON4)) { +// float inc = check_loudness(); + // encoders[0]->cur = inc * 100; +// } - if (EVENT_PRESSED(event, Buttons.BUTTON1) || - EVENT_PRESSED(event, Buttons.BUTTON2) || - EVENT_PRESSED(event, Buttons.BUTTON3)) { - GUI.setPage(&grid_page); + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + GUI.setPage(&page_select_page); return true; } From 0157a293d4bdcae995b2120c8b605f4fd5db39a9 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 14 Nov 2019 13:15:27 +0800 Subject: [PATCH 278/469] pageselect page new layout --- avr/cores/megacommand/MCL/MCLGUI.cpp | 88 ++++++++++- avr/cores/megacommand/MCL/MCLGUI.h | 20 +++ avr/cores/megacommand/MCL/PageSelectPage.cpp | 155 ++++++++++++++----- avr/cores/megacommand/MCL/RAMPage.h | 22 --- design/design_pageselect.xml | 5 + design/design_pageselect_2.xml | 5 + design/page_select.png | Bin 0 -> 536 bytes design/page_select_2.png | Bin 0 -> 518 bytes design/popup_menu.png | Bin 242 -> 209 bytes design/popup_menu.xml | 5 + 10 files changed, 230 insertions(+), 70 deletions(-) create mode 100644 design/design_pageselect.xml create mode 100644 design/design_pageselect_2.xml create mode 100644 design/page_select.png create mode 100644 design/page_select_2.png create mode 100644 design/popup_menu.xml diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 51c9189c6..5d3134d1e 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -159,8 +159,8 @@ void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, temp_bitmask = (temp_bitmask >> shift) | (temp_bitmask << (8 - shift)); } if (s_progress_count == s_progress_speed) { - s_progress_cookie = (bitmask >> shift) | (bitmask << (8 - shift)); - s_progress_count = 0; + s_progress_cookie = (bitmask >> shift) | (bitmask << (8 - shift)); + s_progress_count = 0; } s_progress_count++; oled_display.drawRect(x_pos, y_pos, s_progress_w, s_progress_h, WHITE); @@ -660,6 +660,80 @@ const unsigned char encoder_small_6[] PROGMEM = { 0x0e, 0x00, 0x31, 0x80, 0x40, 0x40, 0x40, 0x40, 0xb0, 0x20, 0xb0, 0x20, 0xb0, 0x20, 0x40, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; +// 'wheel1', 19x19px +const unsigned char wheel_top [] PROGMEM = { + 0x03, 0xf8, 0x00, 0x0e, 0x0e, 0x00, 0x1e, 0x0f, 0x00, 0x3e, 0x0f, 0x80, 0x7f, 0x1f, 0xc0, 0x7f, + 0x1f, 0xc0, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0xff, 0x5f, 0xe0, 0xff, 0xbf, + 0xe0, 0xf8, 0xe3, 0xe0, 0x60, 0xe0, 0xe0, 0x40, 0xe0, 0x40, 0x61, 0xf0, 0xc0, 0x31, 0xf1, 0x80, + 0x1b, 0xf7, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 +}; +// 'wheel2', 19x19px +const unsigned char wheel_angle [] PROGMEM = { + 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1f, 0xfb, 0x00, 0x3f, 0xf1, 0x80, 0x7f, 0xf0, 0xc0, 0x7f, + 0xe0, 0x40, 0xff, 0xe0, 0xe0, 0x8f, 0xe3, 0xe0, 0x83, 0xbf, 0xe0, 0x81, 0x5f, 0xe0, 0x83, 0xbf, + 0xe0, 0x8f, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0x7f, 0x1f, 0xc0, 0x7f, 0x1f, 0xc0, 0x3e, 0x0f, 0x80, + 0x1e, 0x0f, 0x00, 0x0e, 0x0e, 0x00, 0x03, 0xf8, 0x00 +}; +// 'wheel3', 19x19px +const unsigned char wheel_side [] PROGMEM = { + 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1b, 0xff, 0x00, 0x31, 0xff, 0x80, 0x61, 0xff, 0xc0, 0x40, + 0xff, 0xc0, 0xe0, 0xff, 0xe0, 0xf8, 0xfe, 0x20, 0xff, 0xb8, 0x20, 0xff, 0x50, 0x20, 0xff, 0xb8, + 0x20, 0xf8, 0xfe, 0x20, 0xe0, 0xff, 0xe0, 0x40, 0xff, 0xc0, 0x61, 0xff, 0xc0, 0x31, 0xff, 0x80, + 0x1b, 0xff, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 +}; + +// 'chroma', 24x25px +const unsigned char icon_chroma[] PROGMEM = { + 0x00, 0x00, 0x00, 0x75, 0x77, 0x00, 0x45, 0x55, 0x03, 0x47, 0x65, + 0x0f, 0x75, 0x57, 0x39, 0x00, 0x00, 0xe7, 0x00, 0x03, 0x9c, 0x00, + 0x0f, 0xf1, 0x00, 0x3f, 0xc2, 0x00, 0xff, 0x09, 0x03, 0xfc, 0x64, + 0x0f, 0xf0, 0xb2, 0x37, 0xc2, 0x59, 0xd7, 0x09, 0x2d, 0xdc, 0x64, + 0x96, 0x70, 0xb2, 0x59, 0x82, 0x59, 0x67, 0xc9, 0x2d, 0x9e, 0xec, + 0x96, 0x78, 0x76, 0x59, 0xe0, 0x3b, 0x67, 0x80, 0x1d, 0x9e, 0x00, + 0x0e, 0x78, 0x00, 0x06, 0xe0, 0x00, 0x02, 0x80, 0x00}; + +// 'grid', 24x15px +const unsigned char icon_grid[] PROGMEM = { + 0xc0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x36, 0xdb, 0x6c, 0x36, 0xdb, 0x6c, + 0x00, 0x00, 0x00, 0x36, 0xdb, 0x6c, 0x36, 0xdb, 0x6c, 0x00, 0x00, 0x00, + 0x36, 0xdb, 0x6c, 0x36, 0xdb, 0x6c, 0x00, 0x00, 0x00, 0x36, 0xdb, 0x6c, + 0x36, 0xdb, 0x6c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03}; + +// 'lfo', 24x25px +const unsigned char icon_lfo[] PROGMEM = { + 0xc0, 0x1f, 0x1c, 0xc0, 0x3f, 0x3e, 0xc0, 0x60, 0x63, 0xc0, 0x60, + 0x63, 0xc0, 0x7c, 0x63, 0xc0, 0x7c, 0x63, 0xe0, 0x60, 0x63, 0x70, + 0x60, 0x63, 0x3e, 0x60, 0x63, 0x1e, 0x60, 0x3e, 0x00, 0x00, 0x1c, + 0x0f, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x40, 0x20, 0x00, 0x40, 0x20, + 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0xaa, 0xb5, 0x55, 0x00, + 0x10, 0x02, 0x00, 0x10, 0x02, 0x00, 0x10, 0x02, 0x00, 0x08, 0x04, + 0x00, 0x08, 0x04, 0x00, 0x06, 0x18, 0x00, 0x01, 0xe0}; + +// 'para', 24x19px +const unsigned char icon_para[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x1c, 0x50, + 0x00, 0x63, 0x08, 0x00, 0x63, 0x08, 0x00, 0xff, 0x84, 0x10, 0xff, 0x84, + 0x10, 0xff, 0x80, 0x08, 0x7f, 0x00, 0x08, 0x7f, 0x00, 0x05, 0x1c, 0x00, + 0x03, 0x00, 0x00, 0x07, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xdd, 0xc0, + 0x01, 0xdd, 0xc0, 0x01, 0xdd, 0xc0, 0x00, 0x00, 0x00}; + +// 'step', 24x25px +const unsigned char icon_step[] PROGMEM = { + 0x00, 0x00, 0xd7, 0x00, 0x03, 0xdd, 0x00, 0x0c, 0xf5, 0x00, 0x38, + 0x77, 0x00, 0xe0, 0x3c, 0x03, 0x80, 0x7f, 0x0f, 0x01, 0xde, 0x39, + 0x87, 0x38, 0xd6, 0xcc, 0xe1, 0xd9, 0xfb, 0x86, 0x7f, 0xfe, 0x19, + 0xa7, 0x98, 0x67, 0xde, 0x61, 0x9e, 0xe9, 0x86, 0x78, 0x76, 0x19, + 0xe0, 0x3a, 0x67, 0x80, 0x1d, 0x9e, 0x00, 0x0e, 0x78, 0x00, 0x06, + 0xe0, 0x00, 0x02, 0x80, 0x00, 0x00, 0x77, 0x77, 0x00, 0x42, 0x45, + 0x00, 0x72, 0x67, 0x00, 0x12, 0x44, 0x00, 0x72, 0x74}; + +// 'mixer', 24x15px +const unsigned char icon_mixer[] PROGMEM = { + 0x8c, 0x46, 0x23, 0x0c, 0x06, 0x03, 0x8c, 0x5f, 0xa3, 0x3f, 0x1f, 0x83, + 0xbf, 0x50, 0xaf, 0x21, 0x1f, 0x8f, 0xbf, 0x50, 0xa8, 0x21, 0x1f, 0x8f, + 0xbf, 0x50, 0xa8, 0x21, 0x1f, 0x8f, 0xbf, 0x5f, 0xa8, 0x3f, 0x06, 0x0f, + 0x8c, 0x46, 0x2f, 0x0c, 0x06, 0x03, 0x8c, 0x46, 0x23}; + // 'gateboxlarge', 24x25px const unsigned char icon_gatebox[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x80, 0x00, 0x7e, @@ -682,12 +756,10 @@ const unsigned char icon_rhytmecho[] PROGMEM = { // 'route', 24x16px const unsigned char icon_route[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf1, 0xf8, 0x07, - 0xe3, 0xf0, 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x1f, 0x8f, - 0xc0, 0x0f, 0x07, 0x80, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0x07, 0x80, 0x19, 0x8c, 0xc0, 0x0f, 0x07, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf1, 0xf8, 0x07, 0xe3, 0xf0, + 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x1f, 0x8f, 0xc0, 0x0f, 0x07, 0x80, + 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x80, 0x19, 0x8c, 0xc0, + 0x0f, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 'md_rev', 34x24px const unsigned char icon_md[] PROGMEM = { diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index f89791a6e..81d76d95f 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -194,6 +194,26 @@ extern const unsigned char encoder_small_4[]; extern const unsigned char encoder_small_5[]; // 'encoder6', 11x11px extern const unsigned char encoder_small_6[]; + +// 'wheel1', 19x19px +extern const unsigned char wheel_top []; +// 'wheel2', 19x19px +extern const unsigned char wheel_angle []; +// 'wheel3', 19x19px +extern const unsigned char wheel_side []; + +// 'chroma', 24x25px +extern const unsigned char icon_chroma[]; +// 'grid', 24x15px +extern const unsigned char icon_grid[]; +// 'lfo', 24x25px +extern const unsigned char icon_lfo[]; +// 'mixer', 24x15px +extern const unsigned char icon_mixer[]; +// 'para', 24x19px +extern const unsigned char icon_para[]; +// 'step', 24x25px +extern const unsigned char icon_step[]; // 'gatebox', 24x25px extern const unsigned char icon_gatebox[]; // 'rythmecho', 24x25px diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index f8a71343a..8d644d237 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -13,6 +13,9 @@ struct PageSelectEntry { LightPage *Page; uint8_t PageNumber; // same as trig id uint8_t CategoryId; + uint8_t IconWidth; + uint8_t IconHeight; + uint8_t *IconData; }; const PageCategory Categories[] PROGMEM = { @@ -23,24 +26,24 @@ const PageCategory Categories[] PROGMEM = { }; const PageSelectEntry Entries[] PROGMEM = { - {"GRID", &grid_page, 0, 0}, - {"MIXER", &mixer_page, 1, 0}, - {"ROUTE", &route_page, 2, 0}, - {"LFO", &lfo_page, 3, 0}, - - {"STEP EDIT", &seq_step_page, 4, 1}, - {"RECORD", &seq_rtrk_page, 5, 1}, - {"LOCKS", &seq_param_page[0], 6, 1}, - {"CHROMA", &seq_ptc_page, 7, 1}, - - {"SOUND MANAGER", &sound_browser, 8, 2}, - {"WAV DESIGNER", &wd.pages[0], 9, 2}, - {"LOUDNESS", &loudness_page, 10, 2}, - - {"DELAY", &fx_page_a, 12, 3}, - {"REVERB", &fx_page_b, 13, 3}, - {"RAM-1", &ram_page_a, 14, 3}, - {"RAM-2", &ram_page_b, 15, 3}, + {"GRID", &grid_page, 0, 0, 24, 15, (uint8_t *)icon_grid}, + {"MIXER", &mixer_page, 1, 0, 24, 15, (uint8_t *)icon_mixer}, + {"ROUTE", &route_page, 2, 0, 24, 16, (uint8_t *)icon_route}, + {"LFO", &lfo_page, 3, 0, 24, 25, (uint8_t *)icon_lfo}, + + {"STEP EDIT", &seq_step_page, 4, 1, 24, 25, (uint8_t *)icon_step}, + {"RECORD", &seq_rtrk_page, 5, 1, 0, 0, nullptr}, + {"LOCKS", &seq_param_page[0], 6, 1, 24, 19, (uint8_t *)icon_para}, + {"CHROMA", &seq_ptc_page, 7, 1, 24, 25, (uint8_t *)icon_chroma}, + + {"SOUND MANAGER", &sound_browser, 8, 2, 0, 0, nullptr}, + {"WAV DESIGNER", &wd.pages[0], 9, 2, 0, 0, nullptr}, + {"LOUDNESS", &loudness_page, 10, 2, 0, 0, nullptr}, + + {"DELAY", &fx_page_a, 12, 3, 24, 25, (uint8_t *)icon_rhytmecho}, + {"REVERB", &fx_page_b, 13, 3, 24, 25, (uint8_t *)icon_gatebox}, + {"RAM-1", &ram_page_a, 14, 3, 19, 19, (uint8_t *)wheel_top}, + {"RAM-2", &ram_page_b, 15, 3, 19, 19, (uint8_t *)wheel_angle}, }; constexpr uint8_t n_category = sizeof(Categories) / sizeof(PageCategory); @@ -56,8 +59,7 @@ static uint8_t get_pageidx(uint8_t page_number) { return i; } -static LightPage *get_page(uint8_t page_number, char *str) { - uint8_t pageidx = get_pageidx(page_number); +static LightPage *get_page(uint8_t pageidx, char *str) { if (pageidx < n_entry) { if (str) { m_strncpy_p(str, (PGM_P) & (Entries[pageidx].Name), 16); @@ -71,10 +73,28 @@ static LightPage *get_page(uint8_t page_number, char *str) { } } +static void get_page_icon(uint8_t pageidx, const uint8_t *&icon, uint8_t &w, + uint8_t &h) { + if (pageidx < n_entry) { + icon = pgm_read_word(&Entries[pageidx].IconData); + w = pgm_read_byte(&Entries[pageidx].IconWidth); + h = pgm_read_byte(&Entries[pageidx].IconHeight); + } else { + icon = nullptr; + w = h = 0; + } +} + +static void get_category_name_by_idx(uint8_t catidx, char *str) { + if (str) { + m_strncpy_p(str, (PGM_P) & (Categories[catidx].Name), 16); + } +} + static void get_category_name(uint8_t page_number, char *str) { uint8_t pageidx, catidx; - pageidx= get_pageidx(page_number); + pageidx = get_pageidx(page_number); if (pageidx >= n_entry) { goto get_category_name_fail; } @@ -82,9 +102,7 @@ static void get_category_name(uint8_t page_number, char *str) { if (catidx >= n_category) { goto get_category_name_fail; } - if (str) { - m_strncpy_p(str, (PGM_P) & (Categories[catidx].Name), 16); - } + get_category_name_by_idx(catidx, str); return; get_category_name_fail: @@ -98,6 +116,19 @@ void PageSelectPage::setup() {} void PageSelectPage::init() { #ifdef OLED_DISPLAY oled_display.clearDisplay(); + oled_display.fillRect(0, 0, 128, 7, WHITE); + oled_display.setFont(&TomThumb); + oled_display.setTextColor(BLACK); + oled_display.setCursor(47, 6); + oled_display.print("PAGE SELECT"); + oled_display.setTextColor(WHITE); + char str[16]; + uint8_t label_pos[4] = {30, 57, 81, 104}; + for (uint8_t i = 0; i < 4; ++i) { + get_category_name_by_idx(i, str); + oled_display.setCursor(label_pos[i], 31); + oled_display.print(str); + } #endif md_exploit.on(); note_interface.state = true; @@ -107,7 +138,7 @@ void PageSelectPage::cleanup() { note_interface.init_notes(); } uint8_t PageSelectPage::get_nextpage_down() { for (int8_t i = page_select - 1; i >= 0; i--) { - if (get_page(i, nullptr)) { + if (get_page(get_pageidx(i), nullptr)) { return i; } } @@ -116,7 +147,7 @@ uint8_t PageSelectPage::get_nextpage_down() { uint8_t PageSelectPage::get_nextpage_up() { for (uint8_t i = page_select + 1; i < 16; i++) { - if (get_page(i, nullptr)) { + if (get_page(get_pageidx(i), nullptr)) { return i; } } @@ -156,7 +187,7 @@ uint8_t PageSelectPage::get_category_page(uint8_t offset) { } void PageSelectPage::loop() { - MCLEncoder *enc_ = encoders[0]; + auto enc_ = (MCLEncoder *)encoders[0]; int8_t diff = enc_->cur - enc_->old; if ((diff > 0) && (page_select < 16)) { page_select = get_nextpage_up(); @@ -168,7 +199,7 @@ void PageSelectPage::loop() { enc_->cur = 64 + diff; enc_->old = 64; - enc_ = encoders[1]; + enc_ = (MCLEncoder *)encoders[1]; diff = enc_->cur - enc_->old; if ((diff > 0) && (page_select < 16)) { page_select = get_nextpage_catup(); @@ -183,18 +214,62 @@ void PageSelectPage::loop() { void PageSelectPage::display() { #ifdef OLED_DISPLAY - oled_display.clearDisplay(); char str[16]; - oled_display.setCursor(0,0); - oled_display.print("Page Select:"); - get_category_name(page_select, str); - oled_display.print(str); + const uint8_t *icon; + uint8_t iconw, iconh; + uint8_t pageidx; + uint8_t catidx; + + pageidx = get_pageidx(page_select); + get_page_icon(pageidx, icon, iconw, iconh); + get_page(pageidx, str); + + if(pageidx < n_entry) { + catidx = pgm_read_byte(&Entries[pageidx].CategoryId); + }else { + catidx = 0xFF; + } + + oled_display.fillRect(28, 7, 100, 16, BLACK); + oled_display.fillRect(0, 7, 25, 25, BLACK); + + // 4x trig groups + uint8_t group_x = 28; + uint8_t pagenr = 0; + for (uint8_t i = 0; i < 4; ++i) { + uint8_t trig_x = group_x + 2; + if (i == catidx) { + oled_display.fillRect(group_x, 18, 23, 6, WHITE); + } else { + oled_display.drawRect(group_x, 18, 23, 6, WHITE); + } + + for (uint8_t j = 0; j < 4; ++j) { + if (pagenr == page_select) { + oled_display.fillRect(trig_x, 19, 4, 4, BLACK); + } else if (catidx == i) { + oled_display.fillRect(trig_x + 1, 20, 2, 2, BLACK); + } else { + oled_display.fillRect(trig_x + 1, 20, 2, 2, WHITE); + } - oled_display.setCursor(0,15); - get_page(page_select, str); + ++pagenr; + trig_x += 5; + } + + group_x += 24; + } + + oled_display.setFont(); + oled_display.setCursor(29, 9); oled_display.print(str); - oled_display.display(); + if (icon != nullptr) { + oled_display.drawBitmap(12 - iconw / 2, 19 - iconh / 2, icon, iconw, iconh, + WHITE); + } + + oled_display.display(); #else GUI.setLine(GUI.LINE1); char str[16]; @@ -203,7 +278,7 @@ void PageSelectPage::display() { GUI.put_string_at(12, str); GUI.setLine(GUI.LINE2); - get_page(page_select, str); + get_page(get_pageidx(page_select), str); GUI.put_string_at_fill(0, str); #endif } @@ -234,7 +309,7 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON2)) { LightPage *p; - p = get_page(page_select, nullptr); + p = get_page(get_pageidx(page_select), nullptr); if (BUTTON_DOWN(Buttons.BUTTON1) || (!p)) { md_exploit.off(); GUI.setPage(&grid_page); @@ -270,6 +345,6 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { return false; } -MCLEncoder page_select_param1(0,127); -MCLEncoder page_select_param2(0,127); +MCLEncoder page_select_param1(0, 127); +MCLEncoder page_select_param2(0, 127); PageSelectPage page_select_page(&page_select_param1, &page_select_param2); diff --git a/avr/cores/megacommand/MCL/RAMPage.h b/avr/cores/megacommand/MCL/RAMPage.h index bf93a4c67..1bf52a5ed 100644 --- a/avr/cores/megacommand/MCL/RAMPage.h +++ b/avr/cores/megacommand/MCL/RAMPage.h @@ -10,28 +10,6 @@ #define SLOT_RAM_RECORD (1 << (sizeof(GridChain::row) * 8)) - 1 - 1 #define SLOT_RAM_PLAY (1 << (sizeof(GridChain::row) * 8)) - 1 - 2 -// 'wheel1', 19x19px -const unsigned char wheel_top [] PROGMEM = { - 0x03, 0xf8, 0x00, 0x0e, 0x0e, 0x00, 0x1e, 0x0f, 0x00, 0x3e, 0x0f, 0x80, 0x7f, 0x1f, 0xc0, 0x7f, - 0x1f, 0xc0, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0xff, 0x5f, 0xe0, 0xff, 0xbf, - 0xe0, 0xf8, 0xe3, 0xe0, 0x60, 0xe0, 0xe0, 0x40, 0xe0, 0x40, 0x61, 0xf0, 0xc0, 0x31, 0xf1, 0x80, - 0x1b, 0xf7, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 -}; -// 'wheel2', 19x19px -const unsigned char wheel_angle [] PROGMEM = { - 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1f, 0xfb, 0x00, 0x3f, 0xf1, 0x80, 0x7f, 0xf0, 0xc0, 0x7f, - 0xe0, 0x40, 0xff, 0xe0, 0xe0, 0x8f, 0xe3, 0xe0, 0x83, 0xbf, 0xe0, 0x81, 0x5f, 0xe0, 0x83, 0xbf, - 0xe0, 0x8f, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0x7f, 0x1f, 0xc0, 0x7f, 0x1f, 0xc0, 0x3e, 0x0f, 0x80, - 0x1e, 0x0f, 0x00, 0x0e, 0x0e, 0x00, 0x03, 0xf8, 0x00 -}; -// 'wheel3', 19x19px -const unsigned char wheel_side [] PROGMEM = { - 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1b, 0xff, 0x00, 0x31, 0xff, 0x80, 0x61, 0xff, 0xc0, 0x40, - 0xff, 0xc0, 0xe0, 0xff, 0xe0, 0xf8, 0xfe, 0x20, 0xff, 0xb8, 0x20, 0xff, 0x50, 0x20, 0xff, 0xb8, - 0x20, 0xf8, 0xfe, 0x20, 0xe0, 0xff, 0xe0, 0x40, 0xff, 0xc0, 0x61, 0xff, 0xc0, 0x31, 0xff, 0x80, - 0x1b, 0xff, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 -}; - class RAMPage : public LightPage, MidiCallback { public: RAMPage(uint8_t _page_id, Encoder *e1 = NULL, Encoder *e2 = NULL, diff --git a/design/design_pageselect.xml b/design/design_pageselect.xml new file mode 100644 index 000000000..b7b7636ef --- /dev/null +++ b/design/design_pageselect.xml @@ -0,0 +1,5 @@ + + + iVBORw0KGgoAAAANSUhEUgAAAIAAAAAgCAIAAABVQOdyAAAACXBIWXMAABnWAAAZ1gEY0crtAAAByklEQVRoge2awXLEIAhAY6f//8v0kBmGIrgIuGFb3mEnZgkqKGLidTWPMk5XAACnqzjNGAetdNYBa+tjx26xuwgA9D6TEZ9l1yi5kLE0T6wunW+L0LlRjOa4DSeaRqvdLcP8ZGynXRixuO3LoTcR2kTWXCyOMcSe0PsWmZqYZsA52AgVjSWOPhq1NJnF/To87ACNeWZoa8AsY7mug2l6Vmv0p5AW/doBDoxG84cgMdvDf9fpoyjZqMzO1NI77ZFF8a9OL2O/EtLQHsIRkrOgOS41a6IzAADmgN7WtxN1wGzr3V3+P8cZgsR3XuxflvBoe9eeLq/pEe3gfVlQE8ETguITImudKKhn9xH/GoDX7qYHo3/WKpKuZ4u0EMTevK+LFJbIbhUr6zHSa8DDpO2E7S9/GNqHMEuxsh4jTgekLFlZ2W01PfnUbVlhju8D7groRhfviNcWhXElszzTs9VB2sf5N4XQIkw7SQ8oXCQ4bkVJ7aDDbqjFZ+H3h357vg/KCYF0Qg5I2Q1QxJHrUB7PDrVK2fmlOJn7gLgGceuwe7Yn6yyQdkYm93Vv/rEUjJK7VhAzV4cpF9ZxD96H05DOghwYjWadAe2DQ/wAGfblwJUTGhwAAAAASUVORK5CYII= + + diff --git a/design/design_pageselect_2.xml b/design/design_pageselect_2.xml new file mode 100644 index 000000000..69d29f78a --- /dev/null +++ b/design/design_pageselect_2.xml @@ -0,0 +1,5 @@ + + + iVBORw0KGgoAAAANSUhEUgAAAIAAAAAgCAIAAABVQOdyAAAACXBIWXMAABnWAAAZ1gEY0crtAAABuElEQVRoge2a0ZaEIAhAtbP//8vsg3McBoRVQ2U27kMH0qhIEc0MACk4x8/pB/gg51yE0iyKCgD4PKlDriJyTx1ibTO+PkBCTinuazqIO0svVeqQ77Sf69SNJarHiYzVnDMvUkr182fx2AOw3HQZabA4XvFS6SonuPsAErxn8PjOS3vks5wMf0EQPJ6FWUEzB3/fWBgzeXR2mLp8AVLS3aygq/97lLpSSgCw9CWjCSu80tBtPpJyx8dyJRR2i3fmBAlSoS4zLHujL2P5UgT39dm1F2/szoK4HFnQi0UhKNBxtxr6NJb0bpM+YTVUOLSD1fdqaF0mnBP0O00/+s0BwCrrNbdT2RqCyC8RXcWQkWZI9Wwn4R6A/yhNCMEcC0MQhzQEXcVI/yZ7VM920tI/YiZDllWO682OPX6fzDc2g3DT+3WmVlU8d+Nyz13uG+H1iZ1OI+TC5rEHmw8gJTD4Jcm4PTeMS9tPRnOBei18brzoT/ZB2LExyto01GQqgGm23Anjo41dMsJPkv1kf7JvHnC/vUjzhtEdV1Y7tKROPzRnPrAvqEbJUS8009YJVyremQ4skYMEwRS/xDVFoxRB3jQAAAAASUVORK5CYII= + + diff --git a/design/page_select.png b/design/page_select.png new file mode 100644 index 0000000000000000000000000000000000000000..40ad578161d06bd7771ea05a010c0961ea050ff8 GIT binary patch literal 536 zcmV+z0_XjSP);R2q4OlCz*WE;VbSGXMbQfZo=(6Lr9B>)l=cKgSt@#CKloUzYG_d)J;Wujvu( z;C)$86TuUL;u9ikohYbh_YwaZfY~DbNe?JoBsgwGN$sG+Zm7j);rEArd%h3K34wG%4oKMzP{fhzP#(_?wMyCf zr8t@UfVc*6>#%#aC+jSbc19-mb#MN$6pWI)RuD5+hbb4 literal 0 HcmV?d00001 diff --git a/design/page_select_2.png b/design/page_select_2.png new file mode 100644 index 0000000000000000000000000000000000000000..b21003a788f05f86747c03348d2ab9d587f71692 GIT binary patch literal 518 zcmV+h0{Q)kP)T_&6p z^gB&I`pp5rD(f}3Q`s7V$S)CH#gLI^NcC(d;U0khC`|~#91BAbhcpCT#Yq1qdsNSM z($YU-CPS80j_b{iJ$|3eH#UQC)BAO%R#iNA<8sek?i=CtiJeX(N1w>J&B4jTwCIBGxnA0O8BMbXT1+Kid^?25Ys9zIss zx{UqJ-sSE-(b)ywn;G*q{N28jZTbD`*>m|UXTLK1B3t9xFoWU$G1(6XTc27m8Zss^ x9bxI<6cAQWbMOJmU>BGtKJR0+qy$6V1J>3`A#I(nS3DVjz|+;wWt~$(697BGZx^prw85kH?(j9#r85lP9bN@+Xov0{L-|p$+7?R=q_L3#v z0R + + iVBORw0KGgoAAAANSUhEUgAAAIAAAAAgCAIAAABVQOdyAAAACXBIWXMAABnWAAAZ1gEY0crtAAAAg0lEQVRoge3ZMQrDMBAAQSvk/19WivQhNjmWmJnePnOLUOHjILWmB+y9p0dMW2twS7MBbrD9t7kGp997m50OOZvqMfQdfEmAmAD/xh3wwYXlOAExAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsSeF57xWxgAfuAFhXkSKyz11EkAAAAASUVORK5CYII= + + From 8011a075a2fe9df9a8954998477cbe169a85efda Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 14 Nov 2019 16:55:46 +1100 Subject: [PATCH 279/469] Remove encoder button from menu/file pages. Use 1 for no, 4 for yes --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 9 ++------- avr/cores/megacommand/MCL/MenuPage.cpp | 10 ++-------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 2a8497731..603c16704 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -378,10 +378,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { return false; } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { int i_save; _calcindices(i_save); @@ -417,9 +414,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { } // cancel - if (EVENT_PRESSED(event, Buttons.BUTTON1) || - EVENT_RELEASED(event, Buttons.BUTTON3) || - EVENT_PRESSED(event, Buttons.BUTTON4)) { + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { on_cancel(); return true; } diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 864a7d4ff..23ba318af 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -214,18 +214,12 @@ bool MenuPageBase::handleEvent(gui_event_t *event) { return true; } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { enter(); return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON1) || - EVENT_PRESSED(event, Buttons.BUTTON2) || - EVENT_PRESSED(event, Buttons.BUTTON3) || - EVENT_PRESSED(event, Buttons.BUTTON4)) { + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { exit(); return true; } From 025fbdf30bd8f9c8780e4f7eab33aee2589ca29d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 14 Nov 2019 17:25:07 +1100 Subject: [PATCH 280/469] Add GUI.ignoreNextEvevent(i), for ignore button release when exit pages --- avr/cores/megacommand/GUI/Events.cpp | 19 ++++++++++++------- avr/cores/megacommand/GUI/Events.hh | 1 + avr/cores/megacommand/GUI/GUI.h | 4 +++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/GUI/Events.cpp b/avr/cores/megacommand/GUI/Events.cpp index 6e0dbf1e3..45b5dc006 100644 --- a/avr/cores/megacommand/GUI/Events.cpp +++ b/avr/cores/megacommand/GUI/Events.cpp @@ -5,18 +5,23 @@ #define MAX_BUTTONS 8 volatile CRingBuffer EventRB; +volatile uint8_t event_ignore_next_mask; void pollEventGUI() { for (int i = 0; i < MAX_BUTTONS; i++) { gui_event_t event; event.source = i; - if (BUTTON_PRESSED(i)) { - event.mask = EVENT_BUTTON_PRESSED; - EventRB.putp(&event); - } - if (BUTTON_RELEASED(i)) { - event.mask = EVENT_BUTTON_RELEASED; - EventRB.putp(&event); + if (!IS_BIT_SET64(event_ignore_next_mask, i)) { + if (BUTTON_PRESSED(i)) { + event.mask = EVENT_BUTTON_PRESSED; + EventRB.putp(&event); + } + if (BUTTON_RELEASED(i)) { + event.mask = EVENT_BUTTON_RELEASED; + EventRB.putp(&event); + } + } else { + CLEAR_BIT64(event_ignore_next_mask, i); } } } diff --git a/avr/cores/megacommand/GUI/Events.hh b/avr/cores/megacommand/GUI/Events.hh index 56a717224..78571531c 100644 --- a/avr/cores/megacommand/GUI/Events.hh +++ b/avr/cores/megacommand/GUI/Events.hh @@ -53,6 +53,7 @@ void pollEventGUI(); #define MAX_EVENTS 32 extern volatile CRingBuffer EventRB; +extern volatile uint8_t event_ignore_next_mask; /** @} **/ diff --git a/avr/cores/megacommand/GUI/GUI.h b/avr/cores/megacommand/GUI/GUI.h index 6e7450d93..808d755d1 100644 --- a/avr/cores/megacommand/GUI/GUI.h +++ b/avr/cores/megacommand/GUI/GUI.h @@ -174,7 +174,9 @@ class GuiClass { void removeEventHandler(event_handler_t handler) { eventHandlers.remove(handler); } - + void ignoreNextEvent(uint8_t i) { + SET_BIT64(event_ignore_next_mask, i); + } /** * Add a new task to be periodically polled (max 8). **/ From 3a0fb51b2576ca2247b2a1b99cec60f581994c77 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 14 Nov 2019 17:31:23 +1100 Subject: [PATCH 281/469] use correct is_bit macro --- avr/cores/megacommand/GUI/Events.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/GUI/Events.cpp b/avr/cores/megacommand/GUI/Events.cpp index 45b5dc006..1493fd811 100644 --- a/avr/cores/megacommand/GUI/Events.cpp +++ b/avr/cores/megacommand/GUI/Events.cpp @@ -11,7 +11,7 @@ void pollEventGUI() { for (int i = 0; i < MAX_BUTTONS; i++) { gui_event_t event; event.source = i; - if (!IS_BIT_SET64(event_ignore_next_mask, i)) { + if (!IS_BIT_SET(event_ignore_next_mask, i)) { if (BUTTON_PRESSED(i)) { event.mask = EVENT_BUTTON_PRESSED; EventRB.putp(&event); @@ -21,7 +21,7 @@ void pollEventGUI() { EventRB.putp(&event); } } else { - CLEAR_BIT64(event_ignore_next_mask, i); + CLEAR_BIT(event_ignore_next_mask, i); } } } From 7022c05ffec1adb5eef396bd522889f091ef11c6 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 14 Nov 2019 22:32:46 +1100 Subject: [PATCH 282/469] add rec_icon.png --- art/sprites/rec_icon.png | Bin 0 -> 249 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/rec_icon.png diff --git a/art/sprites/rec_icon.png b/art/sprites/rec_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d225fd004cd92a05c988f273b5868f77758a28a9 GIT binary patch literal 249 zcmVX1^@s6D=Y3@00001b5ch_0Itp) z=>Px#vq?ljR7ef&RM8HAAP5xK|NpXi+BtMA+mxPkOA7Js08K&&DL-x_x*C$xLatdS;I-%pQ;s0eV>eHy`#k0(|ITzSCkI1EiL( zuGU${xc8vTU@Qq0!;Y$}*7@8K5JVHvH;1w#{w Date: Fri, 15 Nov 2019 00:16:22 +0800 Subject: [PATCH 283/469] add rec icon to pageselect. update confirm page --- avr/cores/megacommand/MCL/GridPage.cpp | 1 + avr/cores/megacommand/MCL/MCLGUI.cpp | 43 +++++++++++-------- avr/cores/megacommand/MCL/MCLGUI.h | 2 + avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 +- .../megacommand/MCL/QuestionDialogPage.cpp | 12 +++--- 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 4d5219ad1..a52584a7b 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -825,6 +825,7 @@ bool GridPage::handleEvent(gui_event_t *event) { md_exploit.ignore_last_track_once = true; } + // TODO add back an option for this? /* if (EVENT_PRESSED(event, Buttons.ENCODER1)) { seq_step_page.isSetup = false; diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 5d3134d1e..55dd3fe92 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -661,26 +661,26 @@ const unsigned char encoder_small_6[] PROGMEM = { 0x20, 0xb0, 0x20, 0x40, 0x40, 0x40, 0x40, 0x31, 0x80, 0x0e, 0x00}; // 'wheel1', 19x19px -const unsigned char wheel_top [] PROGMEM = { - 0x03, 0xf8, 0x00, 0x0e, 0x0e, 0x00, 0x1e, 0x0f, 0x00, 0x3e, 0x0f, 0x80, 0x7f, 0x1f, 0xc0, 0x7f, - 0x1f, 0xc0, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0xff, 0x5f, 0xe0, 0xff, 0xbf, - 0xe0, 0xf8, 0xe3, 0xe0, 0x60, 0xe0, 0xe0, 0x40, 0xe0, 0x40, 0x61, 0xf0, 0xc0, 0x31, 0xf1, 0x80, - 0x1b, 0xf7, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 -}; +const unsigned char wheel_top[] PROGMEM = { + 0x03, 0xf8, 0x00, 0x0e, 0x0e, 0x00, 0x1e, 0x0f, 0x00, 0x3e, 0x0f, 0x80, + 0x7f, 0x1f, 0xc0, 0x7f, 0x1f, 0xc0, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xe0, + 0xff, 0xbf, 0xe0, 0xff, 0x5f, 0xe0, 0xff, 0xbf, 0xe0, 0xf8, 0xe3, 0xe0, + 0x60, 0xe0, 0xe0, 0x40, 0xe0, 0x40, 0x61, 0xf0, 0xc0, 0x31, 0xf1, 0x80, + 0x1b, 0xf7, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00}; // 'wheel2', 19x19px -const unsigned char wheel_angle [] PROGMEM = { - 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1f, 0xfb, 0x00, 0x3f, 0xf1, 0x80, 0x7f, 0xf0, 0xc0, 0x7f, - 0xe0, 0x40, 0xff, 0xe0, 0xe0, 0x8f, 0xe3, 0xe0, 0x83, 0xbf, 0xe0, 0x81, 0x5f, 0xe0, 0x83, 0xbf, - 0xe0, 0x8f, 0xff, 0xe0, 0xff, 0xbf, 0xe0, 0x7f, 0x1f, 0xc0, 0x7f, 0x1f, 0xc0, 0x3e, 0x0f, 0x80, - 0x1e, 0x0f, 0x00, 0x0e, 0x0e, 0x00, 0x03, 0xf8, 0x00 -}; +const unsigned char wheel_angle[] PROGMEM = { + 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1f, 0xfb, 0x00, 0x3f, 0xf1, 0x80, + 0x7f, 0xf0, 0xc0, 0x7f, 0xe0, 0x40, 0xff, 0xe0, 0xe0, 0x8f, 0xe3, 0xe0, + 0x83, 0xbf, 0xe0, 0x81, 0x5f, 0xe0, 0x83, 0xbf, 0xe0, 0x8f, 0xff, 0xe0, + 0xff, 0xbf, 0xe0, 0x7f, 0x1f, 0xc0, 0x7f, 0x1f, 0xc0, 0x3e, 0x0f, 0x80, + 0x1e, 0x0f, 0x00, 0x0e, 0x0e, 0x00, 0x03, 0xf8, 0x00}; // 'wheel3', 19x19px -const unsigned char wheel_side [] PROGMEM = { - 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1b, 0xff, 0x00, 0x31, 0xff, 0x80, 0x61, 0xff, 0xc0, 0x40, - 0xff, 0xc0, 0xe0, 0xff, 0xe0, 0xf8, 0xfe, 0x20, 0xff, 0xb8, 0x20, 0xff, 0x50, 0x20, 0xff, 0xb8, - 0x20, 0xf8, 0xfe, 0x20, 0xe0, 0xff, 0xe0, 0x40, 0xff, 0xc0, 0x61, 0xff, 0xc0, 0x31, 0xff, 0x80, - 0x1b, 0xff, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00 -}; +const unsigned char wheel_side[] PROGMEM = { + 0x03, 0xf8, 0x00, 0x0f, 0xfe, 0x00, 0x1b, 0xff, 0x00, 0x31, 0xff, 0x80, + 0x61, 0xff, 0xc0, 0x40, 0xff, 0xc0, 0xe0, 0xff, 0xe0, 0xf8, 0xfe, 0x20, + 0xff, 0xb8, 0x20, 0xff, 0x50, 0x20, 0xff, 0xb8, 0x20, 0xf8, 0xfe, 0x20, + 0xe0, 0xff, 0xe0, 0x40, 0xff, 0xc0, 0x61, 0xff, 0xc0, 0x31, 0xff, 0x80, + 0x1b, 0xff, 0x00, 0x0f, 0xfe, 0x00, 0x03, 0xf8, 0x00}; // 'chroma', 24x25px const unsigned char icon_chroma[] PROGMEM = { @@ -692,6 +692,13 @@ const unsigned char icon_chroma[] PROGMEM = { 0x96, 0x78, 0x76, 0x59, 0xe0, 0x3b, 0x67, 0x80, 0x1d, 0x9e, 0x00, 0x0e, 0x78, 0x00, 0x06, 0xe0, 0x00, 0x02, 0x80, 0x00}; +// 'rec', 24x15px +const unsigned char icon_rec[] PROGMEM = { + 0x3f, 0xff, 0xfc, 0x40, 0x00, 0x02, 0x40, 0x00, 0x02, 0x80, 0x00, 0x01, + 0xb9, 0xe7, 0x39, 0xa5, 0x08, 0x7d, 0xa5, 0x08, 0x7d, 0xbd, 0xc8, 0x7d, + 0xa5, 0x08, 0x7d, 0xa5, 0x08, 0x7d, 0xa5, 0xe7, 0x39, 0x80, 0x00, 0x01, + 0x40, 0x00, 0x02, 0x40, 0x00, 0x02, 0x3f, 0xff, 0xfc}; + // 'grid', 24x15px const unsigned char icon_grid[] PROGMEM = { 0xc0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x36, 0xdb, 0x6c, 0x36, 0xdb, 0x6c, diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index ac6bc7327..b401e14ea 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -204,6 +204,8 @@ extern const unsigned char wheel_side []; // 'chroma', 24x25px extern const unsigned char icon_chroma[]; +// 'rec', 24x15px +extern const unsigned char icon_rec[]; // 'grid', 24x15px extern const unsigned char icon_grid[]; // 'lfo', 24x25px diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 8d644d237..ff4b6242b 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -32,7 +32,7 @@ const PageSelectEntry Entries[] PROGMEM = { {"LFO", &lfo_page, 3, 0, 24, 25, (uint8_t *)icon_lfo}, {"STEP EDIT", &seq_step_page, 4, 1, 24, 25, (uint8_t *)icon_step}, - {"RECORD", &seq_rtrk_page, 5, 1, 0, 0, nullptr}, + {"RECORD", &seq_rtrk_page, 5, 1, 24, 15, (uint8_t*) icon_rec}, {"LOCKS", &seq_param_page[0], 6, 1, 24, 19, (uint8_t *)icon_para}, {"CHROMA", &seq_ptc_page, 7, 1, 24, 25, (uint8_t *)icon_chroma}, diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp index 129a6574d..dd130450f 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -12,10 +12,10 @@ void QuestionDialogPage::init(const char* title, const char* text) { oled_display.setTextColor(WHITE); oled_display.setCursor(MCLGUI::dlg_info_x2 - 86, MCLGUI::dlg_info_y1 + 24); - oled_display.print("2 YES"); + oled_display.print("W YES"); oled_display.setCursor(MCLGUI::dlg_info_x2 - 55, MCLGUI::dlg_info_y1 + 24); - oled_display.print("3 NO"); + oled_display.print("S NO"); oled_display.drawRect(MCLGUI::dlg_info_x2 - 88, MCLGUI::dlg_info_y1 + 17, 21, 8, WHITE); oled_display.drawRect(MCLGUI::dlg_info_x2 - 57, MCLGUI::dlg_info_y1 + 17, 19, 8, WHITE); @@ -35,25 +35,25 @@ bool QuestionDialogPage::handleEvent(gui_event_t *event) { return false; } - if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { oled_display.fillRect(MCLGUI::dlg_info_x2 - 82, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { oled_display.fillRect(MCLGUI::dlg_info_x2 - 51, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); return true; } - if (EVENT_RELEASED(event, Buttons.BUTTON2)) { + if (EVENT_RELEASED(event, Buttons.BUTTON4)) { return_state = true; GUI.popPage(); return true; } - if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + if (EVENT_RELEASED(event, Buttons.BUTTON1)) { return_state = false; GUI.popPage(); return true; From fe5b6fa19802a6acd5c5355f618c4ebf86bc8aeb Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 15 Nov 2019 17:17:21 +1100 Subject: [PATCH 284/469] New and improved icons --- art/sprites/lfo.png | Bin 310 -> 0 bytes art/sprites/lfo_icon.png | Bin 0 -> 301 bytes art/sprites/loudness_icon.png | Bin 0 -> 294 bytes art/sprites/mixer_icon_new.png | Bin 0 -> 183 bytes art/sprites/wavd_icon.png | Bin 0 -> 291 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 art/sprites/lfo.png create mode 100644 art/sprites/lfo_icon.png create mode 100644 art/sprites/loudness_icon.png create mode 100644 art/sprites/mixer_icon_new.png create mode 100644 art/sprites/wavd_icon.png diff --git a/art/sprites/lfo.png b/art/sprites/lfo.png deleted file mode 100644 index 41df91f8cce55e49da1b978d914e9619c1db25de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 310 zcmV-60m=S}P)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#@JU2LR7efIlZ_68FbIU##`k~a&1?1*B$!8vBeS0=kb>a>|=mYmC36{UwQ)HH0!4(pc#|MKbr diff --git a/art/sprites/lfo_icon.png b/art/sprites/lfo_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c5c87a6c02a5c872a3ffca457bebf0236fedb74f GIT binary patch literal 301 zcmV+|0n+}7P)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#=Sf6CR7efQR0$5lAPg(0|Nk5D00000NkvXXu0mjfczt$k literal 0 HcmV?d00001 diff --git a/art/sprites/loudness_icon.png b/art/sprites/loudness_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..685ee1b4b96f6af91072b93dfcf5367a412f9bbc GIT binary patch literal 294 zcmV+>0oneEP)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#;7LS5R7ef&Q_%_pAqcGFex&~YAKQeRn9N*ZueS{%b96?Ha!RT7eQbS>G5?6a zcLJ^X4d$F(im3xlrtcP$j?woaAONb-K|(ATZ!Lx#iGeLKLjVaZ1Ro0{89t5y2A`aZ z0E28pSXam~mK_$5WUE4plzm|Vj0Bx9iDxeY)(B9!U@XMMJK^yUK+GhXfbZI7;acki z%TF?QuPU@Szmzg@$v517)y_Uu!c{T=Kl@-WZvfz5=Xiq%q3$S><96V+4)VJg_YFYu sJpOI-tPgJp1AQ|3%Le6>RtyCnU6T$~k%G2!#qxEWUj$HMy?ix4eN-ILo9*Chzhm zrrkKLe0b`nXcG$)=d`_Df4ThpeeCu(ty=!(`H?mGiklzyHvBzufAfk}C)O?aAD+1V hg?+>Otixs9c?aXvExkJzoB>+S;OXk;vd$@?2>_aVNMQf~ literal 0 HcmV?d00001 diff --git a/art/sprites/wavd_icon.png b/art/sprites/wavd_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6f90ac5303fd3aa1350700c4945d33b5c78ff7d4 GIT binary patch literal 291 zcmV+;0o?wHP)X1^@s6D=Y3@00001b5ch_0Itp) z=>Px#-AP12R7ef&Ra*|jAPfW4`(K&OXhz0rTGp1IX-Ej1haHk?%PFP$-pf{5#{41v z0fAb_5jeiv!qy@ub$myp3FMrUk~)Tu`&+R-WG7XOJGL(^3O>iM6-{&^^&wan$lxcb zW5)Iu(D3xx1z5=vf#`Wph!&Eg^1YtIju;iw9QdP!xM9c$q=*g}cgGPa?JT0tfDW8W zruHlMU4bh^QO>5lMdgm8#94cLfw#o*5gChOj^phpnLd3LZF-WNHpPR069FSw6G$?9 p;umcELzvB+j9dN{{RK`Z@d6MWaW$CJbglpZ002ovPDHLkV1no$d4m7| literal 0 HcmV?d00001 From d1dd5aab77cbee457ee40d77d8cd3a95efd9df1e Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 14:43:55 +0800 Subject: [PATCH 285/469] implement soundbrowser pageselect, text input new layout --- .../megacommand/MCL/SoundBrowserPage.cpp | 9 ++ avr/cores/megacommand/MCL/SoundBrowserPage.h | 1 + avr/cores/megacommand/MCL/TextInputPage.cpp | 94 +++++++++++-------- 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp index 3eddc91f8..c07afa6fd 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp @@ -92,6 +92,15 @@ void SoundBrowserPage::on_cancel() { void SoundBrowserPage::on_select(const char *__) { load_sound(); } +bool SoundBrowserPage::handleEvent(gui_event_t* event) { + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + GUI.setPage(&page_select_page); + return true; + } + + return FileBrowserPage::handleEvent(event); +} + MCLEncoder soundbrowser_param1(1, 10, ENCODER_RES_SYS); MCLEncoder soundbrowser_param2(0, 36, ENCODER_RES_SYS); SoundBrowserPage sound_browser(&soundbrowser_param1, &soundbrowser_param2); diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.h b/avr/cores/megacommand/MCL/SoundBrowserPage.h index a11cdaa35..fa9eec50c 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.h +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.h @@ -14,6 +14,7 @@ class SoundBrowserPage : public FileBrowserPage { virtual void on_new(); virtual void on_select(const char*); virtual void on_cancel(); + virtual bool handleEvent(gui_event_t *event); void add_entry(char *entry); void draw_scrollbar(uint8_t x_offset); void init(); diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 1d8160d62..f2fae7d8f 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -12,8 +12,7 @@ inline char _getchar(uint8_t i) { } // chr -> idx -uint8_t _findchar(char chr) -{ +uint8_t _findchar(char chr) { // Check to see that the character chosen is in the list of allowed // characters for (auto x = 0; x < sz_allowedchar; ++x) { @@ -155,7 +154,7 @@ void TextInputPage::display_normal() { auto time = clock_diff(last_clock, slowclock); #ifdef OLED_DISPLAY - //mcl_gui.clear_popup(); <-- E_TOOSLOW + // mcl_gui.clear_popup(); <-- E_TOOSLOW oled_display.fillRect(s_text_x, s_text_y, 6 * length, 8, BLACK); oled_display.setFont(); oled_display.setCursor(s_text_x, s_text_y); @@ -163,8 +162,7 @@ void TextInputPage::display_normal() { if (time < FLASH_SPEED) { // the default font is 6x8 auto tx = s_text_x + 6 * cursor_position; - oled_display.fillRect(tx, s_text_y, 6, 8, - WHITE); + oled_display.fillRect(tx, s_text_y, 6, 8, WHITE); oled_display.setCursor(s_text_x + 6 * cursor_position, s_text_y); oled_display.setTextColor(BLACK); oled_display.print(text[cursor_position]); @@ -200,7 +198,8 @@ void TextInputPage::display_charpane() { calc_charpane_coord(sx, sy); oled_display.fillRect(sx, sy, 7, 7, INVERT); // draw new highlight - sx = encoders[0]->cur; sy = encoders[1]->cur; + sx = encoders[0]->cur; + sy = encoders[1]->cur; calc_charpane_coord(sx, sy); oled_display.fillRect(sx, sy, 7, 7, INVERT); // update text. in charpane mode, cursor_position remains constant @@ -234,7 +233,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { oled_display.clearDisplay(); // before exiting charpane, advance current cursor to the next. ++cursor_position; - if(cursor_position >= length) { + if (cursor_position >= length) { cursor_position = length - 1; } // then, config normal input line @@ -244,52 +243,69 @@ bool TextInputPage::handleEvent(gui_event_t *event) { return false; } - if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { - return_state = true; - uint8_t cpy_len = length; - for (uint8_t n = length - 1; - n > 0 && text[n] == ' '; n--) { - cpy_len -= 1; - } - m_strncpy(textp, text, cpy_len); - textp[cpy_len] = '\0'; + if (EVENT_RELEASED(event, Buttons.BUTTON1)) { + return_state = false; GUI.popPage(); return true; } + if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + config_charpane(); + return true; + } + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { - // Toggle upper + lower case - if (encoders[1]->cur <= 25) { - encoders[1]->cur += 26; - } else if (encoders[1]->cur <= 51) { - encoders[1]->cur -= 26; + if (cursor_position == 0) { + return false; + } + + if (cursor_position == length - 1 && isspace(text[cursor_position])) { + // delete last + text[cursor_position] = ' '; + } else { + // backspace + for (uint8_t i = cursor_position - 1; i < length - 1; ++i) { + text[i] = text[i + 1]; + } + --cursor_position; } + config_normal(); return true; } if (EVENT_PRESSED(event, Buttons.BUTTON4)) { - // Clear text - for (uint8_t n = 1; n < length; n++) { - text[n] = ' '; + return_state = true; + uint8_t cpy_len = length; + for (uint8_t n = length - 1; n > 0 && text[n] == ' '; n--) { + cpy_len -= 1; } - text[0] = 'a'; - encoders[0]->cur = 0; - DEBUG_PRINTLN(text); - update_char(); + m_strncpy(textp, text, cpy_len); + textp[cpy_len] = '\0'; + GUI.popPage(); return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON2)) { - config_charpane(); - return true; - } + // if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + // Toggle upper + lower case + // if (encoders[1]->cur <= 25) { + // encoders[1]->cur += 26; + //} else if (encoders[1]->cur <= 51) { + // encoders[1]->cur -= 26; + //} + // return true; + //} + + // if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + // Clear text + // for (uint8_t n = 1; n < length; n++) { + // text[n] = ' '; + //} + // text[0] = 'a'; + // encoders[0]->cur = 0; + // DEBUG_PRINTLN(text); + // update_char(); + // return true; + //} - if (EVENT_RELEASED(event, Buttons.BUTTON1)) { - return_state = false; - GUI.popPage(); - } return false; } From 9e013821e14d6c5dedb12def77bb2aaf254644cb Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 14:52:47 +0800 Subject: [PATCH 286/469] pick up the updated icons --- avr/cores/megacommand/MCL/MCLGUI.cpp | 43 +++++++++++++++++++--------- avr/cores/megacommand/MCL/MCLGUI.h | 8 ++++-- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 55dd3fe92..cebde7211 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -706,15 +706,32 @@ const unsigned char icon_grid[] PROGMEM = { 0x36, 0xdb, 0x6c, 0x36, 0xdb, 0x6c, 0x00, 0x00, 0x00, 0x36, 0xdb, 0x6c, 0x36, 0xdb, 0x6c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03}; -// 'lfo', 24x25px +// 'lfo', 24x24px const unsigned char icon_lfo[] PROGMEM = { - 0xc0, 0x1f, 0x1c, 0xc0, 0x3f, 0x3e, 0xc0, 0x60, 0x63, 0xc0, 0x60, - 0x63, 0xc0, 0x7c, 0x63, 0xc0, 0x7c, 0x63, 0xe0, 0x60, 0x63, 0x70, - 0x60, 0x63, 0x3e, 0x60, 0x63, 0x1e, 0x60, 0x3e, 0x00, 0x00, 0x1c, - 0x0f, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x40, 0x20, 0x00, 0x40, 0x20, - 0x00, 0x80, 0x10, 0x00, 0x80, 0x10, 0x00, 0xaa, 0xb5, 0x55, 0x00, - 0x10, 0x02, 0x00, 0x10, 0x02, 0x00, 0x10, 0x02, 0x00, 0x08, 0x04, - 0x00, 0x08, 0x04, 0x00, 0x06, 0x18, 0x00, 0x01, 0xe0}; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xff, 0x10, 0x1e, 0x00, + 0x08, 0x60, 0x00, 0x08, 0x80, 0x00, 0x09, 0x00, 0x00, 0xaa, 0xaa, 0xaa, + 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3e, 0x1c, 0xc0, 0x7e, 0x3e, + 0xc0, 0x60, 0x63, 0xc0, 0x60, 0x63, 0xc0, 0x7c, 0x63, 0xe0, 0x7c, 0x63, + 0x70, 0x60, 0x63, 0x3e, 0x60, 0x3e, 0x0e, 0x60, 0x1c, 0x00, 0x00, 0x00}; + +// 'loudness', 24x16px +const unsigned char icon_loudness[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x11, 0xff, 0x88, 0x25, + 0xc3, 0xa4, 0x29, 0x99, 0x94, 0x29, 0xa5, 0x94, 0x29, 0xb5, + 0x94, 0x29, 0x99, 0x94, 0x25, 0xc3, 0xa4, 0x11, 0xff, 0x88, + 0x01, 0xff, 0x80, 0x01, 0xe7, 0x80, 0x01, 0xe7, 0x80, 0x01, + 0xff, 0x80, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 + +}; + +// 'wavd', 24x19px +const unsigned char icon_wavd[] PROGMEM = { + 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0xcc, 0x00, 0x00, 0xaa, 0x70, 0x00, + 0x99, 0x11, 0x10, 0x00, 0x12, 0xa8, 0x00, 0x12, 0xa8, 0x20, 0x12, 0xa8, + 0x51, 0x12, 0xa8, 0x8a, 0x74, 0xa9, 0x04, 0x10, 0xaa, 0x00, 0x10, 0xaa, + 0x00, 0x10, 0xaa, 0xee, 0x10, 0xaa, 0xaa, 0x10, 0x44, 0xaa, 0x70, 0x00, + 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 'para', 24x19px const unsigned char icon_para[] PROGMEM = { @@ -734,12 +751,12 @@ const unsigned char icon_step[] PROGMEM = { 0xe0, 0x00, 0x02, 0x80, 0x00, 0x00, 0x77, 0x77, 0x00, 0x42, 0x45, 0x00, 0x72, 0x67, 0x00, 0x12, 0x44, 0x00, 0x72, 0x74}; -// 'mixer', 24x15px +// 'mixer', 24x16px const unsigned char icon_mixer[] PROGMEM = { - 0x8c, 0x46, 0x23, 0x0c, 0x06, 0x03, 0x8c, 0x5f, 0xa3, 0x3f, 0x1f, 0x83, - 0xbf, 0x50, 0xaf, 0x21, 0x1f, 0x8f, 0xbf, 0x50, 0xa8, 0x21, 0x1f, 0x8f, - 0xbf, 0x50, 0xa8, 0x21, 0x1f, 0x8f, 0xbf, 0x5f, 0xa8, 0x3f, 0x06, 0x0f, - 0x8c, 0x46, 0x2f, 0x0c, 0x06, 0x03, 0x8c, 0x46, 0x23}; + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0xee, 0x00, 0x0e, + 0xee, 0x00, 0x0e, 0xee, 0x00, 0xee, 0xee, 0xe0, 0xee, 0xee, 0xe0, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 'gateboxlarge', 24x25px const unsigned char icon_gatebox[] PROGMEM = { diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index b401e14ea..3487a8683 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -208,9 +208,13 @@ extern const unsigned char icon_chroma[]; extern const unsigned char icon_rec[]; // 'grid', 24x15px extern const unsigned char icon_grid[]; -// 'lfo', 24x25px +// 'lfo', 24x24px extern const unsigned char icon_lfo[]; -// 'mixer', 24x15px +// 'loudness', 24x16px +extern const unsigned char icon_loudness[]; +// 'wavd', 24x19px +extern const unsigned char icon_wavd[]; +// 'mixer', 24x16px extern const unsigned char icon_mixer[]; // 'para', 24x19px extern const unsigned char icon_para[]; From f77aee6ad34740809d1ab5ff49498bec51ad2166 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 14:55:12 +0800 Subject: [PATCH 287/469] add new icons to page select page --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index ff4b6242b..09d0dc7ac 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -27,9 +27,9 @@ const PageCategory Categories[] PROGMEM = { const PageSelectEntry Entries[] PROGMEM = { {"GRID", &grid_page, 0, 0, 24, 15, (uint8_t *)icon_grid}, - {"MIXER", &mixer_page, 1, 0, 24, 15, (uint8_t *)icon_mixer}, + {"MIXER", &mixer_page, 1, 0, 24, 16, (uint8_t *)icon_mixer}, {"ROUTE", &route_page, 2, 0, 24, 16, (uint8_t *)icon_route}, - {"LFO", &lfo_page, 3, 0, 24, 25, (uint8_t *)icon_lfo}, + {"LFO", &lfo_page, 3, 0, 24, 24, (uint8_t *)icon_lfo}, {"STEP EDIT", &seq_step_page, 4, 1, 24, 25, (uint8_t *)icon_step}, {"RECORD", &seq_rtrk_page, 5, 1, 24, 15, (uint8_t*) icon_rec}, @@ -37,8 +37,8 @@ const PageSelectEntry Entries[] PROGMEM = { {"CHROMA", &seq_ptc_page, 7, 1, 24, 25, (uint8_t *)icon_chroma}, {"SOUND MANAGER", &sound_browser, 8, 2, 0, 0, nullptr}, - {"WAV DESIGNER", &wd.pages[0], 9, 2, 0, 0, nullptr}, - {"LOUDNESS", &loudness_page, 10, 2, 0, 0, nullptr}, + {"WAV DESIGNER", &wd.pages[0], 9, 2, 24, 19, (uint8_t*)icon_wavd}, + {"LOUDNESS", &loudness_page, 10, 2, 24, 16, (uint8_t*)icon_loudness}, {"DELAY", &fx_page_a, 12, 3, 24, 25, (uint8_t *)icon_rhytmecho}, {"REVERB", &fx_page_b, 13, 3, 24, 25, (uint8_t *)icon_gatebox}, From 04c71700248309bbda17418415a593d5a1ca82c2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 15 Nov 2019 18:11:26 +1100 Subject: [PATCH 288/469] Add sound_icon --- art/sprites/sound_icon.png | Bin 0 -> 245 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/sprites/sound_icon.png diff --git a/art/sprites/sound_icon.png b/art/sprites/sound_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1885058a2480477ce681cbaf67edbfb07aa74fd2 GIT binary patch literal 245 zcmVX1^@s6D=Y3@00001b5ch_0Itp) z=>Px#uSrBfR7ef&mEj72FbIUV=>1=LB_tf`+$HQsP!Mya-|eii-dY>?z3n=-kst7O zfwSsh4By$_yTj1}5zx_s2dO|6iaO Date: Fri, 15 Nov 2019 17:50:50 +0800 Subject: [PATCH 289/469] seq menu wip --- avr/cores/megacommand/MCL/GridPage.cpp | 3 +- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 32 +- avr/cores/megacommand/MCL/SeqPage.cpp | 361 ++++++++++--------- avr/cores/megacommand/MCL/SeqPage.h | 17 +- avr/cores/megacommand/MCL/SeqPages.cpp | 39 +- avr/cores/megacommand/MCL/SeqPages.h | 7 +- avr/cores/megacommand/MCL/SeqParamPage.cpp | 26 +- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 40 +- avr/cores/megacommand/MCL/SeqRlckPage.cpp | 17 +- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 26 +- avr/cores/megacommand/MCL/SeqStepPage.cpp | 21 +- 11 files changed, 279 insertions(+), 310 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index a52584a7b..2773a5542 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -798,6 +798,7 @@ bool GridPage::handleEvent(gui_event_t *event) { } #endif +/* if (BUTTON_DOWN(Buttons.BUTTON3) && (EVENT_PRESSED(event, Buttons.ENCODER1) || EVENT_PRESSED(event, Buttons.ENCODER2) || @@ -825,8 +826,6 @@ bool GridPage::handleEvent(gui_event_t *event) { md_exploit.ignore_last_track_once = true; } - // TODO add back an option for this? -/* if (EVENT_PRESSED(event, Buttons.ENCODER1)) { seq_step_page.isSetup = false; prepare(); diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 0a06e6da7..1023805b8 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -18,6 +18,9 @@ void SeqExtStepPage::config() { #endif strcpy(info2, "EXT"); + + // config menu + config_as_trackedit(); } void SeqExtStepPage::config_encoders() { @@ -153,7 +156,7 @@ void SeqExtStepPage::display() { } #else void SeqExtStepPage::display() { - SeqPage::display(); + oled_display.clearDisplay(); draw_knob_frame(); @@ -246,6 +249,8 @@ void SeqExtStepPage::display() { } draw_pattern_mask(page_select * 16, DEVICE_A4); + + SeqPage::display(); oled_display.display(); #endif } @@ -342,34 +347,11 @@ bool SeqExtStepPage::handleEvent(gui_event_t *event) { return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON2)) { - if (active_track.resolution == 1) { - active_track.resolution = 2; - init(); - - } else { - active_track.resolution = 1; - init(); - } - - return true; - } - if (EVENT_RELEASED(event, Buttons.BUTTON1)) { GUI.setPage(&seq_step_page); return true; } - if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - active_track.clear_track(); - return true; - } - if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || - (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { - for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { - active_track.clear_track(); - } - return true; - } + #endif return false; } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 346540ce0..40b50c5d1 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -5,12 +5,14 @@ uint8_t SeqPage::page_select = 0; uint8_t SeqPage::midi_device = DEVICE_MD; -uint8_t SeqPage::length = 0; -uint8_t SeqPage::resolution = 0; -uint8_t SeqPage::apply = 0; uint8_t SeqPage::ignore_button_release = 255; uint8_t SeqPage::page_count = 4; -bool SeqPage::show_track_menu = false; +bool SeqPage::show_seq_menu = false; + +uint8_t opt_resolution = 0; +uint8_t opt_trackid = 0; +static uint8_t opt_midi_device_capture = DEVICE_MD; +static SeqPage *opt_seqpage_capture = nullptr; void SeqPage::create_chars_seq() { uint8_t temp_charmap1[8] = {0, 15, 16, 16, 16, 15, 0}; @@ -50,26 +52,14 @@ void SeqPage::select_track(uint8_t device, uint8_t track) { note_interface.state = true; md_exploit.on(); } -#ifdef EXT_TRACK - if (GUI.currentPage() == &seq_extstep_page) { - GUI.setPage(&seq_step_page); - } -#endif - GUI.currentPage()->redisplay = true; - GUI.currentPage()->config(); - encoders[2]->old = encoders[2]->cur; } -#ifdef EXT_TRACK +#ifdef EXT_TRACKS else { - last_ext_track = track - 16; - if (GUI.currentPage() == &seq_step_page) { - GUI.setPage(&seq_extstep_page); - } - GUI.currentPage()->redisplay = true; - GUI.currentPage()->config(); - encoders[2]->old = encoders[2]->cur; + last_ext_track = min(track, 4); // XXX } #endif + GUI.currentPage()->redisplay = true; + GUI.currentPage()->config(); } bool SeqPage::handleEvent(gui_event_t *event) { @@ -78,10 +68,24 @@ bool SeqPage::handleEvent(gui_event_t *event) { uint8_t device = midi_active_peering.get_device(port); uint8_t track = event->source - 128; - // TI + SHIFT1: adjust track seq length. - // Ignore SHIFT1 release event so it won't trigger - // a seq page select action. - if (BUTTON_DOWN(Buttons.BUTTON3)) { + // =================== seq menu mode TI events ================ + + if (show_seq_menu) { + // TI + SHIFT2 = select track. + if (BUTTON_DOWN(Buttons.BUTTON3)) { + opt_trackid = track; + select_track(device, track); + } + + return true; + } + + // =================== normal mode TI events ================ + + // TI + WRITE (BUTTON4): adjust track seq length. + // Ignore WRITE release event so it won't trigger + // a page select action. + if (BUTTON_DOWN(Buttons.BUTTON4)) { // calculate the intended seq length. uint8_t step = track; step += 1 + page_select * 16; @@ -107,55 +111,28 @@ bool SeqPage::handleEvent(gui_event_t *event) { encoders[2]->cur = step; if (event->mask == EVENT_BUTTON_RELEASED) { note_interface.notes[track] = 0; - ignore_button_release = 2; + ignore_button_release = 4; } return true; } - // TI + SHIFT2 = select track. - /* if (BUTTON_DOWN(Buttons.BUTTON3)) { - select_track(device, track); - return true; - }*/ - // notify derived class about unhandled TI event return false; } // end TI events - // if no TI button pressed, enable page switching. -/* - if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { - if (EVENT_PRESSED(event, Buttons.ENCODER1)) { - GUI.setPage(&seq_step_page); - return false; - } - if (EVENT_PRESSED(event, Buttons.ENCODER2)) { - GUI.setPage(&seq_rtrk_page); - return false; - } - if (EVENT_PRESSED(event, Buttons.ENCODER3)) { - - GUI.setPage(&seq_param_page[0]); - return false; - } - if (EVENT_PRESSED(event, Buttons.ENCODER4)) { - - GUI.setPage(&seq_ptc_page); - return false; - } - } -*/ - // A not-ignored BUTTON2 release event triggers sequence page select - if (EVENT_RELEASED(event, Buttons.BUTTON3)) { - if (ignore_button_release != 2) { - ignore_button_release = 255; + // A not-ignored WRITE (BUTTON4) release event triggers sequence page select + if (EVENT_RELEASED(event, Buttons.BUTTON4)) { + if (ignore_button_release != 4) { page_select += 1; if (page_select >= page_count) { page_select = 0; } + } else { + // clear ignore flag + ignore_button_release = 255; } - return false; + return true; } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { @@ -165,65 +142,81 @@ bool SeqPage::handleEvent(gui_event_t *event) { GUI.setPage(&page_select_page); } - /* - // SHIFT2 changes ENC4 to track select - if (EVENT_PRESSED(event, Buttons.BUTTON3)) { - encoders[3] = &trackselect_enc; - if (midi_device == DEVICE_MD) { - trackselect_enc.cur = trackselect_enc.old = last_md_track; +#ifdef OLED_DISPLAY + // activate show_seq_menu only if S2 press is not a key combination + if (EVENT_PRESSED(event, Buttons.BUTTON3) && !show_seq_menu && + !BUTTON_DOWN(Buttons.BUTTON4)) { + show_seq_menu = true; + // capture current midi_device value + opt_midi_device_capture = midi_device; + // capture current page. + opt_seqpage_capture = this; + + if (opt_midi_device_capture == DEVICE_MD) { + DEBUG_PRINTLN("okay using MD for length update"); + opt_trackid = last_md_track; + opt_resolution = (mcl_seq.md_tracks[last_md_track].resolution); + } else { + opt_trackid = last_ext_track; + opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); } -#ifdef EXT_TRACKS - else if (midi_device == DEVICE_A4) { - trackselect_enc.cur = trackselect_enc.old = last_ext_track; + + encoders[0] = &seq_menu_value_encoder; + encoders[1] = &seq_menu_entry_encoder; + seq_menu_page.init(); + return true; + } + if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + encoders[0] = &seq_param1; + encoders[1] = &seq_param2; + oled_display.clearDisplay(); + show_seq_menu = false; + void (*row_func)() = + seq_menu_page.menu.get_row_function(seq_menu_page.encoders[1]->cur); + DEBUG_PRINTLN(seq_menu_page.encoders[1]->cur); + if (row_func != NULL) { + DEBUG_PRINTLN("func call"); + (*row_func)(); + return true; } -#endif - } else if (EVENT_RELEASED(event, Buttons.BUTTON3)) { - encoders[3] = &seq_param4; + seq_menu_page.enter(); + return true; } -*/ - /* - #ifdef OLED_DISPLAY - - if (EVENT_PRESSED(event, Buttons.BUTTON3)) { +#else + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + // capture current midi_device value + opt_midi_device_capture = midi_device; + // capture current page. + opt_seqpage_capture = this; + GUI.pushPage(&seq_menu_page); + return true; + } +#endif - show_track_menu = true; - if (midi_device == DEVICE_MD) { - DEBUG_PRINTLN("okay using MD for length update"); - SeqPage::length = (mcl_seq.md_tracks[last_md_track].length); - SeqPage::resolution = (mcl_seq.md_tracks[last_md_track].resolution); + // legacy enc push page switching code + /* + if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { + if (EVENT_PRESSED(event, Buttons.ENCODER1)) { + GUI.setPage(&seq_step_page); + return false; } - if (midi_device == DEVICE_A4) { - SeqPage::length = (mcl_seq.ext_tracks[last_ext_track].length); - SeqPage::resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); + if (EVENT_PRESSED(event, Buttons.ENCODER2)) { + GUI.setPage(&seq_rtrk_page); + return false; } + if (EVENT_PRESSED(event, Buttons.ENCODER3)) { - encoders[0] = &track_menu_param1; - encoders[1] = &track_menu_param2; - track_menu_page.init(); - SeqPage::length = 0; - return true; - } - if (EVENT_RELEASED(event, Buttons.BUTTON3)) { - oled_display.clearDisplay(); - show_track_menu = false; - void (*row_func)() = - track_menu_page.menu.get_row_function(track_menu_page.encoders[1]->cur); - DEBUG_PRINTLN(track_menu_page.encoders[1]->cur); - if (row_func != NULL) { - DEBUG_PRINTLN("func call"); - (*row_func)(); - return; - } - track_menu_page.enter(); - return true; - } - #else - if (EVENT_PRESSED(event, Buttons.BUTTON3)) { - GUI.pushPage(&track_menu_page); - return true; + GUI.setPage(&seq_param_page[0]); + return false; + } + if (EVENT_PRESSED(event, Buttons.ENCODER4)) { + + GUI.setPage(&seq_ptc_page); + return false; + } } - #endif */ + return false; } @@ -429,30 +422,38 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, } #else - - -void SeqPage::draw_lock_mask(uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step) { - mcl_gui.draw_leds(MCLGUI::seq_x0, MCLGUI::led_y, offset, lock_mask, step_count, length, show_current_step); +void SeqPage::draw_lock_mask(uint8_t offset, uint64_t lock_mask, + uint8_t step_count, uint8_t length, + bool show_current_step) { + mcl_gui.draw_leds(MCLGUI::seq_x0, MCLGUI::led_y, offset, lock_mask, + step_count, length, show_current_step); } void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { auto &active_track = mcl_seq.md_tracks[last_md_track]; - draw_lock_mask(offset, active_track.lock_mask, active_track.step_count, active_track.length, show_current_step); + draw_lock_mask(offset, active_track.lock_mask, active_track.step_count, + active_track.length, show_current_step); } -void SeqPage::draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step) { - mcl_gui.draw_trigs(MCLGUI::seq_x0, MCLGUI::trig_y, offset, pattern_mask, step_count, length); +void SeqPage::draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, + uint8_t step_count, uint8_t length, + bool show_current_step) { + mcl_gui.draw_trigs(MCLGUI::seq_x0, MCLGUI::trig_y, offset, pattern_mask, + step_count, length); } void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step) { if (device == DEVICE_MD) { auto &active_track = mcl_seq.md_tracks[last_md_track]; - draw_pattern_mask(offset, active_track.pattern_mask, active_track.step_count, active_track.length, show_current_step); + draw_pattern_mask(offset, active_track.pattern_mask, + active_track.step_count, active_track.length, + show_current_step); } #ifdef EXT_TRACKS else { - mcl_gui.draw_ext_track(MCLGUI::seq_x0, MCLGUI::trig_y, offset, last_ext_track, show_current_step); + mcl_gui.draw_ext_track(MCLGUI::seq_x0, MCLGUI::trig_y, offset, + last_ext_track, show_current_step); } #endif } @@ -489,40 +490,81 @@ void pattern_len_handler(Encoder *enc) { #endif } -void SeqPage::loop() { +void opt_trackid_handler() { + opt_seqpage_capture->select_track(opt_midi_device_capture, opt_trackid); +} - if (show_track_menu) { - if (midi_device == DEVICE_MD) { - DEBUG_PRINTLN("okay using MD for length update"); - (mcl_seq.md_tracks[last_md_track].length) = SeqPage::length; - (mcl_seq.md_tracks[last_md_track].resolution) = SeqPage::resolution; - } +void opt_resolution_handler() { + + if (opt_midi_device_capture == DEVICE_MD) { + DEBUG_PRINTLN("okay using MD for length update"); + (mcl_seq.md_tracks[last_md_track].resolution) = opt_resolution; + } #ifdef EXT_TRACKS - if (midi_device == DEVICE_A4) { - (mcl_seq.ext_tracks[last_ext_track].length) = SeqPage::length; - (mcl_seq.ext_tracks[last_ext_track].resolution) = SeqPage::resolution; - } + else { + (mcl_seq.ext_tracks[last_ext_track].resolution) = opt_resolution; + } #endif - track_menu_page.loop(); - return; + opt_seqpage_capture->init(); +} + +void opt_clear_track_handler() { + if (opt_midi_device_capture == DEVICE_MD) { + mcl_seq.md_tracks[last_md_track].clear_track(); + } else { + mcl_seq.ext_tracks[last_ext_track].clear_track(); } +} - if (trackselect_enc.hasChanged()) { +void opt_clear_locks_handler() { + if (opt_midi_device_capture == DEVICE_MD) { + mcl_seq.md_tracks[last_md_track].clear_locks(); + } else { + // TODO ext locks + } +} - auto plus = trackselect_enc.cur > trackselect_enc.old; - auto track = last_md_track; -#ifdef EXT_TRACKS - if (midi_device == DEVICE_A4) { - track = last_ext_track; +void opt_clear_all_tracks_handler() { + if (opt_midi_device_capture == DEVICE_MD) { + for (uint8_t n = 0; n < 16; ++n) { + mcl_seq.md_tracks[n].clear_track(); } -#endif - if (plus && track < 15) { - ++track; - } else if (!plus && track > 0) { - --track; + } else { + mcl_seq.ext_tracks[last_ext_track].clear_track(); + } +} + +void opt_clear_all_locks_handler() { + if (opt_midi_device_capture == DEVICE_MD) { + for (uint8_t n = 0; n < 16; ++n) { + mcl_seq.md_tracks[n].clear_locks(); } - select_track(midi_device, track); - trackselect_enc.old = trackselect_enc.cur = track; + } else { + // TODO ext locks + } +} + +void SeqPage::config_as_trackedit() { + seq_menu_page.menu.enable_entry(2, true); + seq_menu_page.menu.enable_entry(3, false); + + seq_menu_page.menu.enable_entry(7, true); + seq_menu_page.menu.enable_entry(8, false); +} + +void SeqPage::config_as_lockedit() { + seq_menu_page.menu.enable_entry(2, false); + seq_menu_page.menu.enable_entry(3, true); + + seq_menu_page.menu.enable_entry(7, false); + seq_menu_page.menu.enable_entry(8, true); +} + +void SeqPage::loop() { + + if (show_seq_menu) { + seq_menu_page.loop(); + return; } } @@ -533,8 +575,8 @@ void SeqPage::draw_page_index(bool show_page_index) { // XXX should retrieve true track length uint8_t playing_idx = (MidiClock.bar_counter - 1) % page_count; uint8_t w = pidx_w; - if (page_count == 8) { - w /= 2; + if (page_count == 8) { + w /= 2; pidx_x -= 1; } @@ -557,8 +599,6 @@ void SeqPage::draw_page_index(bool show_page_index) { pidx_x += w + 1; } - - } #ifndef OLED_DISPLAY @@ -579,8 +619,6 @@ void SeqPage::display() { // ref: design/Sequencer.png void SeqPage::display() { - auto* oldfont = oled_display.getFont(); - oled_display.clearDisplay(); bool is_md = (midi_device == DEVICE_MD); #ifdef EXT_TRACKS @@ -599,7 +637,7 @@ void SeqPage::display() { mcl_gui.draw_panel_number(track_id); // draw MD/EXT label - const char* str_ext = "MI"; + const char *str_ext = "MI"; if (ext_is_a4) { str_ext = "A4"; } @@ -614,14 +652,13 @@ void SeqPage::display() { // draw info lines mcl_gui.draw_panel_labels(info1, info2); - // if (show_track_menu) { - // uint8_t x_offset = 43; - // uint8_t y_offset = 8; - // oled_display.setFont(&TomThumb); - // oled_display.fillRect(84, 0, 40, 32, BLACK); - // track_menu_page.draw_menu(86, y_offset, 39); - //} - oled_display.setFont(oldfont); + if (show_seq_menu) { + constexpr uint8_t width = 52; + oled_display.setFont(&TomThumb); + oled_display.fillRect(128 - width - 2, 0, width + 2, 32, BLACK); + seq_menu_page.draw_menu(128 - width, 8, width); + } + } #endif @@ -634,11 +671,11 @@ void SeqPage::draw_knob_frame() { } void SeqPage::draw_knob(uint8_t i, const char *title, const char *text) { - mcl_gui.draw_knob(i,title,text); + mcl_gui.draw_knob(i, title, text); } -void SeqPage::draw_knob(uint8_t i, Encoder* enc, const char* title) { - mcl_gui.draw_knob(i,enc,title); +void SeqPage::draw_knob(uint8_t i, Encoder *enc, const char *title) { + mcl_gui.draw_knob(i, enc, title); } void SeqPageMidiEvents::setup_callbacks() { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 300673294..9ea35f2e8 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -16,17 +16,24 @@ class SeqPageMidiEvents : public MidiCallback { extern void pattern_len_handler(Encoder *enc); + +extern uint8_t opt_trackid; +extern uint8_t opt_resolution; +extern void opt_trackid_handler(); +extern void opt_resolution_handler(); +extern void opt_clear_track_handler(); +extern void opt_clear_locks_handler(); +extern void opt_clear_all_tracks_handler(); +extern void opt_clear_all_locks_handler(); + class SeqPage : public LightPage { public: // Static variables shared amongst derived objects static uint8_t page_select; static uint8_t page_count; static uint8_t midi_device; - static uint8_t length; - static uint8_t resolution; - static uint8_t apply; static uint8_t ignore_button_release; - static bool show_track_menu; + static bool show_seq_menu; bool recording = false; bool display_page_index = true; @@ -39,6 +46,8 @@ class SeqPage : public LightPage { Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { } + void config_as_trackedit(); + void config_as_lockedit(); void create_chars_seq(); void draw_lock_mask(uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step = true); void draw_lock_mask(uint8_t offset, bool show_current_step = true); diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 68d9a3123..382b9e5cb 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -1,25 +1,10 @@ #include "MCL.h" -const menu_t<5> track_menu_layout PROGMEM = { - "TRACk", - { - {"LENGTH:", 0, 64, 0, (uint8_t *) &SeqPage::length, (Page*) NULL, NULL, {}}, - {"MULTI:", 1, 2, 2, (uint8_t *) &SeqPage::resolution, (Page*) NULL, NULL, {{1, "1x"},{2, "2x"}}}, - - {"APPLY:", 0, 1, 2, (uint8_t *) &SeqPage::apply, (Page*) NULL, NULL, {{1, "--"},{1, "ALL"}}}, -// {"LOAD SND:", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, (void*) &mcl_load_sound, {}}, -// {"SAVE SND:", 0, 0, 0, (uint8_t *) NULL, (Page*) NULL, (void*) &mcl_save_sound, {}}, - }, - (&mclsys_apply_config), -}; - MCLEncoder seq_param1(0, 3, ENCODER_RES_SEQ); MCLEncoder seq_param2(0, 4, ENCODER_RES_SEQ); MCLEncoder seq_param3(0, 10, ENCODER_RES_SEQ); MCLEncoder seq_param4(0, 64, ENCODER_RES_SEQ); -MCLEncoder trackselect_enc(0, 15, ENCODER_RES_SEQ); - MCLEncoder seq_lock1(0, 127, ENCODER_RES_PARAM); MCLEncoder seq_lock2(0, 127, ENCODER_RES_PARAM); @@ -40,9 +25,27 @@ SeqExtStepPage seq_extstep_page(&seq_param1, &seq_param2, &seq_param3, #endif SeqPtcPage seq_ptc_page(&ptc_param_oct, &ptc_param_finetune, &ptc_param_len, &ptc_param_scale); -MCLEncoder track_menu_param1(0, 8, ENCODER_RES_PAT); -MCLEncoder track_menu_param2(0, 8, ENCODER_RES_PAT); -MenuPage<5> track_menu_page(&track_menu_layout, &track_menu_param1, &track_menu_param2); +const menu_t<9> seq_menu_layout PROGMEM = { + "SEQ", + { + {"TRACK SEL.", 0, 16, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, + {"COPY TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"CLEAR TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_track_handler, {}}, + {"CLEAR LCKS.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_locks_handler, {}}, + {"PASTE TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"TRACK RES.", 1, 2, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {0, "1x"}, {1, "2x"} }}, + {"STEP SHIFT", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + // clear all tracks + {"CLEAR ALL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_all_tracks_handler, {}}, + // clear all locks + {"CLEAR ALL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_all_locks_handler, {}}, + }, + NULL, +}; + +MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); +MCLEncoder seq_menu_entry_encoder(0, 8, ENCODER_RES_PAT); +MenuPage<9> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index a15ce38e0..cb4ee394c 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -21,7 +21,6 @@ extern MCLEncoder seq_param1; extern MCLEncoder seq_param2; extern MCLEncoder seq_param3; extern MCLEncoder seq_param4; -extern MCLEncoder trackselect_enc; extern MCLEncoder seq_lock1; extern MCLEncoder seq_lock2; @@ -49,9 +48,9 @@ extern SeqExtStepPage seq_extstep_page; extern SeqPtcPage seq_ptc_page; -extern MCLEncoder track_menu_param1; -extern MCLEncoder track_menu_param2; -extern MenuPage<5> track_menu_page; +extern MCLEncoder seq_menu_value_encoder; +extern MCLEncoder seq_menu_entry_encoder; +extern MenuPage<9> seq_menu_page; extern void mcl_save_sound(); extern void mcl_load_sound(); diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index b46ada37e..3ca84095e 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -22,6 +22,9 @@ void SeqParamPage::config() { } else { strcat(info2, "B"); } + + // config menu + config_as_lockedit(); } void SeqParamPage::init() { @@ -115,7 +118,9 @@ void SeqParamPage::display() { } #else void SeqParamPage::display() { - SeqPage::display(); + oled_display.clearDisplay(); + auto *oldfont = oled_display.getFont(); + draw_knob_frame(); char myName[4] = "-- "; @@ -147,7 +152,9 @@ void SeqParamPage::display() { draw_pattern_mask(page_select * 16, DEVICE_MD); draw_lock_mask(page_select * 16); + SeqPage::display(); oled_display.display(); + oled_display.setFont(oldfont); } #endif @@ -252,19 +259,6 @@ if (utiming == 0) { return true; } */ - if (EVENT_PRESSED(event, Buttons.BUTTON4)) { - mcl_seq.md_tracks[last_md_track].clear_locks(); - return true; - } - - if ((EVENT_PRESSED(event, Buttons.BUTTON1) && BUTTON_DOWN(Buttons.BUTTON4)) || - (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { - - for (uint8_t n = 0; n < 16; n++) { - mcl_seq.md_tracks[n].clear_locks(); - } - return true; - } if (EVENT_RELEASED(event, Buttons.BUTTON1)) { uint8_t page_depth = page_id; @@ -277,10 +271,6 @@ if (utiming == 0) { GUI.setPage(&seq_param_page[page_depth]); return true; } - if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - mcl_seq.md_tracks[last_md_track].clear_locks(); - return true; - } return false; } diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index bb505b965..2ee6a99ff 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -134,6 +134,9 @@ void SeqPtcPage::config() { strcpy(info2, "CHROMAT"); display_page_index = false; + + // config menu + config_as_trackedit(); } void ptc_pattern_len_handler(Encoder *enc) { @@ -264,7 +267,8 @@ void SeqPtcPage::display() { return; } - SeqPage::display(); + oled_display.clearDisplay(); + auto *oldfont = oled_display.getFont(); if (midi_device == DEVICE_MD) { dev_num = last_md_track; @@ -315,7 +319,10 @@ void SeqPtcPage::display() { // draw TI keyboard mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); + + SeqPage::display(); oled_display.display(); + oled_display.setFont(oldfont); } #endif @@ -467,37 +474,6 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { return true; } */ - if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || - (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { - if (midi_device == DEVICE_MD) { - for (uint8_t n = 0; n < mcl_seq.num_md_tracks; n++) { - mcl_seq.md_tracks[n].clear_track(); - } - - } -#ifdef EXT_TRACKS - else { - for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { - mcl_seq.ext_tracks[n].clear_track(); - } - } -#endif - return true; - } - - if (EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON2)) { -#ifdef EXT_TRACKS - if (midi_device != DEVICE_MD) { - if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { - mcl_seq.ext_tracks[last_ext_track].resolution = 2; - } else { - mcl_seq.ext_tracks[last_ext_track].resolution = 1; - } - seq_ptc_page.queue_redraw(); - } -#endif - return true; - } if (EVENT_RELEASED(event, Buttons.BUTTON4)) { diff --git a/avr/cores/megacommand/MCL/SeqRlckPage.cpp b/avr/cores/megacommand/MCL/SeqRlckPage.cpp index 224f6128a..c6619fc07 100644 --- a/avr/cores/megacommand/MCL/SeqRlckPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRlckPage.cpp @@ -19,6 +19,9 @@ void SeqRlckPage::config() { strcpy(info2, "RLCK"); display_page_index = false; + + // config menu + config_as_lockedit(); } void SeqRlckPage::init() { @@ -87,7 +90,9 @@ void SeqRlckPage::display() { if ((!redisplay) && (MidiClock.state == 2)) { return; } - SeqPage::display(); + + oled_display.clearDisplay(); + auto *oldfont = oled_display.getFont(); draw_knob_frame(); @@ -106,7 +111,9 @@ void SeqRlckPage::display() { draw_lock_mask(page_select * 16, show_current_step); draw_pattern_mask(page_select * 16, DEVICE_MD, show_current_step); + SeqPage::display(); oled_display.display(); + oled_display.setFont(oldfont); } #endif @@ -122,14 +129,6 @@ bool SeqRlckPage::handleEvent(gui_event_t *event) { return true; } - if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || - (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].clear_locks(); - } - return true; - } - if (EVENT_RELEASED(event, Buttons.BUTTON4)) { if (MD.getCurrentTrack(CALLBACK_TIMEOUT) != last_md_track) { for (uint8_t c = 0; c < 4; c++) { diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index e1d96b156..2a04dd6ac 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -77,8 +77,9 @@ void SeqRtrkPage::display() { #else void SeqRtrkPage::display() { if ((!redisplay) && (MidiClock.state == 2)) { return; } - SeqPage::display(); + oled_display.clearDisplay(); + auto *oldfont = oled_display.getFont(); draw_knob_frame(); uint8_t len = encoders[2]->getValue(); @@ -96,7 +97,9 @@ void SeqRtrkPage::display() { draw_lock_mask(page_select * 16, show_current_step); draw_pattern_mask(page_select * 16, DEVICE_MD, show_current_step); + SeqPage::display(); oled_display.display(); + oled_display.setFont(oldfont); } #endif @@ -139,27 +142,6 @@ bool SeqRtrkPage::handleEvent(gui_event_t *event) { // return true; // } - if ((EVENT_PRESSED(event, Buttons.BUTTON3) && BUTTON_DOWN(Buttons.BUTTON4)) || - (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { - - for (uint8_t n = 0; n < 16; n++) { - mcl_seq.md_tracks[n].clear_track(); - } - return true; - } - - if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - if (SeqPage::midi_device == DEVICE_MD) { - mcl_seq.md_tracks[last_md_track].clear_track(); - } -#ifdef EXT_TRACKS - else { - mcl_seq.ext_tracks[last_ext_track].clear_track(); - } -#endif - return true; - } - if (SeqPage::handleEvent(event)) { return true; } diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 1cc1b696f..9ac4d1a22 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -21,8 +21,10 @@ void SeqStepPage::config() { strncat(info1, ">", len1); m_strncpy_p(buf, str2, len1); strncat(info1, buf, len1); - strcpy(info2, "NOTE"); + + // config menu + config_as_trackedit(); } void SeqStepPage::init() { @@ -119,7 +121,8 @@ void SeqStepPage::display() { } #else void SeqStepPage::display() { - SeqPage::display(); + oled_display.clearDisplay(); + auto *oldfont = oled_display.getFont(); draw_knob_frame(); @@ -172,8 +175,9 @@ void SeqStepPage::display() { draw_lock_mask((page_select * 16), DEVICE_MD); draw_pattern_mask((page_select * 16), DEVICE_MD); + SeqPage::display(); oled_display.display(); - oled_display.setFont(); + oled_display.setFont(oldfont); } #endif @@ -343,17 +347,6 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { return true; } - if ((EVENT_PRESSED(event, Buttons.BUTTON1) && BUTTON_DOWN(Buttons.BUTTON4)) || - (EVENT_PRESSED(event, Buttons.BUTTON4) && BUTTON_DOWN(Buttons.BUTTON3))) { - for (uint8_t n = 0; n < 16; n++) { - mcl_seq.md_tracks[n].clear_track(); - } - return true; - } - if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - active_track.clear_track(); - return true; - } #ifdef EXT_TRACKS if (EVENT_RELEASED(event, Buttons.BUTTON1)) { GUI.setPage(&seq_extstep_page); From 91a31e3285944e014434e461835aa53bc467e129 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 18:08:27 +0800 Subject: [PATCH 290/469] add sound icon to pageselectpage --- avr/cores/megacommand/MCL/MCLGUI.cpp | 9 +++++++++ avr/cores/megacommand/MCL/MCLGUI.h | 2 ++ avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index cebde7211..6f0570a2c 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -785,6 +785,15 @@ const unsigned char icon_route[] PROGMEM = { 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x80, 0x19, 0x8c, 0xc0, 0x0f, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +// 'sound', 24x19px +const unsigned char icon_sound[] PROGMEM = { + 0x00, 0x03, 0xe0, 0x00, 0x3f, 0xe0, 0x01, 0xff, 0xe0, 0x01, 0xfc, 0xe0, + 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, + 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc3, 0xe0, 0x01, 0xc7, 0xe0, + 0x01, 0xc7, 0xe0, 0x07, 0xc7, 0xc0, 0x0f, 0xc3, 0x80, 0x0f, 0xc0, 0x00, + 0x0f, 0x80, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + // 'md_rev', 34x24px const unsigned char icon_md[] PROGMEM = { 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x00, 0x00, 0x00, diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 3487a8683..bfc55e451 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -226,6 +226,8 @@ extern const unsigned char icon_gatebox[]; extern const unsigned char icon_rhytmecho []; // 'route', 24x16px extern const unsigned char icon_route []; +// 'sound', 24x19px +extern const unsigned char icon_sound[]; // 'md_rev', 34x24px extern const unsigned char icon_md[]; // 'a4_rev', 34x24px diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 09d0dc7ac..4dca5d1d5 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -36,7 +36,7 @@ const PageSelectEntry Entries[] PROGMEM = { {"LOCKS", &seq_param_page[0], 6, 1, 24, 19, (uint8_t *)icon_para}, {"CHROMA", &seq_ptc_page, 7, 1, 24, 25, (uint8_t *)icon_chroma}, - {"SOUND MANAGER", &sound_browser, 8, 2, 0, 0, nullptr}, + {"SOUND MANAGER", &sound_browser, 8, 2, 24, 19, (uint8_t *)icon_sound}, {"WAV DESIGNER", &wd.pages[0], 9, 2, 24, 19, (uint8_t*)icon_wavd}, {"LOUDNESS", &loudness_page, 10, 2, 24, 16, (uint8_t*)icon_loudness}, From 5d24117cad3894deb2759d0db8c83208d6f63152 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 18:41:30 +0800 Subject: [PATCH 291/469] cancel seq page encoder interference with the menu --- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 92 ++++++++++---------- avr/cores/megacommand/MCL/SeqParamPage.cpp | 84 +++++++++--------- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 64 +++++++------- avr/cores/megacommand/MCL/SeqRlckPage.cpp | 18 ++-- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 18 ++-- avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 +- 6 files changed, 139 insertions(+), 139 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index 1023805b8..a434d0aef 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -4,7 +4,7 @@ void SeqExtStepPage::setup() { SeqPage::setup(); } void SeqExtStepPage::config() { #ifdef EXT_TRACKS - encoders[2]->cur = mcl_seq.ext_tracks[last_ext_track].length; + seq_param3.cur = mcl_seq.ext_tracks[last_ext_track].length; #endif // config info labels constexpr uint8_t len1 = sizeof(info1); @@ -26,13 +26,13 @@ void SeqExtStepPage::config() { void SeqExtStepPage::config_encoders() { #ifdef EXT_TRACKS if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { - ((MCLEncoder *)encoders[1])->cur = 6; - ((MCLEncoder *)encoders[1])->max = 11; + seq_param2.cur = 6; + seq_param2.max = 11; } else { - ((MCLEncoder *)encoders[1])->cur = 12; - ((MCLEncoder *)encoders[1])->max = 23; + seq_param2.cur = 12; + seq_param2.max = 23; } - ((MCLEncoder *)encoders[2])->max = 128; + seq_param3.max = 128; config(); SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); #endif @@ -61,48 +61,48 @@ void SeqExtStepPage::display() { char c[3] = "--"; - if (encoders[0]->getValue() == 0) { + if (seq_param1.getValue() == 0) { GUI.put_string_at(0, "L1"); - } else if (encoders[0]->getValue() <= 8) { + } else if (seq_param1.getValue() <= 8) { GUI.put_string_at(0, "L"); - GUI.put_value_at1(1, encoders[0]->getValue()); + GUI.put_value_at1(1, seq_param1.getValue()); } else { GUI.put_string_at(0, "P"); uint8_t prob[5] = {1, 2, 5, 7, 9}; - GUI.put_value_at1(1, prob[encoders[0]->getValue() - 9]); + GUI.put_value_at1(1, prob[seq_param1.getValue() - 9]); } // Cond - // GUI.put_value_at2(0, encoders[1]->getValue()); + // GUI.put_value_at2(0, seq_param2.getValue()); // Pos // 0 1 2 3 4 5 6 7 8 9 10 11 // -5 -4 -3 -2 -1 0 #ifdef EXT_TRACKS if (mcl_seq.ext_tracks[last_ext_track].resolution == 1) { - if (encoders[1]->getValue() == 0) { + if (seq_param2.getValue() == 0) { GUI.put_string_at(2, "--"); - } else if ((encoders[1]->getValue() < 6) && - (encoders[1]->getValue() != 0)) { + } else if ((seq_param2.getValue() < 6) && + (seq_param2.getValue() != 0)) { GUI.put_string_at(2, "-"); - GUI.put_value_at1(3, encoders[1]->getValue() - 6); + GUI.put_value_at1(3, seq_param2.getValue() - 6); } else { GUI.put_string_at(2, "+"); - GUI.put_value_at1(3, encoders[1]->getValue() - 6); + GUI.put_value_at1(3, seq_param2.getValue() - 6); } } else { - if (encoders[1]->getValue() == 0) { + if (seq_param2.getValue() == 0) { GUI.put_string_at(2, "--"); - } else if ((encoders[1]->getValue() < 12) && - (encoders[1]->getValue() != 0)) { + } else if ((seq_param2.getValue() < 12) && + (seq_param2.getValue() != 0)) { GUI.put_string_at(2, "-"); - GUI.put_value_at1(3, 12 - encoders[1]->getValue()); + GUI.put_value_at1(3, 12 - seq_param2.getValue()); } else { GUI.put_string_at(2, "+"); - GUI.put_value_at1(3, encoders[1]->getValue() - 12); + GUI.put_value_at1(3, seq_param2.getValue() - 12); } } @@ -139,9 +139,9 @@ void SeqExtStepPage::display() { } } else { GUI.put_value_at1(15, page_select + 1); - GUI.put_value_at(6, encoders[2]->getValue()); + GUI.put_value_at(6, seq_param3.getValue()); - GUI.put_value_at(6, (encoders[2]->getValue() / + GUI.put_value_at(6, (seq_param3.getValue() / (2 / mcl_seq.ext_tracks[last_ext_track].resolution))); if (Analog4.connected) { GUI.put_string_at(10, "A4T"); @@ -161,16 +161,16 @@ void SeqExtStepPage::display() { draw_knob_frame(); char K[4]; - if (encoders[0]->getValue() == 0) { + if (seq_param1.getValue() == 0) { strcpy(K, "L1"); - } else if (encoders[0]->getValue() <= 8) { + } else if (seq_param1.getValue() <= 8) { strcpy(K, "L "); - K[1] = encoders[0]->getValue() + '0'; - } else if (encoders[0]->getValue() <= 13) { + K[1] = seq_param1.getValue() + '0'; + } else if (seq_param1.getValue() <= 13) { strcpy(K, "P "); uint8_t prob[5] = {1, 2, 5, 7, 9}; - K[1] = prob[encoders[0]->getValue() - 9] + '0'; - } else if (encoders[0]->getValue() == 14) { + K[1] = prob[seq_param1.getValue() - 9] + '0'; + } else if (seq_param1.getValue() == 14) { strcpy(K, "1S"); } draw_knob(0, "COND", K); @@ -180,23 +180,23 @@ void SeqExtStepPage::display() { strcpy(K, "--"); K[3] = '\0'; if (active_track.resolution == 1) { - if (encoders[1]->getValue() == 0) { - } else if ((encoders[1]->getValue() < 6) && - (encoders[1]->getValue() != 0)) { - itoa(6 - encoders[1]->getValue(), K + 1, 10); + if (seq_param2.getValue() == 0) { + } else if ((seq_param2.getValue() < 6) && + (seq_param2.getValue() != 0)) { + itoa(6 - seq_param2.getValue(), K + 1, 10); } else { K[0] = '+'; - itoa(encoders[1]->getValue() - 6, K + 1, 10); + itoa(seq_param2.getValue() - 6, K + 1, 10); } } else { - if (encoders[1]->getValue() == 0) { - } else if ((encoders[1]->getValue() < 12) && - (encoders[1]->getValue() != 0)) { - itoa(12 - encoders[1]->getValue(), K + 1, 10); + if (seq_param2.getValue() == 0) { + } else if ((seq_param2.getValue() < 12) && + (seq_param2.getValue() != 0)) { + itoa(12 - seq_param2.getValue(), K + 1, 10); } else { K[0] = '+'; - itoa(encoders[1]->getValue() - 12, K + 1, 10); + itoa(seq_param2.getValue() - 12, K + 1, 10); } } draw_knob(1, "UTIM", K); @@ -210,7 +210,7 @@ void SeqExtStepPage::display() { } } - itoa(encoders[2]->getValue() / (2 / active_track.resolution), K, 10); + itoa(seq_param3.getValue() / (2 / active_track.resolution), K, 10); draw_knob(2, "LEN", K); if (notes_held > 0) { @@ -288,18 +288,18 @@ bool SeqExtStepPage::handleEvent(gui_event_t *event) { active_track.timing[(track + (page_select * 16))]; // upper uint8_t condition = active_track.conditional[(track + (page_select * 16))]; // lower - encoders[0]->cur = condition; + seq_param1.cur = condition; // Micro if (utiming == 0) { if (active_track.resolution == 1) { utiming = 6; - ((MCLEncoder *)encoders[1])->max = 11; + seq_param2.max = 11; } else { - ((MCLEncoder *)encoders[1])->max = 23; + seq_param2.max = 23; utiming = 12; } } - encoders[1]->cur = utiming; + seq_param2.cur = utiming; note_interface.last_note = track; } @@ -307,8 +307,8 @@ bool SeqExtStepPage::handleEvent(gui_event_t *event) { if (mask == EVENT_BUTTON_RELEASED) { if (device == DEVICE_MD) { - uint8_t utiming = (encoders[1]->cur + 0); - uint8_t condition = encoders[0]->cur; + uint8_t utiming = (seq_param2.cur + 0); + uint8_t condition = seq_param1.cur; if ((track + (page_select * 16)) >= active_track.length) { return true; } diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 3ca84095e..521a3cadb 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -32,26 +32,26 @@ void SeqParamPage::init() { md_exploit.on(); note_interface.state = true; - ((MCLEncoder *)encoders[0])->max = 24; - ((MCLEncoder *)encoders[1])->max = 128; - ((MCLEncoder *)encoders[2])->max = 24; - ((MCLEncoder *)encoders[3])->max = 128; + seq_param1.max = 24; + seq_param2.max = 128; + seq_param3.max = 24; + seq_param4.max = 128; - ((MCLEncoder *)encoders[2])->handler = NULL; + seq_param3.handler = NULL; - encoders[0]->cur = mcl_seq.md_tracks[last_md_track].locks_params[p1]; - encoders[2]->cur = mcl_seq.md_tracks[last_md_track].locks_params[p2]; - encoders[1]->cur = + seq_param1.cur = mcl_seq.md_tracks[last_md_track].locks_params[p1]; + seq_param3.cur = mcl_seq.md_tracks[last_md_track].locks_params[p2]; + seq_param2.cur = MD.kit.params[last_md_track] [mcl_seq.md_tracks[last_md_track].locks_params[p1]]; - encoders[3]->cur = + seq_param4.cur = MD.kit.params[last_md_track] [mcl_seq.md_tracks[last_md_track].locks_params[p2]]; // Prevent hasChanged from being called - encoders[0]->old = encoders[0]->cur; - encoders[1]->old = encoders[1]->cur; - encoders[2]->old = encoders[2]->cur; - encoders[3]->old = encoders[3]->cur; + seq_param1.old = seq_param1.cur; + seq_param2.old = seq_param2.cur; + seq_param3.old = seq_param3.cur; + seq_param4.old = seq_param4.cur; midi_events.setup_callbacks(); #ifdef OLED_DISPLAY @@ -74,37 +74,37 @@ void SeqParamPage::display() { GUI.setLine(GUI.LINE1); char myName[4] = "-- "; char myName2[4] = "-- "; - if (encoders[0]->getValue() == 0) { + if (seq_param1.getValue() == 0) { GUI.put_string_at(0, "--"); } else { PGM_P modelname = NULL; modelname = model_param_name(MD.kit.models[last_md_track], - encoders[0]->getValue() - 1); + seq_param1.getValue() - 1); if (modelname != NULL) { m_strncpy_p(myName, modelname, 4); } GUI.put_string_at(0, myName); } - if (encoders[1]->getValue() == 0) { + if (seq_param2.getValue() == 0) { GUI.put_string_at(4, "--"); } else { - GUI.put_value_at2(4, encoders[1]->getValue() - 1); + GUI.put_value_at2(4, seq_param2.getValue() - 1); } - if (encoders[2]->getValue() == 0) { + if (seq_param3.getValue() == 0) { GUI.put_string_at(7, "--"); } else { PGM_P modelname = NULL; modelname = model_param_name(MD.kit.models[last_md_track], - encoders[2]->getValue() - 1); + seq_param3.getValue() - 1); if (modelname != NULL) { m_strncpy_p(myName2, modelname, 4); } GUI.put_string_at(7, myName2); } - if (encoders[3]->getValue() == 0) { + if (seq_param4.getValue() == 0) { GUI.put_string_at(11, "--"); } else { - GUI.put_value_at2(11, encoders[3]->getValue() - 1); + GUI.put_value_at2(11, seq_param4.getValue() - 1); } if (page_id == 0) { GUI.put_string_at(14, "A"); @@ -126,19 +126,19 @@ void SeqParamPage::display() { char myName[4] = "-- "; char myName2[4] = "-- "; - if (encoders[0]->getValue() != 0) { + if (seq_param1.getValue() != 0) { PGM_P modelname = NULL; modelname = model_param_name(MD.kit.models[last_md_track], - encoders[0]->getValue() - 1); + seq_param1.getValue() - 1); if (modelname != NULL) { m_strncpy_p(myName, modelname, 4); } } - if (encoders[2]->getValue() != 0) { + if (seq_param3.getValue() != 0) { PGM_P modelname = NULL; modelname = model_param_name(MD.kit.models[last_md_track], - encoders[2]->getValue() - 1); + seq_param3.getValue() - 1); if (modelname != NULL) { m_strncpy_p(myName2, modelname, 4); } @@ -147,8 +147,8 @@ void SeqParamPage::display() { draw_knob(0, "TGT", myName); draw_knob(2, "TGT", myName2); - draw_knob(1, encoders[1], "VAL"); - draw_knob(3, encoders[3], "VAL"); + draw_knob(1, &seq_param2, "VAL"); + draw_knob(3, &seq_param4, "VAL"); draw_pattern_mask(page_select * 16, DEVICE_MD); draw_lock_mask(page_select * 16); @@ -160,8 +160,8 @@ void SeqParamPage::display() { #endif void SeqParamPage::loop() { - if (encoders[0]->hasChanged() || encoders[1]->hasChanged() || - encoders[2]->hasChanged() || encoders[3]->hasChanged()) { + if (seq_param1.hasChanged() || seq_param2.hasChanged() || + seq_param3.hasChanged() || seq_param4.hasChanged()) { for (uint8_t n = 0; n < 16; n++) { if (note_interface.notes[n] == 1) { @@ -178,14 +178,14 @@ void SeqParamPage::loop() { } SET_BIT64(mcl_seq.md_tracks[last_md_track].lock_mask, step); - mcl_seq.md_tracks[last_md_track].locks[p1][step] = encoders[1]->cur; - mcl_seq.md_tracks[last_md_track].locks[p2][step] = encoders[3]->cur; + mcl_seq.md_tracks[last_md_track].locks[p1][step] = seq_param2.cur; + mcl_seq.md_tracks[last_md_track].locks[p2][step] = seq_param4.cur; } } - if (encoders[0]->hasChanged() || encoders[2]->hasChanged()) { + if (seq_param1.hasChanged() || seq_param3.hasChanged()) { mcl_seq.md_tracks[last_md_track].reset_params(); - mcl_seq.md_tracks[last_md_track].locks_params[p1] = encoders[0]->cur; - mcl_seq.md_tracks[last_md_track].locks_params[p2] = encoders[2]->cur; + mcl_seq.md_tracks[last_md_track].locks_params[p1] = seq_param1.cur; + mcl_seq.md_tracks[last_md_track].locks_params[p2] = seq_param3.cur; mcl_seq.md_tracks[last_md_track].update_params(); } } @@ -210,11 +210,11 @@ bool SeqParamPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_PRESSED) { uint8_t param_offset; - encoders[0]->cur = mcl_seq.md_tracks[last_md_track].locks_params[p1]; - encoders[2]->cur = mcl_seq.md_tracks[last_md_track].locks_params[p2]; + seq_param1.cur = mcl_seq.md_tracks[last_md_track].locks_params[p1]; + seq_param3.cur = mcl_seq.md_tracks[last_md_track].locks_params[p2]; - encoders[1]->cur = mcl_seq.md_tracks[last_md_track].locks[p1][step]; - encoders[3]->cur = mcl_seq.md_tracks[last_md_track].locks[p2][step]; + seq_param2.cur = mcl_seq.md_tracks[last_md_track].locks[p1][step]; + seq_param4.cur = mcl_seq.md_tracks[last_md_track].locks[p2][step]; } if (event->mask == EVENT_BUTTON_RELEASED) { if (device == DEVICE_A4) { @@ -241,13 +241,13 @@ if (utiming == 0) { } /* mcl_seq.md_tracks[last_md_track].locks[p1][step] = - encoders[1]->cur; + seq_param2.cur; mcl_seq.md_tracks[last_md_track].locks[p2][step] = - encoders[3]->cur; + seq_param4.cur; mcl_seq.md_tracks[last_md_track].locks_params[p1] = - encoders[0]->cur; mcl_seq.md_tracks[last_md_track].locks_params[p2] = - encoders[2]->cur; + seq_param1.cur; mcl_seq.md_tracks[last_md_track].locks_params[p2] = + seq_param3.cur; */ } return true; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 2ee6a99ff..72630b9e2 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -60,14 +60,14 @@ void SeqPtcPage::cleanup() { } void SeqPtcPage::config_encoders() { if (midi_device == DEVICE_MD) { - ((MCLEncoder *)encoders[2])->max = 64; + seq_param3.max = 64; encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; } #ifdef EXT_TRACKS else { - ((MCLEncoder *)encoders[2])->max = (uint8_t)128; - ((MCLEncoder *)encoders[2])->cur = + seq_param3.max = (uint8_t)128; + seq_param3.cur = mcl_seq.ext_tracks[last_ext_track].length; } #endif @@ -96,7 +96,7 @@ void SeqPtcPage::init_poly() { void SeqPtcPage::init() { DEBUG_PRINT_FN(); SeqPage::init(); - ((MCLEncoder *)encoders[2])->handler = ptc_pattern_len_handler; + seq_param3.handler = ptc_pattern_len_handler; recording = false; midi_events.setup_callbacks(); note_mask = 0; @@ -116,8 +116,8 @@ void SeqPtcPage::init() { void SeqPtcPage::config() { config_encoders(); - encoders[1]->cur = 32; - encoders[0]->cur = 1; + seq_param2.cur = 32; + seq_param1.cur = 1; // config info labels const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); @@ -178,12 +178,12 @@ void ptc_pattern_len_handler(Encoder *enc) { void SeqPtcPage::loop() { #ifdef EXT_TRACKS - if (encoders[0]->hasChanged() || encoders[3]->hasChanged()) { + if (seq_param1.hasChanged() || seq_param4.hasChanged()) { mcl_seq.ext_tracks[last_ext_track].buffer_notesoff(); } #endif - if (encoders[0]->hasChanged() || encoders[1]->hasChanged() || encoders[2]->hasChanged() || encoders[3]->hasChanged()) { + if (seq_param1.hasChanged() || seq_param2.hasChanged() || seq_param3.hasChanged() || seq_param4.hasChanged()) { queue_redraw(); } @@ -222,13 +222,13 @@ void SeqPtcPage::display() { GUI.put_string_at(0, "PTC"); } if (midi_device == DEVICE_MD) { - GUI.put_value_at(5, encoders[2]->getValue()); + GUI.put_value_at(5, seq_param3.getValue()); GUI.put_p_string_at(9, str1); GUI.put_p_string_at(11, str2); } #ifdef EXT_TRACKS else { - GUI.put_value_at(5, (encoders[2]->getValue() / + GUI.put_value_at(5, (seq_param3.getValue() / (2 / mcl_seq.ext_tracks[last_ext_track].resolution))); if (Analog4.connected) { GUI.put_string_at(9, "A4T"); @@ -241,15 +241,15 @@ void SeqPtcPage::display() { GUI.setLine(GUI.LINE2); GUI.put_string_at(0, "OC:"); - GUI.put_value_at2(3, encoders[0]->getValue()); + GUI.put_value_at2(3, seq_param1.getValue()); - if (encoders[1]->getValue() < 32) { + if (seq_param2.getValue() < 32) { GUI.put_string_at(6, "F:-"); - GUI.put_value_at2(9, 32 - encoders[1]->getValue()); + GUI.put_value_at2(9, 32 - seq_param2.getValue()); - } else if (encoders[1]->getValue() > 32) { + } else if (seq_param2.getValue() > 32) { GUI.put_string_at(6, "F:+"); - GUI.put_value_at2(9, encoders[1]->getValue() - 32); + GUI.put_value_at2(9, seq_param2.getValue() - 32); } else { GUI.put_string_at(6, "F: 0"); @@ -257,7 +257,7 @@ void SeqPtcPage::display() { GUI.put_string_at(12, "S:"); - GUI.put_value_at2(14, encoders[3]->getValue()); + GUI.put_value_at2(14, seq_param4.getValue()); SeqPage::display(); } #else @@ -283,16 +283,16 @@ void SeqPtcPage::display() { char buf1[4]; // draw OCTAVE - itoa(encoders[0]->getValue(), buf1, 10); + itoa(seq_param1.getValue(), buf1, 10); draw_knob(0, "OCT", buf1); // draw FREQ - if (encoders[1]->getValue() < 32) { + if (seq_param2.getValue() < 32) { strcpy(buf1, "-"); - itoa(32 - encoders[1]->getValue(), buf1 + 1, 10); - } else if (encoders[1]->getValue() > 32) { + itoa(32 - seq_param2.getValue(), buf1 + 1, 10); + } else if (seq_param2.getValue() > 32) { strcpy(buf1, "+"); - itoa(encoders[1]->getValue() - 32, buf1 + 1, 10); + itoa(seq_param2.getValue() - 32, buf1 + 1, 10); } else { strcpy(buf1, "0"); } @@ -300,12 +300,12 @@ void SeqPtcPage::display() { // draw LEN if (midi_device == DEVICE_MD) { - itoa(encoders[2]->getValue(), buf1, 10); + itoa(seq_param3.getValue(), buf1, 10); draw_knob(2, "LEN", buf1); } #ifdef EXT_TRACKS else { - itoa(encoders[2]->getValue() / + itoa(seq_param3.getValue() / (2 / mcl_seq.ext_tracks[last_ext_track].resolution), buf1, 10); draw_knob(2, "LEN", buf1); @@ -313,7 +313,7 @@ void SeqPtcPage::display() { #endif // draw SCALE - m_strncpy_p(buf1, scale_names[encoders[3]->getValue()], 4); + m_strncpy_p(buf1, scale_names[seq_param4.getValue()], 4); draw_knob(3, "SCA", buf1); // draw TI keyboard @@ -327,11 +327,11 @@ void SeqPtcPage::display() { #endif uint8_t SeqPtcPage::calc_pitch(uint8_t note_num) { - uint8_t size = scales[encoders[3]->cur]->size; + uint8_t size = scales[seq_param4.cur]->size; uint8_t oct = note_num / size; note_num = note_num - oct * size; - return scales[encoders[3]->cur]->pitches[note_num] + oct * 12; + return scales[seq_param4.cur]->pitches[note_num] + oct * 12; } uint8_t SeqPtcPage::get_next_voice(uint8_t pitch) { @@ -384,7 +384,7 @@ uint8_t SeqPtcPage::get_machine_pitch(uint8_t track, uint8_t pitch) { } uint8_t machine_pitch = - pgm_read_byte(&tuning->tuning[pitch]) + encoders[1]->getValue() - 32; + pgm_read_byte(&tuning->tuning[pitch]) + seq_param2.getValue() - 32; return machine_pitch; } @@ -513,15 +513,15 @@ uint8_t SeqPtcPage::seq_ext_pitch(uint8_t note_num) { uint8_t root_note = (note_num / 12) * 12; uint8_t pos = note_num - root_note; uint8_t oct = note_num / 12; - // if (pos >= scales[encoders[4]->cur]->size) { - oct += pos / scales[encoders[3]->cur]->size; + // if (pos >= scales[seq_param5.cur]->size) { + oct += pos / scales[seq_param4.cur]->size; pos = pos - - scales[encoders[3]->cur]->size * (pos / scales[encoders[3]->cur]->size); + scales[seq_param4.cur]->size * (pos / scales[seq_param4.cur]->size); // } - // if (encoders[4]->getValue() > 0) { + // if (seq_param5.getValue() > 0) { pitch = octave_to_pitch() + - scales[encoders[3]->cur]->pitches[pos] + oct * 12; + scales[seq_param4.cur]->pitches[pos] + oct * 12; // } return pitch; diff --git a/avr/cores/megacommand/MCL/SeqRlckPage.cpp b/avr/cores/megacommand/MCL/SeqRlckPage.cpp index c6619fc07..ee120c916 100644 --- a/avr/cores/megacommand/MCL/SeqRlckPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRlckPage.cpp @@ -34,11 +34,11 @@ void SeqRlckPage::init() { recording = true; config(); - ((MCLEncoder *)encoders[0])->max = 4; - ((MCLEncoder *)encoders[1])->max = 64; - ((MCLEncoder *)encoders[2])->max = 64; - ((MCLEncoder *)encoders[3])->max = 11; - encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; + seq_param1.max = 4; + seq_param2.max = 64; + seq_param3.max = 64; + seq_param4.max = 11; + seq_param3.cur = mcl_seq.md_tracks[last_md_track].length; curpage = SEQ_RTRK_PAGE; midi_events.setup_callbacks(); @@ -67,11 +67,11 @@ void SeqRlckPage::display() { if (SeqPage::midi_device == DEVICE_MD) { GUI.put_p_string_at(9, str1); GUI.put_p_string_at(11, str2); - GUI.put_value_at(5, encoders[2]->getValue()); + GUI.put_value_at(5, seq_param3.getValue()); } #ifdef EXT_TRACKS else { - GUI.put_value_at(5, (encoders[2]->getValue() / + GUI.put_value_at(5, (seq_param3.getValue() / (2 / mcl_seq.ext_tracks[last_ext_track].resolution))); if (Analog4.connected) { GUI.put_string_at(9, "A4T"); @@ -96,7 +96,7 @@ void SeqRlckPage::display() { draw_knob_frame(); - uint8_t len = encoders[2]->getValue(); + uint8_t len = seq_param3.getValue(); #ifdef EXT_TRACKS if (SeqPage::midi_device != DEVICE_MD) { len = len / (2 / mcl_seq.ext_tracks[last_ext_track].resolution); @@ -170,7 +170,7 @@ void SeqRlckPageMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { last_md_track = track; //ignore level if (track_param > 31) { return; } - seq_rlck_page.encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; + seq_param3.cur = mcl_seq.md_tracks[last_md_track].length; mcl_seq.md_tracks[track].update_param(track_param, value); diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 2a04dd6ac..0647b3af7 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -27,11 +27,11 @@ void SeqRtrkPage::init() { note_interface.state = true; - ((MCLEncoder *)encoders[0])->max = 4; - ((MCLEncoder *)encoders[1])->max = 64; - ((MCLEncoder *)encoders[2])->max = 64; - ((MCLEncoder *)encoders[3])->max = 11; - encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; + seq_param1.max = 4; + seq_param2.max = 64; + seq_param3.max = 64; + seq_param4.max = 11; + seq_param3.cur = mcl_seq.md_tracks[last_md_track].length; midi_device = DEVICE_MD; curpage = SEQ_RTRK_PAGE; recording = true; @@ -56,11 +56,11 @@ void SeqRtrkPage::display() { if (SeqPage::midi_device == DEVICE_MD) { GUI.put_p_string_at(9, str1); GUI.put_p_string_at(11, str2); - GUI.put_value_at(5, encoders[2]->getValue()); + GUI.put_value_at(5, seq_param3.getValue()); } #ifdef EXT_TRACKS else { - GUI.put_value_at(5, (encoders[2]->getValue() / + GUI.put_value_at(5, (seq_param3.getValue() / (2 / mcl_seq.ext_tracks[last_ext_track].resolution))); if (Analog4.connected) { GUI.put_string_at(9, "A4T"); @@ -82,7 +82,7 @@ void SeqRtrkPage::display() { auto *oldfont = oled_display.getFont(); draw_knob_frame(); - uint8_t len = encoders[2]->getValue(); + uint8_t len = seq_param3.getValue(); #ifdef EXT_TRACKS if (SeqPage::midi_device != DEVICE_MD) { len = len / (2 / mcl_seq.ext_tracks[last_ext_track].resolution); @@ -117,7 +117,7 @@ bool SeqRtrkPage::handleEvent(gui_event_t *event) { if (device != DEVICE_MD) { return true; } last_md_track = track; - encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; + seq_param3.cur = mcl_seq.md_tracks[last_md_track].length; MD.triggerTrack(track, 127); if (MidiClock.state == 2) { mcl_seq.md_tracks[last_md_track].record_track(track, 127); diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 9ac4d1a22..5f217c671 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -247,7 +247,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { return true; } - ((MCLEncoder *)encoders[1])->max = 23; + seq_param2.max = 23; int8_t utiming = active_track.timing[step]; // upper uint8_t condition = active_track.conditional[step]; // lower uint8_t pitch = active_track.get_track_lock(step, 0) - 1; From 4fdaef88e116a2d5c87750b5ac399e682db570e4 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 19:32:48 +0800 Subject: [PATCH 292/469] cleanup. --- avr/cores/megacommand/MCL/SeqPage.cpp | 21 +++++--- avr/cores/megacommand/MCL/SeqPages.cpp | 4 +- avr/cores/megacommand/MCL/SeqPages.h | 5 ++ avr/cores/megacommand/MCL/SeqParamPage.cpp | 40 +++++++------- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 63 +++++++++++----------- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 3 ++ 6 files changed, 75 insertions(+), 61 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 40b50c5d1..666a96b3c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -10,7 +10,7 @@ uint8_t SeqPage::page_count = 4; bool SeqPage::show_seq_menu = false; uint8_t opt_resolution = 0; -uint8_t opt_trackid = 0; +uint8_t opt_trackid = 1; static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; @@ -73,8 +73,9 @@ bool SeqPage::handleEvent(gui_event_t *event) { if (show_seq_menu) { // TI + SHIFT2 = select track. if (BUTTON_DOWN(Buttons.BUTTON3)) { - opt_trackid = track; + opt_trackid = track + 1; select_track(device, track); + redisplay = true; } return true; @@ -154,10 +155,10 @@ bool SeqPage::handleEvent(gui_event_t *event) { if (opt_midi_device_capture == DEVICE_MD) { DEBUG_PRINTLN("okay using MD for length update"); - opt_trackid = last_md_track; + opt_trackid = last_md_track + 1; opt_resolution = (mcl_seq.md_tracks[last_md_track].resolution); } else { - opt_trackid = last_ext_track; + opt_trackid = last_ext_track + 1; opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); } @@ -467,7 +468,7 @@ void pattern_len_handler(Encoder *enc) { } if (SeqPage::midi_device == DEVICE_MD) { DEBUG_PRINTLN("under 16"); - if (BUTTON_DOWN(Buttons.BUTTON3)) { + if (BUTTON_DOWN(Buttons.BUTTON4)) { for (uint8_t c = 0; c < 16; c++) { mcl_seq.md_tracks[c].set_length(enc_->cur); } @@ -477,7 +478,7 @@ void pattern_len_handler(Encoder *enc) { } #ifdef EXT_TRACKS else { - if (BUTTON_DOWN(Buttons.BUTTON3)) { + if (BUTTON_DOWN(Buttons.BUTTON4)) { for (uint8_t c = 0; c < mcl_seq.num_ext_tracks; c++) { mcl_seq.ext_tracks[c].buffer_notesoff(); mcl_seq.ext_tracks[c].set_length(enc_->cur); @@ -491,7 +492,7 @@ void pattern_len_handler(Encoder *enc) { } void opt_trackid_handler() { - opt_seqpage_capture->select_track(opt_midi_device_capture, opt_trackid); + opt_seqpage_capture->select_track(opt_midi_device_capture, opt_trackid - 1); } void opt_resolution_handler() { @@ -545,6 +546,9 @@ void opt_clear_all_locks_handler() { } void SeqPage::config_as_trackedit() { + + toggleLed(); + seq_menu_page.menu.enable_entry(2, true); seq_menu_page.menu.enable_entry(3, false); @@ -553,6 +557,9 @@ void SeqPage::config_as_trackedit() { } void SeqPage::config_as_lockedit() { + + toggleLed2(); + seq_menu_page.menu.enable_entry(2, false); seq_menu_page.menu.enable_entry(3, true); diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 382b9e5cb..eb6e3c765 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -29,12 +29,12 @@ SeqPtcPage seq_ptc_page(&ptc_param_oct, &ptc_param_finetune, &ptc_param_len, &pt const menu_t<9> seq_menu_layout PROGMEM = { "SEQ", { - {"TRACK SEL.", 0, 16, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, + {"TRACK SEL.", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, {"COPY TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, {"CLEAR TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_track_handler, {}}, {"CLEAR LCKS.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_locks_handler, {}}, {"PASTE TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"TRACK RES.", 1, 2, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {0, "1x"}, {1, "2x"} }}, + {"TRACK RES.", 0, 2, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {0, "1x"}, {1, "2x"} }}, {"STEP SHIFT", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, // clear all tracks {"CLEAR ALL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_all_tracks_handler, {}}, diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index cb4ee394c..89d65d334 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -46,6 +46,11 @@ extern SeqRlckPage seq_rlck_page; extern SeqExtStepPage seq_extstep_page; #endif +extern MCLEncoder ptc_param_oct; +extern MCLEncoder ptc_param_finetune; +extern MCLEncoder ptc_param_len; +extern MCLEncoder ptc_param_scale; + extern SeqPtcPage seq_ptc_page; extern MCLEncoder seq_menu_value_encoder; diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 521a3cadb..500eee023 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -33,25 +33,25 @@ void SeqParamPage::init() { note_interface.state = true; seq_param1.max = 24; - seq_param2.max = 128; + seq_lock1.max = 128; seq_param3.max = 24; - seq_param4.max = 128; + seq_lock2.max = 128; seq_param3.handler = NULL; seq_param1.cur = mcl_seq.md_tracks[last_md_track].locks_params[p1]; seq_param3.cur = mcl_seq.md_tracks[last_md_track].locks_params[p2]; - seq_param2.cur = + seq_lock1.cur = MD.kit.params[last_md_track] [mcl_seq.md_tracks[last_md_track].locks_params[p1]]; - seq_param4.cur = + seq_lock2.cur = MD.kit.params[last_md_track] [mcl_seq.md_tracks[last_md_track].locks_params[p2]]; // Prevent hasChanged from being called seq_param1.old = seq_param1.cur; - seq_param2.old = seq_param2.cur; + seq_lock1.old = seq_lock1.cur; seq_param3.old = seq_param3.cur; - seq_param4.old = seq_param4.cur; + seq_lock2.old = seq_lock2.cur; midi_events.setup_callbacks(); #ifdef OLED_DISPLAY @@ -85,10 +85,10 @@ void SeqParamPage::display() { } GUI.put_string_at(0, myName); } - if (seq_param2.getValue() == 0) { + if (seq_lock1.getValue() == 0) { GUI.put_string_at(4, "--"); } else { - GUI.put_value_at2(4, seq_param2.getValue() - 1); + GUI.put_value_at2(4, seq_lock1.getValue() - 1); } if (seq_param3.getValue() == 0) { GUI.put_string_at(7, "--"); @@ -101,10 +101,10 @@ void SeqParamPage::display() { } GUI.put_string_at(7, myName2); } - if (seq_param4.getValue() == 0) { + if (seq_lock2.getValue() == 0) { GUI.put_string_at(11, "--"); } else { - GUI.put_value_at2(11, seq_param4.getValue() - 1); + GUI.put_value_at2(11, seq_lock2.getValue() - 1); } if (page_id == 0) { GUI.put_string_at(14, "A"); @@ -147,8 +147,8 @@ void SeqParamPage::display() { draw_knob(0, "TGT", myName); draw_knob(2, "TGT", myName2); - draw_knob(1, &seq_param2, "VAL"); - draw_knob(3, &seq_param4, "VAL"); + draw_knob(1, &seq_lock1, "VAL"); + draw_knob(3, &seq_lock2, "VAL"); draw_pattern_mask(page_select * 16, DEVICE_MD); draw_lock_mask(page_select * 16); @@ -160,8 +160,8 @@ void SeqParamPage::display() { #endif void SeqParamPage::loop() { - if (seq_param1.hasChanged() || seq_param2.hasChanged() || - seq_param3.hasChanged() || seq_param4.hasChanged()) { + if (seq_param1.hasChanged() || seq_lock1.hasChanged() || + seq_param3.hasChanged() || seq_lock2.hasChanged()) { for (uint8_t n = 0; n < 16; n++) { if (note_interface.notes[n] == 1) { @@ -178,8 +178,8 @@ void SeqParamPage::loop() { } SET_BIT64(mcl_seq.md_tracks[last_md_track].lock_mask, step); - mcl_seq.md_tracks[last_md_track].locks[p1][step] = seq_param2.cur; - mcl_seq.md_tracks[last_md_track].locks[p2][step] = seq_param4.cur; + mcl_seq.md_tracks[last_md_track].locks[p1][step] = seq_lock1.cur; + mcl_seq.md_tracks[last_md_track].locks[p2][step] = seq_lock2.cur; } } if (seq_param1.hasChanged() || seq_param3.hasChanged()) { @@ -213,8 +213,8 @@ bool SeqParamPage::handleEvent(gui_event_t *event) { seq_param1.cur = mcl_seq.md_tracks[last_md_track].locks_params[p1]; seq_param3.cur = mcl_seq.md_tracks[last_md_track].locks_params[p2]; - seq_param2.cur = mcl_seq.md_tracks[last_md_track].locks[p1][step]; - seq_param4.cur = mcl_seq.md_tracks[last_md_track].locks[p2][step]; + seq_lock1.cur = mcl_seq.md_tracks[last_md_track].locks[p1][step]; + seq_lock2.cur = mcl_seq.md_tracks[last_md_track].locks[p2][step]; } if (event->mask == EVENT_BUTTON_RELEASED) { if (device == DEVICE_A4) { @@ -241,9 +241,9 @@ if (utiming == 0) { } /* mcl_seq.md_tracks[last_md_track].locks[p1][step] = - seq_param2.cur; + seq_lock1.cur; mcl_seq.md_tracks[last_md_track].locks[p2][step] = - seq_param4.cur; + seq_lock2.cur; mcl_seq.md_tracks[last_md_track].locks_params[p1] = seq_param1.cur; mcl_seq.md_tracks[last_md_track].locks_params[p2] = diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 72630b9e2..2cdc90141 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -60,14 +60,13 @@ void SeqPtcPage::cleanup() { } void SeqPtcPage::config_encoders() { if (midi_device == DEVICE_MD) { - seq_param3.max = 64; - - encoders[2]->cur = mcl_seq.md_tracks[last_md_track].length; + ptc_param_len.max = 64; + ptc_param_len.cur = mcl_seq.md_tracks[last_md_track].length; } #ifdef EXT_TRACKS else { - seq_param3.max = (uint8_t)128; - seq_param3.cur = + ptc_param_len.max = (uint8_t)128; + ptc_param_len.cur = mcl_seq.ext_tracks[last_ext_track].length; } #endif @@ -96,7 +95,7 @@ void SeqPtcPage::init_poly() { void SeqPtcPage::init() { DEBUG_PRINT_FN(); SeqPage::init(); - seq_param3.handler = ptc_pattern_len_handler; + ptc_param_len.handler = ptc_pattern_len_handler; recording = false; midi_events.setup_callbacks(); note_mask = 0; @@ -116,8 +115,8 @@ void SeqPtcPage::init() { void SeqPtcPage::config() { config_encoders(); - seq_param2.cur = 32; - seq_param1.cur = 1; + ptc_param_finetune.cur = 32; + ptc_param_oct.cur = 1; // config info labels const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); @@ -178,12 +177,12 @@ void ptc_pattern_len_handler(Encoder *enc) { void SeqPtcPage::loop() { #ifdef EXT_TRACKS - if (seq_param1.hasChanged() || seq_param4.hasChanged()) { + if (ptc_param_oct.hasChanged() || ptc_param_scale.hasChanged()) { mcl_seq.ext_tracks[last_ext_track].buffer_notesoff(); } #endif - if (seq_param1.hasChanged() || seq_param2.hasChanged() || seq_param3.hasChanged() || seq_param4.hasChanged()) { + if (ptc_param_oct.hasChanged() || ptc_param_finetune.hasChanged() || ptc_param_len.hasChanged() || ptc_param_scale.hasChanged()) { queue_redraw(); } @@ -222,13 +221,13 @@ void SeqPtcPage::display() { GUI.put_string_at(0, "PTC"); } if (midi_device == DEVICE_MD) { - GUI.put_value_at(5, seq_param3.getValue()); + GUI.put_value_at(5, ptc_param_len.getValue()); GUI.put_p_string_at(9, str1); GUI.put_p_string_at(11, str2); } #ifdef EXT_TRACKS else { - GUI.put_value_at(5, (seq_param3.getValue() / + GUI.put_value_at(5, (ptc_param_len.getValue() / (2 / mcl_seq.ext_tracks[last_ext_track].resolution))); if (Analog4.connected) { GUI.put_string_at(9, "A4T"); @@ -241,15 +240,15 @@ void SeqPtcPage::display() { GUI.setLine(GUI.LINE2); GUI.put_string_at(0, "OC:"); - GUI.put_value_at2(3, seq_param1.getValue()); + GUI.put_value_at2(3, ptc_param_oct.getValue()); - if (seq_param2.getValue() < 32) { + if (ptc_param_finetune.getValue() < 32) { GUI.put_string_at(6, "F:-"); - GUI.put_value_at2(9, 32 - seq_param2.getValue()); + GUI.put_value_at2(9, 32 - ptc_param_finetune.getValue()); - } else if (seq_param2.getValue() > 32) { + } else if (ptc_param_finetune.getValue() > 32) { GUI.put_string_at(6, "F:+"); - GUI.put_value_at2(9, seq_param2.getValue() - 32); + GUI.put_value_at2(9, ptc_param_finetune.getValue() - 32); } else { GUI.put_string_at(6, "F: 0"); @@ -257,7 +256,7 @@ void SeqPtcPage::display() { GUI.put_string_at(12, "S:"); - GUI.put_value_at2(14, seq_param4.getValue()); + GUI.put_value_at2(14, ptc_param_scale.getValue()); SeqPage::display(); } #else @@ -283,16 +282,16 @@ void SeqPtcPage::display() { char buf1[4]; // draw OCTAVE - itoa(seq_param1.getValue(), buf1, 10); + itoa(ptc_param_oct.getValue(), buf1, 10); draw_knob(0, "OCT", buf1); // draw FREQ - if (seq_param2.getValue() < 32) { + if (ptc_param_finetune.getValue() < 32) { strcpy(buf1, "-"); - itoa(32 - seq_param2.getValue(), buf1 + 1, 10); - } else if (seq_param2.getValue() > 32) { + itoa(32 - ptc_param_finetune.getValue(), buf1 + 1, 10); + } else if (ptc_param_finetune.getValue() > 32) { strcpy(buf1, "+"); - itoa(seq_param2.getValue() - 32, buf1 + 1, 10); + itoa(ptc_param_finetune.getValue() - 32, buf1 + 1, 10); } else { strcpy(buf1, "0"); } @@ -300,12 +299,12 @@ void SeqPtcPage::display() { // draw LEN if (midi_device == DEVICE_MD) { - itoa(seq_param3.getValue(), buf1, 10); + itoa(ptc_param_len.getValue(), buf1, 10); draw_knob(2, "LEN", buf1); } #ifdef EXT_TRACKS else { - itoa(seq_param3.getValue() / + itoa(ptc_param_len.getValue() / (2 / mcl_seq.ext_tracks[last_ext_track].resolution), buf1, 10); draw_knob(2, "LEN", buf1); @@ -313,7 +312,7 @@ void SeqPtcPage::display() { #endif // draw SCALE - m_strncpy_p(buf1, scale_names[seq_param4.getValue()], 4); + m_strncpy_p(buf1, scale_names[ptc_param_scale.getValue()], 4); draw_knob(3, "SCA", buf1); // draw TI keyboard @@ -327,11 +326,11 @@ void SeqPtcPage::display() { #endif uint8_t SeqPtcPage::calc_pitch(uint8_t note_num) { - uint8_t size = scales[seq_param4.cur]->size; + uint8_t size = scales[ptc_param_scale.cur]->size; uint8_t oct = note_num / size; note_num = note_num - oct * size; - return scales[seq_param4.cur]->pitches[note_num] + oct * 12; + return scales[ptc_param_scale.cur]->pitches[note_num] + oct * 12; } uint8_t SeqPtcPage::get_next_voice(uint8_t pitch) { @@ -384,7 +383,7 @@ uint8_t SeqPtcPage::get_machine_pitch(uint8_t track, uint8_t pitch) { } uint8_t machine_pitch = - pgm_read_byte(&tuning->tuning[pitch]) + seq_param2.getValue() - 32; + pgm_read_byte(&tuning->tuning[pitch]) + ptc_param_finetune.getValue() - 32; return machine_pitch; } @@ -514,14 +513,14 @@ uint8_t SeqPtcPage::seq_ext_pitch(uint8_t note_num) { uint8_t pos = note_num - root_note; uint8_t oct = note_num / 12; // if (pos >= scales[seq_param5.cur]->size) { - oct += pos / scales[seq_param4.cur]->size; + oct += pos / scales[ptc_param_scale.cur]->size; pos = pos - - scales[seq_param4.cur]->size * (pos / scales[seq_param4.cur]->size); + scales[ptc_param_scale.cur]->size * (pos / scales[ptc_param_scale.cur]->size); // } // if (seq_param5.getValue() > 0) { pitch = octave_to_pitch() + - scales[seq_param4.cur]->pitches[pos] + oct * 12; + scales[ptc_param_scale.cur]->pitches[pos] + oct * 12; // } return pitch; diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 0647b3af7..6aab2c993 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -20,6 +20,9 @@ void SeqRtrkPage::config() { strcpy(info2, "RTRK"); display_page_index = false; + + // config menu + config_as_trackedit(); } void SeqRtrkPage::init() { From ca1c9f3c1b61757cbf42a26f6f86d59e1cc30214 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 19:49:11 +0800 Subject: [PATCH 293/469] cleanup --- avr/cores/megacommand/MCL/SeqPage.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 666a96b3c..087695a93 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -13,6 +13,8 @@ uint8_t opt_resolution = 0; uint8_t opt_trackid = 1; static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; +static MCLEncoder *opt_param1_capture = nullptr; +static MCLEncoder *opt_param2_capture = nullptr; void SeqPage::create_chars_seq() { uint8_t temp_charmap1[8] = {0, 15, 16, 16, 16, 15, 0}; @@ -162,21 +164,21 @@ bool SeqPage::handleEvent(gui_event_t *event) { opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); } + opt_param1_capture = (MCLEncoder *)encoders[0]; + opt_param2_capture = (MCLEncoder *)encoders[1]; encoders[0] = &seq_menu_value_encoder; encoders[1] = &seq_menu_entry_encoder; seq_menu_page.init(); return true; } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { - encoders[0] = &seq_param1; - encoders[1] = &seq_param2; + encoders[0] = opt_param1_capture; + encoders[1] = opt_param2_capture; oled_display.clearDisplay(); show_seq_menu = false; void (*row_func)() = seq_menu_page.menu.get_row_function(seq_menu_page.encoders[1]->cur); - DEBUG_PRINTLN(seq_menu_page.encoders[1]->cur); if (row_func != NULL) { - DEBUG_PRINTLN("func call"); (*row_func)(); return true; } @@ -547,8 +549,6 @@ void opt_clear_all_locks_handler() { void SeqPage::config_as_trackedit() { - toggleLed(); - seq_menu_page.menu.enable_entry(2, true); seq_menu_page.menu.enable_entry(3, false); @@ -558,8 +558,6 @@ void SeqPage::config_as_trackedit() { void SeqPage::config_as_lockedit() { - toggleLed2(); - seq_menu_page.menu.enable_entry(2, false); seq_menu_page.menu.enable_entry(3, true); @@ -665,7 +663,6 @@ void SeqPage::display() { oled_display.fillRect(128 - width - 2, 0, width + 2, 32, BLACK); seq_menu_page.draw_menu(128 - width, 8, width); } - } #endif From 5a37d854892d56b09094c2f5802eade9eb290cf8 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 21:32:31 +0800 Subject: [PATCH 294/469] lock/chroma: call SeqPage::loop() to get the menu going --- avr/cores/megacommand/MCL/MenuPage.cpp | 29 +++++++++++++--------- avr/cores/megacommand/MCL/SeqPages.cpp | 2 +- avr/cores/megacommand/MCL/SeqParamPage.cpp | 3 +++ avr/cores/megacommand/MCL/SeqPtcPage.cpp | 2 ++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 23ba318af..7159ac99b 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -4,19 +4,21 @@ void MenuPageBase::init() { ((MCLEncoder *)encoders[1])->max = get_menu()->get_number_of_items() - 1; if (((MCLEncoder *)encoders[1])->cur > ((MCLEncoder *)encoders[1])->max) { - ((MCLEncoder *)encoders[1])->cur = 0; + ((MCLEncoder *)encoders[1])->cur = 0; } - ((MCLEncoder *)encoders[0])->max = + ((MCLEncoder *)encoders[0])->max = get_menu()->get_option_range(encoders[1]->cur) - 1; - ((MCLEncoder *)encoders[0])->min = get_menu()->get_option_min(encoders[1]->cur); + ((MCLEncoder *)encoders[0])->min = + get_menu()->get_option_min(encoders[1]->cur); - uint8_t *dest_var = get_menu()->get_dest_variable(encoders[1]->cur); + uint8_t *dest_var = get_menu()->get_dest_variable(encoders[1]->cur); if (dest_var != NULL) { encoders[0]->setValue(*dest_var); } encoders[0]->old = encoders[0]->cur; encoders[1]->old = encoders[1]->cur; } + void MenuPageBase::setup() { #ifdef OLED_DISPLAY classic_display = false; @@ -25,11 +27,11 @@ void MenuPageBase::setup() { void MenuPageBase::loop() { - if (encoders[1]->hasChanged()) { - ((MCLEncoder *)encoders[0])->max = - get_menu()->get_option_range(encoders[1]->cur) - 1; - ((MCLEncoder *)encoders[0])->min = get_menu()->get_option_min(encoders[1]->cur); - + if (encoders[1]->hasChanged()) { + ((MCLEncoder *)encoders[0])->max = + get_menu()->get_option_range(encoders[1]->cur) - 1; + ((MCLEncoder *)encoders[0])->min = + get_menu()->get_option_min(encoders[1]->cur); uint8_t diff = encoders[1]->cur - encoders[1]->old; int8_t new_val = cur_row + diff; @@ -83,7 +85,8 @@ void MenuPageBase::draw_item(uint8_t item_n, uint8_t row) { if (get_menu()->get_option_range(item_n) > 0) { oled_display.print(" "); - pgp = get_menu()->get_option_name(item_n, *(get_menu()->get_dest_variable(item_n))); + pgp = get_menu()->get_option_name(item_n, + *(get_menu()->get_dest_variable(item_n))); if (pgp == NULL) { oled_display.println(*(get_menu()->get_dest_variable(item_n))); } else { @@ -94,7 +97,8 @@ void MenuPageBase::draw_item(uint8_t item_n, uint8_t row) { #endif } -void MenuPageBase::draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width) { +void MenuPageBase::draw_menu(uint8_t x_offset, uint8_t y_offset, + uint8_t width) { #ifdef OLED_DISPLAY oled_display.setCursor(x_offset, y_offset); uint8_t number_of_items = get_menu()->get_number_of_items(); @@ -169,7 +173,8 @@ void MenuPageBase::display() { uint8_t number_of_options = get_menu()->get_number_of_options(cur_row); if (get_menu()->get_option_range(cur_row) > 0) { - pgp = get_menu()->get_option_name(cur_row, *(get_menu()->get_dest_variable(cur_row))); + pgp = get_menu()->get_option_name( + cur_row, *(get_menu()->get_dest_variable(cur_row))); if (pgp == NULL) { GUI.put_value_at(10, *(get_menu()->get_dest_variable(cur_row))); } else { diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index eb6e3c765..5b1d713c8 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -45,7 +45,7 @@ const menu_t<9> seq_menu_layout PROGMEM = { }; MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); -MCLEncoder seq_menu_entry_encoder(0, 8, ENCODER_RES_PAT); +MCLEncoder seq_menu_entry_encoder(0, 9, ENCODER_RES_PAT); MenuPage<9> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 500eee023..53c5c538b 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -189,7 +189,10 @@ void SeqParamPage::loop() { mcl_seq.md_tracks[last_md_track].update_params(); } } + + SeqPage::loop(); } + bool SeqParamPage::handleEvent(gui_event_t *event) { if (SeqPage::handleEvent(event)) { diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 2cdc90141..ace7add82 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -195,6 +195,8 @@ void SeqPtcPage::loop() { deferred_timer = 0; redisplay = true; } + + SeqPage::loop(); } #ifndef OLED_DISPLAY From a37a0423748b8a843f80ae4c7817d7670b24f00d Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 15 Nov 2019 22:15:22 +0800 Subject: [PATCH 295/469] SeqPage::loop locks ext select track range; merge clear track/all --- avr/cores/megacommand/MCL/MenuPage.cpp | 6 ++-- avr/cores/megacommand/MCL/SeqPage.cpp | 47 ++++++++++++++++---------- avr/cores/megacommand/MCL/SeqPage.h | 3 +- avr/cores/megacommand/MCL/SeqPages.cpp | 14 +++----- avr/cores/megacommand/MCL/SeqPages.h | 2 +- 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 7159ac99b..52fd9392f 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -85,10 +85,10 @@ void MenuPageBase::draw_item(uint8_t item_n, uint8_t row) { if (get_menu()->get_option_range(item_n) > 0) { oled_display.print(" "); - pgp = get_menu()->get_option_name(item_n, - *(get_menu()->get_dest_variable(item_n))); + uint8_t *pdest = get_menu()->get_dest_variable(item_n); + pgp = get_menu()->get_option_name(item_n, *pdest); if (pgp == NULL) { - oled_display.println(*(get_menu()->get_dest_variable(item_n))); + oled_display.println(*pdest); } else { m_strncpy_p(str, pgp, 11); oled_display.println(str); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 087695a93..b4dd23309 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -9,8 +9,9 @@ uint8_t SeqPage::ignore_button_release = 255; uint8_t SeqPage::page_count = 4; bool SeqPage::show_seq_menu = false; -uint8_t opt_resolution = 0; +uint8_t opt_resolution = 1; uint8_t opt_trackid = 1; +uint8_t opt_clearall = 0; static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; static MCLEncoder *opt_param1_capture = nullptr; @@ -57,7 +58,7 @@ void SeqPage::select_track(uint8_t device, uint8_t track) { } #ifdef EXT_TRACKS else { - last_ext_track = min(track, 4); // XXX + last_ext_track = min(track, 3); // XXX } #endif GUI.currentPage()->redisplay = true; @@ -506,6 +507,7 @@ void opt_resolution_handler() { #ifdef EXT_TRACKS else { (mcl_seq.ext_tracks[last_ext_track].resolution) = opt_resolution; + seq_extstep_page.config_encoders(); } #endif opt_seqpage_capture->init(); @@ -513,15 +515,33 @@ void opt_resolution_handler() { void opt_clear_track_handler() { if (opt_midi_device_capture == DEVICE_MD) { - mcl_seq.md_tracks[last_md_track].clear_track(); + if (opt_clearall) { + for (uint8_t n = 0; n < 16; ++n) { + mcl_seq.md_tracks[n].clear_track(); + } + } else { + mcl_seq.md_tracks[last_md_track].clear_track(); + } } else { - mcl_seq.ext_tracks[last_ext_track].clear_track(); + if (opt_clearall) { + for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { + mcl_seq.ext_tracks[n].clear_track(); + } + } else { + mcl_seq.ext_tracks[last_ext_track].clear_track(); + } } } void opt_clear_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - mcl_seq.md_tracks[last_md_track].clear_locks(); + if (opt_clearall) { + for (uint8_t n = 0; n < 16; ++n) { + mcl_seq.md_tracks[n].clear_locks(); + } + } else { + mcl_seq.md_tracks[last_md_track].clear_locks(); + } } else { // TODO ext locks } @@ -529,9 +549,6 @@ void opt_clear_locks_handler() { void opt_clear_all_tracks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - for (uint8_t n = 0; n < 16; ++n) { - mcl_seq.md_tracks[n].clear_track(); - } } else { mcl_seq.ext_tracks[last_ext_track].clear_track(); } @@ -539,9 +556,6 @@ void opt_clear_all_tracks_handler() { void opt_clear_all_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - for (uint8_t n = 0; n < 16; ++n) { - mcl_seq.md_tracks[n].clear_locks(); - } } else { // TODO ext locks } @@ -551,24 +565,23 @@ void SeqPage::config_as_trackedit() { seq_menu_page.menu.enable_entry(2, true); seq_menu_page.menu.enable_entry(3, false); - - seq_menu_page.menu.enable_entry(7, true); - seq_menu_page.menu.enable_entry(8, false); } void SeqPage::config_as_lockedit() { seq_menu_page.menu.enable_entry(2, false); seq_menu_page.menu.enable_entry(3, true); - - seq_menu_page.menu.enable_entry(7, false); - seq_menu_page.menu.enable_entry(8, true); } void SeqPage::loop() { if (show_seq_menu) { seq_menu_page.loop(); + if (opt_midi_device_capture != DEVICE_MD && opt_trackid > 4) { + // lock trackid to [1..4] + opt_trackid = min(opt_trackid, 4); + seq_menu_value_encoder.cur = opt_trackid; + } return; } } diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 9ea35f2e8..4f1dba151 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -19,12 +19,11 @@ extern void pattern_len_handler(Encoder *enc); extern uint8_t opt_trackid; extern uint8_t opt_resolution; +extern uint8_t opt_clearall; extern void opt_trackid_handler(); extern void opt_resolution_handler(); extern void opt_clear_track_handler(); extern void opt_clear_locks_handler(); -extern void opt_clear_all_tracks_handler(); -extern void opt_clear_all_locks_handler(); class SeqPage : public LightPage { public: diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 5b1d713c8..67c9ede74 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -26,26 +26,22 @@ SeqExtStepPage seq_extstep_page(&seq_param1, &seq_param2, &seq_param3, SeqPtcPage seq_ptc_page(&ptc_param_oct, &ptc_param_finetune, &ptc_param_len, &ptc_param_scale); -const menu_t<9> seq_menu_layout PROGMEM = { +const menu_t<7> seq_menu_layout PROGMEM = { "SEQ", { {"TRACK SEL.", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, {"COPY TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"CLEAR TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_track_handler, {}}, - {"CLEAR LCKS.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_locks_handler, {}}, + {"CLEAR", 0, 2, 2, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_track_handler, { {0, "TRK."}, {1, "ALL"}}}, + {"CLEAR", 0, 0, 2, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_locks_handler, { {0, "LCKS."}, {1, "ALL"}}}, {"PASTE TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"TRACK RES.", 0, 2, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {0, "1x"}, {1, "2x"} }}, + {"TRACK RES.", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, {"STEP SHIFT", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - // clear all tracks - {"CLEAR ALL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_all_tracks_handler, {}}, - // clear all locks - {"CLEAR ALL", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, opt_clear_all_locks_handler, {}}, }, NULL, }; MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder seq_menu_entry_encoder(0, 9, ENCODER_RES_PAT); -MenuPage<9> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); +MenuPage<7> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 89d65d334..49143ac73 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -55,7 +55,7 @@ extern SeqPtcPage seq_ptc_page; extern MCLEncoder seq_menu_value_encoder; extern MCLEncoder seq_menu_entry_encoder; -extern MenuPage<9> seq_menu_page; +extern MenuPage<7> seq_menu_page; extern void mcl_save_sound(); extern void mcl_load_sound(); From 5bd6033287908f82999f94698ec7f85648e859aa Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 12:36:34 +1100 Subject: [PATCH 296/469] Reverse Yes and No to match button layout. Fix layout --- avr/cores/megacommand/MCL/MCLGUI.cpp | 6 +++--- avr/cores/megacommand/MCL/QuestionDialogPage.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 6f0570a2c..cad9047d4 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -187,8 +187,8 @@ void MCLGUI::draw_infobox(const char *line1, const char *line2, WHITE); oled_display.fillCircle(dlg_circle_x, dlg_circle_y, 6, WHITE); - oled_display.fillRect(dlg_circle_x - 1, dlg_circle_y - 3, 2, 4, BLACK); - oled_display.fillRect(dlg_circle_x - 1, dlg_circle_y + 2, 2, 2, BLACK); + oled_display.fillRect(dlg_circle_x - 1, dlg_circle_y - 3, 3, 4, BLACK); + oled_display.fillRect(dlg_circle_x - 1, dlg_circle_y + 2, 3, 2, BLACK); oled_display.setFont(&TomThumb); oled_display.setTextColor(BLACK); @@ -198,7 +198,7 @@ void MCLGUI::draw_infobox(const char *line1, const char *line2, oled_display.println(title_buf); oled_display.setTextColor(WHITE); - oled_display.setCursor(dlg_info_x1 + 23, dlg_info_y1 + 17 + line2_offset); + oled_display.setCursor(dlg_info_x1 + 23, dlg_info_y1 + 16 + line2_offset); oled_display.println(line2); oled_display.setFont(oldfont); diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp index dd130450f..02d2348ab 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -12,10 +12,10 @@ void QuestionDialogPage::init(const char* title, const char* text) { oled_display.setTextColor(WHITE); oled_display.setCursor(MCLGUI::dlg_info_x2 - 86, MCLGUI::dlg_info_y1 + 24); - oled_display.print("W YES"); + oled_display.print("S NO"); oled_display.setCursor(MCLGUI::dlg_info_x2 - 55, MCLGUI::dlg_info_y1 + 24); - oled_display.print("S NO"); + oled_display.print("W YES"); oled_display.drawRect(MCLGUI::dlg_info_x2 - 88, MCLGUI::dlg_info_y1 + 17, 21, 8, WHITE); oled_display.drawRect(MCLGUI::dlg_info_x2 - 57, MCLGUI::dlg_info_y1 + 17, 19, 8, WHITE); @@ -35,13 +35,13 @@ bool QuestionDialogPage::handleEvent(gui_event_t *event) { return false; } - if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { oled_display.fillRect(MCLGUI::dlg_info_x2 - 82, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { oled_display.fillRect(MCLGUI::dlg_info_x2 - 51, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); return true; From 1095fff7bdabd01d72735be175f081aa53c49d95 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 12:48:08 +1100 Subject: [PATCH 297/469] Use GUI.ignoreNextEvent to ignore button release --- avr/cores/megacommand/MCL/MenuPage.cpp | 1 + avr/cores/megacommand/MCL/SeqPage.cpp | 9 +-------- avr/cores/megacommand/MCL/SeqPage.h | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/MenuPage.cpp b/avr/cores/megacommand/MCL/MenuPage.cpp index 52fd9392f..dc54327fd 100644 --- a/avr/cores/megacommand/MCL/MenuPage.cpp +++ b/avr/cores/megacommand/MCL/MenuPage.cpp @@ -225,6 +225,7 @@ bool MenuPageBase::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + GUI.ignoreNextEvent(event->source); exit(); return true; } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index b4dd23309..5dc219b29 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -5,7 +5,6 @@ uint8_t SeqPage::page_select = 0; uint8_t SeqPage::midi_device = DEVICE_MD; -uint8_t SeqPage::ignore_button_release = 255; uint8_t SeqPage::page_count = 4; bool SeqPage::show_seq_menu = false; @@ -31,7 +30,6 @@ void SeqPage::create_chars_seq() { void SeqPage::setup() { create_chars_seq(); } void SeqPage::init() { - ignore_button_release = 255; page_count = 4; ((MCLEncoder *)encoders[2])->handler = pattern_len_handler; seqpage_midi_events.setup_callbacks(); @@ -115,7 +113,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { encoders[2]->cur = step; if (event->mask == EVENT_BUTTON_RELEASED) { note_interface.notes[track] = 0; - ignore_button_release = 4; + GUI.ignoreNextEvent(event->source); } return true; } @@ -126,16 +124,11 @@ bool SeqPage::handleEvent(gui_event_t *event) { // A not-ignored WRITE (BUTTON4) release event triggers sequence page select if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - if (ignore_button_release != 4) { page_select += 1; if (page_select >= page_count) { page_select = 0; } - } else { - // clear ignore flag - ignore_button_release = 255; - } return true; } diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 4f1dba151..6ff9655eb 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -31,7 +31,6 @@ class SeqPage : public LightPage { static uint8_t page_select; static uint8_t page_count; static uint8_t midi_device; - static uint8_t ignore_button_release; static bool show_seq_menu; bool recording = false; From bb60c3b64d18c1f6d6f3a42a7d1a61f59febe87d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 12:51:25 +1100 Subject: [PATCH 298/469] Don't allow exit from SoundBrowserPage --- avr/cores/megacommand/MCL/SoundBrowserPage.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp index c07afa6fd..7c1bd03a5 100644 --- a/avr/cores/megacommand/MCL/SoundBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/SoundBrowserPage.cpp @@ -98,6 +98,11 @@ bool SoundBrowserPage::handleEvent(gui_event_t* event) { return true; } + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + return true; + } + + return FileBrowserPage::handleEvent(event); } From 92609a50cc2c1d6864bc9be20886f1147fde7b17 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 13:21:32 +1100 Subject: [PATCH 299/469] Use GridSlotMenu style Clear option in TrackMenu --- avr/cores/megacommand/MCL/SeqPage.cpp | 9 +++++---- avr/cores/megacommand/MCL/SeqPages.cpp | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 5dc219b29..43914042a 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -508,22 +508,23 @@ void opt_resolution_handler() { void opt_clear_track_handler() { if (opt_midi_device_capture == DEVICE_MD) { - if (opt_clearall) { + if (opt_clearall == 2) { for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_track(); } - } else { + } else if (opt_clearall == 1) { mcl_seq.md_tracks[last_md_track].clear_track(); } } else { - if (opt_clearall) { + if (opt_clearall == 2) { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { mcl_seq.ext_tracks[n].clear_track(); } - } else { + } else if (opt_clearall == 1) { mcl_seq.ext_tracks[last_ext_track].clear_track(); } } + opt_clearall = 0; } void opt_clear_locks_handler() { diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 67c9ede74..af3673b33 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -31,8 +31,8 @@ const menu_t<7> seq_menu_layout PROGMEM = { { {"TRACK SEL.", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, {"COPY TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"CLEAR", 0, 2, 2, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_track_handler, { {0, "TRK."}, {1, "ALL"}}}, - {"CLEAR", 0, 0, 2, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_locks_handler, { {0, "LCKS."}, {1, "ALL"}}}, + {"CLEAR", 0, 3, 3, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"CLEAR", 0, 0, 3, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {2, "LCKS."}, {2, "ALL"}}}, {"PASTE TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, {"TRACK RES.", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, {"STEP SHIFT", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, From b84d4de55a02f35031fea8c17651eb59be17f7ae Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 13:25:20 +1100 Subject: [PATCH 300/469] Ignore button release when exiting to Grid --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 4dca5d1d5..401c52baf 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -321,6 +321,7 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { if (EVENT_RELEASED(event, Buttons.BUTTON1)) { md_exploit.off(); + GUI.ignoreNextEvent(event->source); GUI.setPage(&grid_page); return true; } From e5c122ab58112fc675b91e13e25b388c6bae3db6 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 13:27:38 +1100 Subject: [PATCH 301/469] Fix pitch paramter lock on track 16, this time for real --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 5f217c671..65bfdde0a 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -206,7 +206,7 @@ void SeqStepPage::loop() { if (!IS_BIT_SET64(active_track.pattern_mask, step)) { SET_BIT64(active_track.pattern_mask, step); } - if ((seq_param4.cur > 0) && (last_md_track < 15) && + if ((seq_param4.cur > 0) && (last_md_track < NUM_MD_TRACKS) && (tuning != NULL)) { uint8_t base = tuning->base; uint8_t note_num = seq_param4.cur; From e59604afa979b2b887bc9ccc566a20d94c27e0c9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 13:53:48 +1100 Subject: [PATCH 302/469] Fixes for LoudnessPage, TI would break in PageSelect --- avr/cores/megacommand/MCL/LoudnessPage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/LoudnessPage.cpp b/avr/cores/megacommand/MCL/LoudnessPage.cpp index a9ee06da0..9e25994b5 100644 --- a/avr/cores/megacommand/MCL/LoudnessPage.cpp +++ b/avr/cores/megacommand/MCL/LoudnessPage.cpp @@ -6,6 +6,8 @@ void LoudnessPage::setup() { DEBUG_PRINT_FN(); } void LoudnessPage::init() { DEBUG_PRINT_FN(); + + md_exploit.off(); #ifdef OLED_DISPLAY // classic_display = false; oled_display.clearDisplay(); @@ -122,6 +124,7 @@ void LoudnessPage::scale_vol(float inc) { MDTrack *md_track = (MDTrack *)&empty_track; grid_page.prepare(); + MD.getCurrentPattern(CALLBACK_TIMEOUT); MD.getBlockingPattern(MD.currentPattern); uint8_t seq_mute_states[NUM_MD_TRACKS]; @@ -139,7 +142,6 @@ void LoudnessPage::scale_vol(float inc) { md_track->place_track_in_pattern(n, n, &MD.pattern); md_track->place_track_in_kit(n, n, &MD.kit); } - mcl_actions.md_setsysex_recpos(8, MD.pattern.origPosition); MD.pattern.toSysex(); @@ -274,11 +276,9 @@ void LoudnessPage::check_grid_loudness(int col, int row) { void LoudnessPage::display() { - if (!classic_display) { #ifdef OLED_DISPLAY oled_display.clearDisplay(); #endif - } GUI.setLine(GUI.LINE1); uint8_t x; // GUI.put_string_at(12,"Loudness"); From 871d94425ba3daf531a289876768db0d863d6077 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 14:01:48 +1100 Subject: [PATCH 303/469] Fix type --- avr/cores/megacommand/GUI/GUI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/GUI/GUI.h b/avr/cores/megacommand/GUI/GUI.h index 808d755d1..498ec32a7 100644 --- a/avr/cores/megacommand/GUI/GUI.h +++ b/avr/cores/megacommand/GUI/GUI.h @@ -175,7 +175,7 @@ class GuiClass { eventHandlers.remove(handler); } void ignoreNextEvent(uint8_t i) { - SET_BIT64(event_ignore_next_mask, i); + SET_BIT(event_ignore_next_mask, i); } /** * Add a new task to be periodically polled (max 8). From 0f061dcf2e2e090a7d874a4095b7a490765389f0 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 18:26:39 +1100 Subject: [PATCH 304/469] Fixes for ignoreNextEvent --- avr/cores/megacommand/GUI/Events.cpp | 18 +++++++++++++----- avr/cores/megacommand/MCL/PageSelectPage.cpp | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/GUI/Events.cpp b/avr/cores/megacommand/GUI/Events.cpp index 1493fd811..4509139ab 100644 --- a/avr/cores/megacommand/GUI/Events.cpp +++ b/avr/cores/megacommand/GUI/Events.cpp @@ -11,17 +11,25 @@ void pollEventGUI() { for (int i = 0; i < MAX_BUTTONS; i++) { gui_event_t event; event.source = i; - if (!IS_BIT_SET(event_ignore_next_mask, i)) { - if (BUTTON_PRESSED(i)) { + if (BUTTON_PRESSED(i)) { + + if (!IS_BIT_SET(event_ignore_next_mask, i)) { event.mask = EVENT_BUTTON_PRESSED; EventRB.putp(&event); + } else { + + CLEAR_BIT(event_ignore_next_mask, i); } - if (BUTTON_RELEASED(i)) { + } + if (BUTTON_RELEASED(i)) { + + if (!IS_BIT_SET(event_ignore_next_mask, i)) { event.mask = EVENT_BUTTON_RELEASED; EventRB.putp(&event); + + } else { + CLEAR_BIT(event_ignore_next_mask, i); } - } else { - CLEAR_BIT(event_ignore_next_mask, i); } } } diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 401c52baf..bb8230519 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -311,6 +311,7 @@ bool PageSelectPage::handleEvent(gui_event_t *event) { LightPage *p; p = get_page(get_pageidx(page_select), nullptr); if (BUTTON_DOWN(Buttons.BUTTON1) || (!p)) { + GUI.ignoreNextEvent(Buttons.BUTTON1); md_exploit.off(); GUI.setPage(&grid_page); } else { From e8e8b1ab88352ea66e80468a277d46f3cda9e268 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 20:51:34 +1100 Subject: [PATCH 305/469] Copy/paste track routines, WIP --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 59 ++++++++++++++++++++-- avr/cores/megacommand/MCL/MCLClipBoard.h | 5 ++ avr/cores/megacommand/MCL/SeqPage.cpp | 35 ++++++++++--- avr/cores/megacommand/MCL/SeqPage.h | 5 +- avr/cores/megacommand/MCL/SeqPages.cpp | 8 +-- 5 files changed, 95 insertions(+), 17 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index 6945405f2..de093b117 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -6,13 +6,13 @@ #define FILENAME_CLIPBOARD "clipboard.tmp" +//Sequencer CLIPBOARD tracks are stored at the end of the GRID + 1. + +#define CLIPBOARD_FILE_SIZE (uint32_t)GRID_SLOT_BYTES + (uint32_t)GRID_SLOT_BYTES * (uint32_t)(GRID_LENGTH + 1) * (uint32_t)(GRID_WIDTH + 1) + bool MCLClipBoard::init() { DEBUG_PRINTLN("Creating clipboard"); - bool ret = file.createContiguous(FILENAME_CLIPBOARD, - (uint32_t)GRID_SLOT_BYTES + - (uint32_t)GRID_SLOT_BYTES * - (uint32_t)GRID_LENGTH * - (uint32_t)(GRID_WIDTH + 1)); + bool ret = file.createContiguous(FILENAME_CLIPBOARD,CLIPBOARD_FILE_SIZE); if (ret) { file.close(); } else { @@ -29,6 +29,55 @@ bool MCLClipBoard::open() { } bool MCLClipBoard::close() { return file.close(); } +bool MCLClipBoard::copy_sequencer() { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (!copy_sequencer_track(n)) { return false; } + } + return true; +} + +bool MCLClipBoard::copy_sequencer_track(uint8_t track) { + bool ret; + EmptyTrack temp_track; + MDTrack *md_track = (MDTrack*)(&temp_track); + int32_t offset = grid.get_slot_offset(track, GRID_LENGTH); + ret = file.seekSet(offset); + if (!ret) { return false; } + memcpy(&(md_track->seq_data), &mcl_seq.md_tracks[track], + sizeof(md_track->seq_data)); + md_track->get_machine_from_kit(track,track); + return mcl_sd.write_data(&temp_track, sizeof(temp_track), &file); +} + +bool MCLClipBoard::paste_sequencer() { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (!paste_sequencer_track(n)) { return false; } + } + return true; +} + +bool MCLClipBoard::paste_sequencer_track(uint8_t track) { + bool ret; + EmptyTrack temp_track; + MDTrack *md_track = (MDTrack*)(&temp_track); + int32_t offset = grid.get_slot_offset(track, GRID_LENGTH); + ret = file.seekSet(offset); + if (ret) { + ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); + if (!ret) { return false; } + } + else { + return false; + } + memcpy(&mcl_seq.md_tracks[track], &(md_track->seq_data), + sizeof(md_track->seq_data)); + md_track->place_track_in_kit(track, track, &(MD.kit),true); + MD.setMachine(track, &(md_track->machine)); + return true; +} + + + bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h) { DEBUG_PRINT_FN(); t_col = col; diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.h b/avr/cores/megacommand/MCL/MCLClipBoard.h index 60f7ab9fa..7ebadf19f 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.h +++ b/avr/cores/megacommand/MCL/MCLClipBoard.h @@ -20,6 +20,11 @@ class MCLClipBoard { bool open(); bool close(); + bool copy_sequencer(); + bool copy_sequencer_track(uint8_t track); + bool paste_sequencer(); + bool paste_sequencer_track(uint8_t track); + bool copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h); bool paste(uint16_t col, uint16_t row); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 43914042a..97fd8d608 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -10,7 +10,8 @@ bool SeqPage::show_seq_menu = false; uint8_t opt_resolution = 1; uint8_t opt_trackid = 1; -uint8_t opt_clearall = 0; +uint8_t opt_trackall = 0; + static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; static MCLEncoder *opt_param1_capture = nullptr; @@ -508,28 +509,28 @@ void opt_resolution_handler() { void opt_clear_track_handler() { if (opt_midi_device_capture == DEVICE_MD) { - if (opt_clearall == 2) { + if (opt_trackall == 2) { for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_track(); } - } else if (opt_clearall == 1) { + } else if (opt_trackall == 1) { mcl_seq.md_tracks[last_md_track].clear_track(); } } else { - if (opt_clearall == 2) { + if (opt_trackall == 2) { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { mcl_seq.ext_tracks[n].clear_track(); } - } else if (opt_clearall == 1) { + } else if (opt_trackall == 1) { mcl_seq.ext_tracks[last_ext_track].clear_track(); } } - opt_clearall = 0; + opt_trackall = 0; } void opt_clear_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - if (opt_clearall) { + if (opt_trackall) { for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_locks(); } @@ -555,6 +556,26 @@ void opt_clear_all_locks_handler() { } } +void opt_copy_track_handler() { + if (opt_trackall == 2) { + mcl_clipboard.copy_sequencer(); + } + if (opt_trackall == 1) { + mcl_clipboard.copy_sequencer_track(last_md_track); + } + opt_trackall =0; +} + +void opt_paste_track_handler() { + if (opt_trackall == 2) { + mcl_clipboard.paste_sequencer(); + } + if (opt_trackall == 1) { + mcl_clipboard.paste_sequencer_track(last_md_track); + } + opt_trackall = 0; +} + void SeqPage::config_as_trackedit() { seq_menu_page.menu.enable_entry(2, true); diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 6ff9655eb..db812b936 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -19,11 +19,14 @@ extern void pattern_len_handler(Encoder *enc); extern uint8_t opt_trackid; extern uint8_t opt_resolution; -extern uint8_t opt_clearall; +extern uint8_t opt_trackall; + extern void opt_trackid_handler(); extern void opt_resolution_handler(); extern void opt_clear_track_handler(); extern void opt_clear_locks_handler(); +extern void opt_copy_track_handler(); +extern void opt_paste_track_handler(); class SeqPage : public LightPage { public: diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index af3673b33..f7b37ed13 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -30,10 +30,10 @@ const menu_t<7> seq_menu_layout PROGMEM = { "SEQ", { {"TRACK SEL.", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, - {"COPY TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, - {"CLEAR", 0, 3, 3, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"CLEAR", 0, 0, 3, (uint8_t *)&opt_clearall, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {2, "LCKS."}, {2, "ALL"}}}, - {"PASTE TRK.", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"COPY", 0, 3, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_copy_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"CLEAR", 0, 3, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"CLEAR", 0, 0, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {2, "LCKS."}, {2, "ALL"}}}, + {"PASTE", 0, 3, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, {"TRACK RES.", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, {"STEP SHIFT", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, }, From 66cd24a7fad7e8addf9c22c32c3448aa427050bc Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 21:18:10 +1100 Subject: [PATCH 306/469] Copy/paste track should allow different source/dest tracks --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 6 +++--- avr/cores/megacommand/MCL/MCLClipBoard.h | 5 ++++- avr/cores/megacommand/MCL/SeqPage.cpp | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index de093b117..e36c0f75c 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -51,16 +51,16 @@ bool MCLClipBoard::copy_sequencer_track(uint8_t track) { bool MCLClipBoard::paste_sequencer() { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (!paste_sequencer_track(n)) { return false; } + if (!paste_sequencer_track(n,n)) { return false; } } return true; } -bool MCLClipBoard::paste_sequencer_track(uint8_t track) { +bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { bool ret; EmptyTrack temp_track; MDTrack *md_track = (MDTrack*)(&temp_track); - int32_t offset = grid.get_slot_offset(track, GRID_LENGTH); + int32_t offset = grid.get_slot_offset(source_track, GRID_LENGTH); ret = file.seekSet(offset); if (ret) { ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.h b/avr/cores/megacommand/MCL/MCLClipBoard.h index 7ebadf19f..a28dccfe0 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.h +++ b/avr/cores/megacommand/MCL/MCLClipBoard.h @@ -14,6 +14,9 @@ class MCLClipBoard { int t_row; int t_w; int t_h; + + uint8_t copy_track; + File file; bool init(); @@ -23,7 +26,7 @@ class MCLClipBoard { bool copy_sequencer(); bool copy_sequencer_track(uint8_t track); bool paste_sequencer(); - bool paste_sequencer_track(uint8_t track); + bool paste_sequencer_track(uint8_t source_track, uint8_t track); bool copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h); bool paste(uint16_t col, uint16_t row); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 97fd8d608..3817dad76 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -561,6 +561,7 @@ void opt_copy_track_handler() { mcl_clipboard.copy_sequencer(); } if (opt_trackall == 1) { + mcl_clipboard.copy_track = last_md_track; mcl_clipboard.copy_sequencer_track(last_md_track); } opt_trackall =0; @@ -571,7 +572,7 @@ void opt_paste_track_handler() { mcl_clipboard.paste_sequencer(); } if (opt_trackall == 1) { - mcl_clipboard.paste_sequencer_track(last_md_track); + mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_md_track); } opt_trackall = 0; } From 436701b5313dc8698e65615ca5713402d9880670 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 22:29:38 +1100 Subject: [PATCH 307/469] Use individual opt variables --- avr/cores/megacommand/MCL/SeqPage.cpp | 29 ++++++++++++++------------ avr/cores/megacommand/MCL/SeqPage.h | 4 +++- avr/cores/megacommand/MCL/SeqPages.cpp | 8 +++---- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 3817dad76..77649fa01 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -10,7 +10,9 @@ bool SeqPage::show_seq_menu = false; uint8_t opt_resolution = 1; uint8_t opt_trackid = 1; -uint8_t opt_trackall = 0; +uint8_t opt_copy = 0; +uint8_t opt_paste = 0; +uint8_t opt_clear = 0; static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; @@ -509,28 +511,28 @@ void opt_resolution_handler() { void opt_clear_track_handler() { if (opt_midi_device_capture == DEVICE_MD) { - if (opt_trackall == 2) { + if (opt_clear == 2) { for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_track(); } - } else if (opt_trackall == 1) { + } else if (opt_clear == 1) { mcl_seq.md_tracks[last_md_track].clear_track(); } } else { - if (opt_trackall == 2) { + if (opt_clear == 2) { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { mcl_seq.ext_tracks[n].clear_track(); } - } else if (opt_trackall == 1) { + } else if (opt_clear == 1) { mcl_seq.ext_tracks[last_ext_track].clear_track(); } } - opt_trackall = 0; + opt_clear = 0; } void opt_clear_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - if (opt_trackall) { + if (opt_clear) { for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_locks(); } @@ -540,6 +542,7 @@ void opt_clear_locks_handler() { } else { // TODO ext locks } + opt_clear = 0; } void opt_clear_all_tracks_handler() { @@ -557,24 +560,24 @@ void opt_clear_all_locks_handler() { } void opt_copy_track_handler() { - if (opt_trackall == 2) { + if (opt_copy == 2) { mcl_clipboard.copy_sequencer(); } - if (opt_trackall == 1) { + if (opt_copy == 1) { mcl_clipboard.copy_track = last_md_track; mcl_clipboard.copy_sequencer_track(last_md_track); } - opt_trackall =0; + opt_copy = 0; } void opt_paste_track_handler() { - if (opt_trackall == 2) { + if (opt_paste == 2) { mcl_clipboard.paste_sequencer(); } - if (opt_trackall == 1) { + if (opt_paste == 1) { mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_md_track); } - opt_trackall = 0; + opt_paste = 0; } void SeqPage::config_as_trackedit() { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index db812b936..49d09cf8b 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -19,7 +19,9 @@ extern void pattern_len_handler(Encoder *enc); extern uint8_t opt_trackid; extern uint8_t opt_resolution; -extern uint8_t opt_trackall; +extern uint8_t opt_copy; +extern uint8_t opt_paste; +extern uint8_t opt_clear; extern void opt_trackid_handler(); extern void opt_resolution_handler(); diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index f7b37ed13..ff14c16ce 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -30,10 +30,10 @@ const menu_t<7> seq_menu_layout PROGMEM = { "SEQ", { {"TRACK SEL.", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, - {"COPY", 0, 3, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_copy_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"CLEAR", 0, 3, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"CLEAR", 0, 0, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {2, "LCKS."}, {2, "ALL"}}}, - {"PASTE", 0, 3, 3, (uint8_t *)&opt_trackall, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"COPY", 0, 3, 3, (uint8_t *)&opt_copy, (Page *)NULL, opt_copy_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"CLEAR", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"CLEAR", 0, 0, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {2, "LCKS."}, {2, "ALL"}}}, + {"PASTE", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, {"TRACK RES.", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, {"STEP SHIFT", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, }, From 9dc8d8a7f9278ed7600cd975c4fb6e22653f8edf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 22:45:57 +1100 Subject: [PATCH 308/469] Remove debug from MidiClock.h --- avr/cores/megacommand/Midi/MidiClock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index be2d40a15..f1c09c39e 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -301,7 +301,7 @@ class MidiClockClass { } void calc_tempo() { - DEBUG_PRINTLN(diff_clock8); + //DEBUG_PRINTLN(diff_clock8); if (last_diff_clock8 != diff_clock8) { tempo = 100000.0f / diff_clock8; last_diff_clock8 = diff_clock8; From e3fed4084a89f458eae7cd68df74e226304ed2ab Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 23:32:26 +1100 Subject: [PATCH 309/469] Fixes for ClipBoard --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 94 +++++++++++++++++----- 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index e36c0f75c..a3e3a9517 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -6,13 +6,13 @@ #define FILENAME_CLIPBOARD "clipboard.tmp" -//Sequencer CLIPBOARD tracks are stored at the end of the GRID + 1. +// Sequencer CLIPBOARD tracks are stored at the end of the GRID + 1. -#define CLIPBOARD_FILE_SIZE (uint32_t)GRID_SLOT_BYTES + (uint32_t)GRID_SLOT_BYTES * (uint32_t)(GRID_LENGTH + 1) * (uint32_t)(GRID_WIDTH + 1) +#define CLIPBOARD_FILE_SIZE (uint32_t) GRID_SLOT_BYTES + (uint32_t)GRID_SLOT_BYTES *(uint32_t)(GRID_LENGTH + 1) * (uint32_t)(GRID_WIDTH + 1) bool MCLClipBoard::init() { DEBUG_PRINTLN("Creating clipboard"); - bool ret = file.createContiguous(FILENAME_CLIPBOARD,CLIPBOARD_FILE_SIZE); + bool ret = file.createContiguous(FILENAME_CLIPBOARD, CLIPBOARD_FILE_SIZE); if (ret) { file.close(); } else { @@ -22,62 +22,111 @@ bool MCLClipBoard::init() { } bool MCLClipBoard::open() { + DEBUG_PRINT_FN(); if (!file.open(FILENAME_CLIPBOARD, O_RDWR)) { init(); return file.open(FILENAME_CLIPBOARD, O_RDWR); } + return true; +/* + if (file.fileSize() < CLIPBOARD_FILE_SIZE) { + file.close(); + SD.remove(FILENAME_CLIPBOARD); + DEBUG_PRINTLN("clipboard file size too small deleting"); + init(); + return file.open(FILENAME_CLIPBOARD, O_RDWR); + } */ } + bool MCLClipBoard::close() { return file.close(); } bool MCLClipBoard::copy_sequencer() { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (!copy_sequencer_track(n)) { return false; } + if (!copy_sequencer_track(n)) { + return false; + } } return true; } bool MCLClipBoard::copy_sequencer_track(uint8_t track) { + DEBUG_PRINT_FN(); bool ret; EmptyTrack temp_track; - MDTrack *md_track = (MDTrack*)(&temp_track); + MDTrack *md_track = (MDTrack *)(&temp_track); + if (!open()) { + DEBUG_PRINTLN("error could not open clipboard"); + return; + } int32_t offset = grid.get_slot_offset(track, GRID_LENGTH); ret = file.seekSet(offset); - if (!ret) { return false; } + if (!ret) { + DEBUG_PRINTLN("failed seek"); + close(); + return false; + } memcpy(&(md_track->seq_data), &mcl_seq.md_tracks[track], - sizeof(md_track->seq_data)); - md_track->get_machine_from_kit(track,track); - return mcl_sd.write_data(&temp_track, sizeof(temp_track), &file); + sizeof(md_track->seq_data)); + md_track->get_machine_from_kit(track, track); + ret = mcl_sd.write_data(&temp_track, sizeof(temp_track), &file); + close(); + if (!ret) { DEBUG_PRINTLN("failed write"); } + return ret; } bool MCLClipBoard::paste_sequencer() { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (!paste_sequencer_track(n,n)) { return false; } + if (!paste_sequencer_track(n, n)) { + return false; + } } return true; } bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { + DEBUG_PRINT_FN(); bool ret; EmptyTrack temp_track; - MDTrack *md_track = (MDTrack*)(&temp_track); + MDTrack *md_track = (MDTrack *)(&temp_track); + if (!open()) { + DEBUG_PRINTLN("error could not open clipboard"); + return; + } int32_t offset = grid.get_slot_offset(source_track, GRID_LENGTH); ret = file.seekSet(offset); if (ret) { - ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); - if (!ret) { return false; } - } - else { - return false; + ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); + if (!ret) { + DEBUG_PRINTLN("failed read"); + close(); + return false; + } + } else { + close(); + DEBUG_PRINTLN("failed seek"); + return false; } + + DEBUG_PRINTLN("loading seq track"); memcpy(&mcl_seq.md_tracks[track], &(md_track->seq_data), - sizeof(md_track->seq_data)); - md_track->place_track_in_kit(track, track, &(MD.kit),true); + sizeof(md_track->seq_data)); + + if (md_track->machine.trigGroup == source_track) { + md_track->machine.trigGroup = 255; + } + if (md_track->machine.muteGroup == source_track) { + md_track->machine.muteGroup = 255; + } + if (md_track->machine.lfo.destinationTrack == source_track) { + md_track->machine.lfo.destinationTrack = track; + } + DEBUG_PRINTLN("sending seq track"); + md_track->place_track_in_kit(track, track, &(MD.kit), true); MD.setMachine(track, &(md_track->machine)); + close(); return true; } - - bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h) { DEBUG_PRINT_FN(); t_col = col; @@ -137,11 +186,12 @@ bool MCLClipBoard::paste(uint16_t col, uint16_t row) { for (int y = 0; y < t_h && y + row < GRID_LENGTH; y++) { header.read(y + row); - if ((strlen(header.name) == 0) || (!header.active) || (t_w == GRID_WIDTH && col == 0)) { + if ((strlen(header.name) == 0) || (!header.active) || + (t_w == GRID_WIDTH && col == 0)) { offset = grid.get_header_offset(y + t_row); ret = file.seekSet(offset); ret = mcl_sd.read_data((uint8_t *)(&header_copy), sizeof(GridRowHeader), - &file); + &file); header.active = true; strcpy(&(header.name[0]), &(header_copy.name[0])); } From 447f9486f252490b387863363f0cfdeec3966c5f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 23:40:28 +1100 Subject: [PATCH 310/469] Use smaller track types for data copying --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index a3e3a9517..be3a18209 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -68,7 +68,7 @@ bool MCLClipBoard::copy_sequencer_track(uint8_t track) { memcpy(&(md_track->seq_data), &mcl_seq.md_tracks[track], sizeof(md_track->seq_data)); md_track->get_machine_from_kit(track, track); - ret = mcl_sd.write_data(&temp_track, sizeof(temp_track), &file); + ret = mcl_sd.write_data(&temp_track, sizeof(MDTrackLight), &file); close(); if (!ret) { DEBUG_PRINTLN("failed write"); } return ret; @@ -95,7 +95,7 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { int32_t offset = grid.get_slot_offset(source_track, GRID_LENGTH); ret = file.seekSet(offset); if (ret) { - ret = mcl_sd.read_data(&temp_track, sizeof(temp_track), &file); + ret = mcl_sd.read_data(&temp_track, sizeof(MDTrackLight), &file); if (!ret) { DEBUG_PRINTLN("failed read"); close(); From bad582c02ca31d8ed176c57251493fe3fd7b8516 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 16 Nov 2019 23:46:29 +1100 Subject: [PATCH 311/469] Ignore button release on exiting TextInput --- avr/cores/megacommand/MCL/TextInputPage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index f2fae7d8f..4744745fe 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -245,6 +245,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { if (EVENT_RELEASED(event, Buttons.BUTTON1)) { return_state = false; + GUI.ignoreNextEvent(event->source); GUI.popPage(); return true; } @@ -281,6 +282,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { } m_strncpy(textp, text, cpy_len); textp[cpy_len] = '\0'; + GUI.ignoreNextEvent(event->source); GUI.popPage(); return true; } From 65416a1e1b96fa45167b7ad0cad5d82a831cdae5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 00:18:24 +1100 Subject: [PATCH 312/469] ignore button release on item select FileBrowser --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 603c16704..febd25c1e 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -409,6 +409,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { } // select an entry + GUI.ignoreNextEvent(event->source); on_select(temp_entry); return true; } From abedddc984cfe8791be7e95db78a2512b018b589 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 11:09:02 +1100 Subject: [PATCH 313/469] Add bit rotate macros --- avr/cores/megacommand/CommonTools/helpers.h | 5 ++++- avr/cores/megacommand/MCL/MCLGUI.cpp | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/CommonTools/helpers.h b/avr/cores/megacommand/CommonTools/helpers.h index 8a360ffcd..5ebf2b4ef 100644 --- a/avr/cores/megacommand/CommonTools/helpers.h +++ b/avr/cores/megacommand/CommonTools/helpers.h @@ -45,8 +45,11 @@ extern const uint32_t _bvmasks32[]; * @{ **/ +#define ROTATE_LEFT(target, length) do { target = (target >> shift) | (target << (length - shift)); } while (false) +#define ROTATE_RIGHT(target, length) do { target = (target << shift) | (target >> (length - shift)); } while (false) + /** 32 bit bit accessing macro. **/ -#define _BV32(i) (_bvmasks32[i]) +#define _BV32(i) (_bvmasks32[i]) /** Bit-level access and test macros. **/ #define SET_BIT(target, bit) do { (target) |= _bvmasks[((uint8_t)(bit))]; } while (false) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index cad9047d4..800e9a774 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -156,10 +156,11 @@ void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, oled_display.drawPixel(i, y_pos + 1 + n, BLACK); } } - temp_bitmask = (temp_bitmask >> shift) | (temp_bitmask << (8 - shift)); + ROTATE_LEFT(temp_bitmask, 8); } if (s_progress_count == s_progress_speed) { - s_progress_cookie = (bitmask >> shift) | (bitmask << (8 - shift)); + s_progress_cookie = bitmask; + ROTATE_LEFT(s_progress_cookie,8); s_progress_count = 0; } s_progress_count++; From aaaae56f025de12d0c5c70fa9c1b9fc34f7bbb8e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 11:28:15 +1100 Subject: [PATCH 314/469] Add rotate_left() and rotate_right() for MDSeqTrack --- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 51 ++++++++++++++++++++++++ avr/cores/megacommand/MCL/MDSeqTrack.h | 4 ++ 2 files changed, 55 insertions(+) diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index 249f0209a..755cb351a 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -490,3 +490,54 @@ void MDSeqTrack::merge_from_md(MDTrack *md_track) { } } } + +#define DIR_LEFT 0 +#define DIR_RIGHT 1 + +void MDSeqTrack::rotate(bool dir) { + +} + +void MDSeqTrack::rotate_left() { + + ROTATE_LEFT(lock_mask, length); + ROTATE_LEFT(pattern_mask, length); + ROTATE_LEFT(oneshot_mask, length); + + int8_t new_pos = 0; + + MDSeqTrackData temp_data; + + memcpy(&temp_data, this, sizeof(MDSeqTrackData)); + + for (uint8_t n = 0; n < length; n++) { + if (n == 0) { new_pos = length - 1 - 1; } + else { new_pos = n - 1; } + memcpy(&locks[0][new_pos], &(temp_data->locks[0][n]), NUM_MD_LOCKS); + conditional[new_pos] = temp_data->conditional[n]; + timing[new_pos] = temp_data->conditional[n]; + } + +} + +void MDSeqTrack::rotate_right() { + + ROTATE_RIGHT(lock_mask, length); + ROTATE_RIGHT(pattern_mask, length); + ROTATE_RIGHT(oneshot_mask, length); + + int8_t new_pos = 0; + + MDSeqTrackData temp_data; + + memcpy(&temp_data, this, sizeof(MDSeqTrackData)); + + for (uint8_t n = 0; n < length; n++) { + if (n == length - 1) { new_pos = 0; } + else { new_pos = n + 1; } + memcpy(&locks[0][new_pos], &(temp_data->locks[0][n]), NUM_MD_LOCKS); + conditional[new_pos] = temp_data->conditional[n]; + timing[new_pos] = temp_data->conditional[n]; + } + +} diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.h b/avr/cores/megacommand/MCL/MDSeqTrack.h index dd073ccb3..46647d264 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.h +++ b/avr/cores/megacommand/MCL/MDSeqTrack.h @@ -67,6 +67,10 @@ class MDSeqTrack : public MDSeqTrackData { void merge_from_md(MDTrack *md_track); void set_length(uint8_t len); + + void rotate_left(); + void rotate_right(); + }; #endif /* MDSEQTRACK_H__ */ From eebbb944f8cabb74e5851c28564facf887f08d5d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 11:39:54 +1100 Subject: [PATCH 315/469] Add shift_left/shift_right routines for MDSeqTracks --- avr/cores/megacommand/CommonTools/helpers.h | 4 +-- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 16 ++++------ avr/cores/megacommand/MCL/SeqPage.cpp | 35 +++++++++++++++++---- avr/cores/megacommand/MCL/SeqPage.h | 2 ++ avr/cores/megacommand/MCL/SeqPages.cpp | 2 +- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/avr/cores/megacommand/CommonTools/helpers.h b/avr/cores/megacommand/CommonTools/helpers.h index 5ebf2b4ef..8dd7e3757 100644 --- a/avr/cores/megacommand/CommonTools/helpers.h +++ b/avr/cores/megacommand/CommonTools/helpers.h @@ -45,8 +45,8 @@ extern const uint32_t _bvmasks32[]; * @{ **/ -#define ROTATE_LEFT(target, length) do { target = (target >> shift) | (target << (length - shift)); } while (false) -#define ROTATE_RIGHT(target, length) do { target = (target << shift) | (target >> (length - shift)); } while (false) +#define ROTATE_LEFT(target, length) do { target = (target >> 1) | (target << (length - 1)); } while (false) +#define ROTATE_RIGHT(target, length) do { target = (target << 1) | (target >> (length - 1)); } while (false) /** 32 bit bit accessing macro. **/ #define _BV32(i) (_bvmasks32[i]) diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index 755cb351a..6c2a26a3b 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -494,10 +494,6 @@ void MDSeqTrack::merge_from_md(MDTrack *md_track) { #define DIR_LEFT 0 #define DIR_RIGHT 1 -void MDSeqTrack::rotate(bool dir) { - -} - void MDSeqTrack::rotate_left() { ROTATE_LEFT(lock_mask, length); @@ -513,9 +509,9 @@ void MDSeqTrack::rotate_left() { for (uint8_t n = 0; n < length; n++) { if (n == 0) { new_pos = length - 1 - 1; } else { new_pos = n - 1; } - memcpy(&locks[0][new_pos], &(temp_data->locks[0][n]), NUM_MD_LOCKS); - conditional[new_pos] = temp_data->conditional[n]; - timing[new_pos] = temp_data->conditional[n]; + memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), NUM_MD_LOCKS); + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.conditional[n]; } } @@ -535,9 +531,9 @@ void MDSeqTrack::rotate_right() { for (uint8_t n = 0; n < length; n++) { if (n == length - 1) { new_pos = 0; } else { new_pos = n + 1; } - memcpy(&locks[0][new_pos], &(temp_data->locks[0][n]), NUM_MD_LOCKS); - conditional[new_pos] = temp_data->conditional[n]; - timing[new_pos] = temp_data->conditional[n]; + memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), NUM_MD_LOCKS); + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.conditional[n]; } } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 77649fa01..0712a3021 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -1,5 +1,5 @@ -#include "SeqPage.h" #include "MCL.h" +#include "SeqPage.h" uint8_t SeqPage::page_select = 0; @@ -13,6 +13,7 @@ uint8_t opt_trackid = 1; uint8_t opt_copy = 0; uint8_t opt_paste = 0; uint8_t opt_clear = 0; +uint8_t opt_shift = 0; static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; @@ -127,11 +128,11 @@ bool SeqPage::handleEvent(gui_event_t *event) { // A not-ignored WRITE (BUTTON4) release event triggers sequence page select if (EVENT_RELEASED(event, Buttons.BUTTON4)) { - page_select += 1; + page_select += 1; - if (page_select >= page_count) { - page_select = 0; - } + if (page_select >= page_count) { + page_select = 0; + } return true; } @@ -575,11 +576,33 @@ void opt_paste_track_handler() { mcl_clipboard.paste_sequencer(); } if (opt_paste == 1) { - mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_md_track); + mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, + last_md_track); } opt_paste = 0; } +void opt_shift_track_handler() { + switch (opt_shift) { + case 0: + mcl_seq.md_tracks[last_md_track].rotate_left(); + break; + case 1: + mcl_seq.md_tracks[last_md_track].rotate_right(); + break; + case 2: + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].rotate_left(); + } + break; + case 3: + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].rotate_right(); + } + break; + } +} + void SeqPage::config_as_trackedit() { seq_menu_page.menu.enable_entry(2, true); diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 49d09cf8b..dada41e3c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -22,6 +22,7 @@ extern uint8_t opt_resolution; extern uint8_t opt_copy; extern uint8_t opt_paste; extern uint8_t opt_clear; +extern uint8_t opt_shift; extern void opt_trackid_handler(); extern void opt_resolution_handler(); @@ -29,6 +30,7 @@ extern void opt_clear_track_handler(); extern void opt_clear_locks_handler(); extern void opt_copy_track_handler(); extern void opt_paste_track_handler(); +extern void opt_shift_track_handler(); class SeqPage : public LightPage { public: diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index ff14c16ce..42e67efbc 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -35,7 +35,7 @@ const menu_t<7> seq_menu_layout PROGMEM = { {"CLEAR", 0, 0, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {2, "LCKS."}, {2, "ALL"}}}, {"PASTE", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, {"TRACK RES.", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, - {"STEP SHIFT", 0, 0, 0, (uint8_t *)NULL, (Page *)NULL, NULL, {}}, + {"SHIFT", 0, 4, 4, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "L"}, {1, "R"}, {2,"L>ALL"}, {3, "R>ALL"}}}, }, NULL, }; From 2cc3af69a6f1545f9753bbf599756aad1d9bd385 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 11:57:27 +1100 Subject: [PATCH 316/469] Add rotation routines for ExtSeqTracks, fix MDSeqTracks --- avr/cores/megacommand/MCL/ExtSeqTrack.cpp | 43 +++++++++++++++++++++++ avr/cores/megacommand/MCL/ExtSeqTrack.h | 3 ++ avr/cores/megacommand/MCL/MDSeqTrack.cpp | 4 +-- avr/cores/megacommand/MCL/SeqPage.cpp | 34 ++++++++++++++---- 4 files changed, 76 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp index 7b3a99e27..774a1a2eb 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp @@ -327,3 +327,46 @@ void ExtSeqTrack::clear_track() { clear_ext_conditional(); buffer_notesoff(); } + +void ExtSeqTrack::rotate_left() { + + for (uint8_t a = 0; a < 4; a++) { + ROTATE_LEFT(lock_masks[a], length); + } + + int8_t new_pos = 0; + + ExtSeqTrackData temp_data; + + memcpy(&temp_data, this, sizeof(ExtSeqTrackData)); + + for (uint8_t n = 0; n < length; n++) { + if (n == 0) { new_pos = length - 1 - 1; } + else { new_pos = n - 1; } + memcpy(¬es[0][new_pos], &(temp_data.notes[0][n]), 4); + memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), 4); + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; + } +} +void ExtSeqTrack::rotate_right() { + + for (uint8_t a = 0; a < 4; a++) { + ROTATE_RIGHT(lock_masks[a], length); + } + + int8_t new_pos = 0; + + ExtSeqTrackData temp_data; + + memcpy(&temp_data, this, sizeof(ExtSeqTrackData)); + + for (uint8_t n = 0; n < length; n++) { + if (n == length - 1) { new_pos = 0; } + else { new_pos = n + 1; } + memcpy(¬es[0][new_pos], &(temp_data.notes[0][n]), 4); + memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), 4); + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; + } +} diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.h b/avr/cores/megacommand/MCL/ExtSeqTrack.h index 74759802b..dbd51f0ea 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.h +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.h @@ -106,6 +106,9 @@ class ExtSeqTrack : public ExtSeqTrackData { } *buf = 0; } + + void rotate_left(); + void rotate_right(); }; #endif /* EXTSEQTRACK_H__ */ diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index 6c2a26a3b..20cb0a83d 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -511,7 +511,7 @@ void MDSeqTrack::rotate_left() { else { new_pos = n - 1; } memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), NUM_MD_LOCKS); conditional[new_pos] = temp_data.conditional[n]; - timing[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; } } @@ -533,7 +533,7 @@ void MDSeqTrack::rotate_right() { else { new_pos = n + 1; } memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), NUM_MD_LOCKS); conditional[new_pos] = temp_data.conditional[n]; - timing[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; } } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 0712a3021..688f02487 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -585,20 +585,42 @@ void opt_paste_track_handler() { void opt_shift_track_handler() { switch (opt_shift) { case 0: - mcl_seq.md_tracks[last_md_track].rotate_left(); + if (opt_midi_device_capture == DEVICE_MD) { + mcl_seq.md_tracks[last_md_track].rotate_left(); + } else { + mcl_seq.ext_tracks[last_ext_track].rotate_left(); + } + break; case 1: - mcl_seq.md_tracks[last_md_track].rotate_right(); + if (opt_midi_device_capture == DEVICE_MD) { + mcl_seq.md_tracks[last_md_track].rotate_right(); + } else { + mcl_seq.ext_tracks[last_ext_track].rotate_right(); + } break; case 2: - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].rotate_left(); + if (opt_midi_device_capture == DEVICE_MD) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].rotate_left(); + } + } else { + for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { + mcl_seq.ext_tracks[n].rotate_left(); + } } break; case 3: - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].rotate_right(); + if (opt_midi_device_capture == DEVICE_MD) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].rotate_right(); + } + } else { + for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { + mcl_seq.ext_tracks[n].rotate_right(); + } } + break; } } From 82b0dce020000529f2925f79d9a128e63bb07e28 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 12:11:13 +1100 Subject: [PATCH 317/469] Add code to allow copying of Ext tracks, no transmission of sound supported --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index be3a18209..6610651bd 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -65,10 +65,18 @@ bool MCLClipBoard::copy_sequencer_track(uint8_t track) { close(); return false; } + if (track < NUM_MD_TRACKS) { memcpy(&(md_track->seq_data), &mcl_seq.md_tracks[track], sizeof(md_track->seq_data)); md_track->get_machine_from_kit(track, track); ret = mcl_sd.write_data(&temp_track, sizeof(MDTrackLight), &file); + } + + else { + memcpy(&temp_track, &mcl_seq.ext_tracks[track - NUM_MD_TRACKS],sizeof(ExtSeqTrackData)); + ret = mcl_sd.write_data(&temp_track, sizeof(ExtSeqTrack), &file); + } + close(); if (!ret) { DEBUG_PRINTLN("failed write"); } return ret; @@ -95,7 +103,12 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { int32_t offset = grid.get_slot_offset(source_track, GRID_LENGTH); ret = file.seekSet(offset); if (ret) { - ret = mcl_sd.read_data(&temp_track, sizeof(MDTrackLight), &file); + if (source_track < NUM_MD_TRACKS) { + ret = mcl_sd.read_data(&temp_track, sizeof(MDTrackLight), &file); + } + else { + ret = mcl_sd.read_data(&temp_track, sizeof(ExtSeqTrackData), &file); + } if (!ret) { DEBUG_PRINTLN("failed read"); close(); @@ -106,7 +119,7 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { DEBUG_PRINTLN("failed seek"); return false; } - + if (source_track < NUM_MD_TRACKS) { DEBUG_PRINTLN("loading seq track"); memcpy(&mcl_seq.md_tracks[track], &(md_track->seq_data), sizeof(md_track->seq_data)); @@ -123,6 +136,12 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { DEBUG_PRINTLN("sending seq track"); md_track->place_track_in_kit(track, track, &(MD.kit), true); MD.setMachine(track, &(md_track->machine)); + } + else if (track >= NUM_MD_TRACKS) { + memcpy(&mcl_seq.ext_tracks[track - NUM_MD_TRACKS], &(temp_track), + sizeof(ExtSeqTrackData)); + + } close(); return true; } From c92b0163924ab39d9ef6d32e1e8021947072d1a9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 12:19:36 +1100 Subject: [PATCH 318/469] Implement copying of ext tracks --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 119 ++++++++++++--------- avr/cores/megacommand/MCL/MCLClipBoard.h | 4 +- avr/cores/megacommand/MCL/SeqPage.cpp | 23 ++++ 3 files changed, 94 insertions(+), 52 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index 6610651bd..548e29f64 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -8,7 +8,10 @@ // Sequencer CLIPBOARD tracks are stored at the end of the GRID + 1. -#define CLIPBOARD_FILE_SIZE (uint32_t) GRID_SLOT_BYTES + (uint32_t)GRID_SLOT_BYTES *(uint32_t)(GRID_LENGTH + 1) * (uint32_t)(GRID_WIDTH + 1) +#define CLIPBOARD_FILE_SIZE \ + (uint32_t) GRID_SLOT_BYTES + \ + (uint32_t)GRID_SLOT_BYTES *(uint32_t)(GRID_LENGTH + 1) * \ + (uint32_t)(GRID_WIDTH + 1) bool MCLClipBoard::init() { DEBUG_PRINTLN("Creating clipboard"); @@ -22,28 +25,36 @@ bool MCLClipBoard::init() { } bool MCLClipBoard::open() { - DEBUG_PRINT_FN(); + DEBUG_PRINT_FN(); if (!file.open(FILENAME_CLIPBOARD, O_RDWR)) { init(); return file.open(FILENAME_CLIPBOARD, O_RDWR); } return true; -/* - if (file.fileSize() < CLIPBOARD_FILE_SIZE) { - file.close(); - SD.remove(FILENAME_CLIPBOARD); - DEBUG_PRINTLN("clipboard file size too small deleting"); - init(); - return file.open(FILENAME_CLIPBOARD, O_RDWR); - } */ + /* + if (file.fileSize() < CLIPBOARD_FILE_SIZE) { + file.close(); + SD.remove(FILENAME_CLIPBOARD); + DEBUG_PRINTLN("clipboard file size too small deleting"); + init(); + return file.open(FILENAME_CLIPBOARD, O_RDWR); + } */ } bool MCLClipBoard::close() { return file.close(); } -bool MCLClipBoard::copy_sequencer() { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (!copy_sequencer_track(n)) { - return false; +bool MCLClipBoard::copy_sequencer(uint8_t offset) { + if (offset < NUM_MD_TRACKS) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (!copy_sequencer_track(n)) { + return false; + } + } + } else { + for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { + if (!copy_sequencer_track(n + NUM_MD_TRACKS)) { + return false; + } } } return true; @@ -66,26 +77,37 @@ bool MCLClipBoard::copy_sequencer_track(uint8_t track) { return false; } if (track < NUM_MD_TRACKS) { - memcpy(&(md_track->seq_data), &mcl_seq.md_tracks[track], - sizeof(md_track->seq_data)); - md_track->get_machine_from_kit(track, track); - ret = mcl_sd.write_data(&temp_track, sizeof(MDTrackLight), &file); + memcpy(&(md_track->seq_data), &mcl_seq.md_tracks[track], + sizeof(md_track->seq_data)); + md_track->get_machine_from_kit(track, track); + ret = mcl_sd.write_data(&temp_track, sizeof(MDTrackLight), &file); } else { - memcpy(&temp_track, &mcl_seq.ext_tracks[track - NUM_MD_TRACKS],sizeof(ExtSeqTrackData)); - ret = mcl_sd.write_data(&temp_track, sizeof(ExtSeqTrack), &file); + memcpy(&temp_track, &mcl_seq.ext_tracks[track - NUM_MD_TRACKS], + sizeof(ExtSeqTrackData)); + ret = mcl_sd.write_data(&temp_track, sizeof(ExtSeqTrack), &file); } close(); - if (!ret) { DEBUG_PRINTLN("failed write"); } + if (!ret) { + DEBUG_PRINTLN("failed write"); + } return ret; } -bool MCLClipBoard::paste_sequencer() { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (!paste_sequencer_track(n, n)) { - return false; +bool MCLClipBoard::paste_sequencer(uint8_t offset) { + if (offset < NUM_MD_TRACKS) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (!paste_sequencer_track(n, n)) { + return false; + } + } + } else { + for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { + if (!paste_sequencer_track(n + offset, n + offset)) { + return false; + } } } return true; @@ -96,7 +118,7 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { bool ret; EmptyTrack temp_track; MDTrack *md_track = (MDTrack *)(&temp_track); - if (!open()) { + if (!open()) { DEBUG_PRINTLN("error could not open clipboard"); return; } @@ -104,10 +126,9 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { ret = file.seekSet(offset); if (ret) { if (source_track < NUM_MD_TRACKS) { - ret = mcl_sd.read_data(&temp_track, sizeof(MDTrackLight), &file); - } - else { - ret = mcl_sd.read_data(&temp_track, sizeof(ExtSeqTrackData), &file); + ret = mcl_sd.read_data(&temp_track, sizeof(MDTrackLight), &file); + } else { + ret = mcl_sd.read_data(&temp_track, sizeof(ExtSeqTrackData), &file); } if (!ret) { DEBUG_PRINTLN("failed read"); @@ -120,27 +141,25 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { return false; } if (source_track < NUM_MD_TRACKS) { - DEBUG_PRINTLN("loading seq track"); - memcpy(&mcl_seq.md_tracks[track], &(md_track->seq_data), - sizeof(md_track->seq_data)); - - if (md_track->machine.trigGroup == source_track) { - md_track->machine.trigGroup = 255; - } - if (md_track->machine.muteGroup == source_track) { - md_track->machine.muteGroup = 255; - } - if (md_track->machine.lfo.destinationTrack == source_track) { - md_track->machine.lfo.destinationTrack = track; - } - DEBUG_PRINTLN("sending seq track"); - md_track->place_track_in_kit(track, track, &(MD.kit), true); - MD.setMachine(track, &(md_track->machine)); - } - else if (track >= NUM_MD_TRACKS) { - memcpy(&mcl_seq.ext_tracks[track - NUM_MD_TRACKS], &(temp_track), - sizeof(ExtSeqTrackData)); + DEBUG_PRINTLN("loading seq track"); + memcpy(&mcl_seq.md_tracks[track], &(md_track->seq_data), + sizeof(md_track->seq_data)); + if (md_track->machine.trigGroup == source_track) { + md_track->machine.trigGroup = 255; + } + if (md_track->machine.muteGroup == source_track) { + md_track->machine.muteGroup = 255; + } + if (md_track->machine.lfo.destinationTrack == source_track) { + md_track->machine.lfo.destinationTrack = track; + } + DEBUG_PRINTLN("sending seq track"); + md_track->place_track_in_kit(track, track, &(MD.kit), true); + MD.setMachine(track, &(md_track->machine)); + } else if (track >= NUM_MD_TRACKS) { + memcpy(&mcl_seq.ext_tracks[track - NUM_MD_TRACKS], &(temp_track), + sizeof(ExtSeqTrackData)); } close(); return true; diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.h b/avr/cores/megacommand/MCL/MCLClipBoard.h index a28dccfe0..e5f91c155 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.h +++ b/avr/cores/megacommand/MCL/MCLClipBoard.h @@ -23,9 +23,9 @@ class MCLClipBoard { bool open(); bool close(); - bool copy_sequencer(); + bool copy_sequencer(uint8_t offset = 0); bool copy_sequencer_track(uint8_t track); - bool paste_sequencer(); + bool paste_sequencer(uint8_t offset = 0); bool paste_sequencer_track(uint8_t source_track, uint8_t track); bool copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 688f02487..ab8f37b22 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -562,22 +562,45 @@ void opt_clear_all_locks_handler() { void opt_copy_track_handler() { if (opt_copy == 2) { + if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.copy_sequencer(); + } + else { + mcl_clipboard.copy_sequencer(NUM_MD_TRACKS); + } } if (opt_copy == 1) { + if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.copy_track = last_md_track; mcl_clipboard.copy_sequencer_track(last_md_track); + } + else { + mcl_clipboard.copy_track = last_ext_track + NUM_MD_TRACKS; + mcl_clipboard.copy_sequencer_track(last_ext_track + NUM_MD_TRACKS); + } } opt_copy = 0; } void opt_paste_track_handler() { if (opt_paste == 2) { + if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.paste_sequencer(); + } + else { + mcl_clipboard.paste_sequencer(NUM_MD_TRACKS); + } + } if (opt_paste == 1) { + if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_md_track); + } + else { + mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, + last_ext_track + NUM_MD_TRACKS); + } } opt_paste = 0; } From 973e631d8436e16950c48485c7c950e9a3693f28 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 13:06:21 +1100 Subject: [PATCH 319/469] Fix locks and note shifting --- avr/cores/megacommand/MCL/ExtSeqTrack.cpp | 16 ++++++++++++---- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 11 +++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp index 774a1a2eb..b7fd1cb7c 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp @@ -343,8 +343,12 @@ void ExtSeqTrack::rotate_left() { for (uint8_t n = 0; n < length; n++) { if (n == 0) { new_pos = length - 1 - 1; } else { new_pos = n - 1; } - memcpy(¬es[0][new_pos], &(temp_data.notes[0][n]), 4); - memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), 4); + + for (uint8_t a = 0; a < 4; a++) { + notes[a][new_pos] = temp_data.notes[a][n]; + locks[a][new_pos] = temp_data.locks[a][n]; + } + conditional[new_pos] = temp_data.conditional[n]; timing[new_pos] = temp_data.timing[n]; } @@ -364,8 +368,12 @@ void ExtSeqTrack::rotate_right() { for (uint8_t n = 0; n < length; n++) { if (n == length - 1) { new_pos = 0; } else { new_pos = n + 1; } - memcpy(¬es[0][new_pos], &(temp_data.notes[0][n]), 4); - memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), 4); + + for (uint8_t a = 0; a < 4; a++) { + notes[a][new_pos] = temp_data.notes[a][n]; + locks[a][new_pos] = temp_data.locks[0][n]; + } + conditional[new_pos] = temp_data.conditional[n]; timing[new_pos] = temp_data.timing[n]; } diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index 20cb0a83d..be2686aa7 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -509,7 +509,10 @@ void MDSeqTrack::rotate_left() { for (uint8_t n = 0; n < length; n++) { if (n == 0) { new_pos = length - 1 - 1; } else { new_pos = n - 1; } - memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), NUM_MD_LOCKS); + + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + locks[a][new_pos] = temp_data.locks[a][n]; + } conditional[new_pos] = temp_data.conditional[n]; timing[new_pos] = temp_data.timing[n]; } @@ -531,7 +534,11 @@ void MDSeqTrack::rotate_right() { for (uint8_t n = 0; n < length; n++) { if (n == length - 1) { new_pos = 0; } else { new_pos = n + 1; } - memcpy(&locks[0][new_pos], &(temp_data.locks[0][n]), NUM_MD_LOCKS); + + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + locks[a][new_pos] = temp_data.locks[a][n]; + } + conditional[new_pos] = temp_data.conditional[n]; timing[new_pos] = temp_data.timing[n]; } From 6c05b2967eafcc18751b6fe7c5e05463e8683044 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 13:11:12 +1100 Subject: [PATCH 320/469] Fix shifting left on 0 --- avr/cores/megacommand/MCL/ExtSeqTrack.cpp | 2 +- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp index b7fd1cb7c..89b8973d7 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp @@ -341,7 +341,7 @@ void ExtSeqTrack::rotate_left() { memcpy(&temp_data, this, sizeof(ExtSeqTrackData)); for (uint8_t n = 0; n < length; n++) { - if (n == 0) { new_pos = length - 1 - 1; } + if (n == 0) { new_pos = length - 1; } else { new_pos = n - 1; } for (uint8_t a = 0; a < 4; a++) { diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index be2686aa7..cca0c57ff 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -507,7 +507,7 @@ void MDSeqTrack::rotate_left() { memcpy(&temp_data, this, sizeof(MDSeqTrackData)); for (uint8_t n = 0; n < length; n++) { - if (n == 0) { new_pos = length - 1 - 1; } + if (n == 0) { new_pos = length - 1; } else { new_pos = n - 1; } for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { From 57fb3de9c1b3da25107498b96900e2fafa80260d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 22:09:25 +1100 Subject: [PATCH 321/469] Clear should now work on Locks page --- avr/cores/megacommand/MCL/SeqPage.cpp | 4 ++-- avr/cores/megacommand/MCL/SeqPages.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index ab8f37b22..e0ed981fb 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -533,11 +533,11 @@ void opt_clear_track_handler() { void opt_clear_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - if (opt_clear) { + if (opt_clear == 2) { for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_locks(); } - } else { + } else if (opt_clear == 1) { mcl_seq.md_tracks[last_md_track].clear_locks(); } } else { diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 42e67efbc..b36584e07 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -29,13 +29,13 @@ SeqPtcPage seq_ptc_page(&ptc_param_oct, &ptc_param_finetune, &ptc_param_len, &pt const menu_t<7> seq_menu_layout PROGMEM = { "SEQ", { - {"TRACK SEL.", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, - {"COPY", 0, 3, 3, (uint8_t *)&opt_copy, (Page *)NULL, opt_copy_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"CLEAR", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"CLEAR", 0, 0, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {2, "LCKS."}, {2, "ALL"}}}, - {"PASTE", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"TRACK RES.", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, - {"SHIFT", 0, 4, 4, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "L"}, {1, "R"}, {2,"L>ALL"}, {3, "R>ALL"}}}, + {"TRACK SEL:", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, + {"COPY:", 0, 3, 3, (uint8_t *)&opt_copy, (Page *)NULL, opt_copy_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"CLEAR:", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"CLEAR:", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {1, "LCKS."}, {2, "ALL"}}}, + {"PASTE:", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"TRACK RES:", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, + {"SHIFT:", 0, 4, 4, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "L"}, {1, "R"}, {2,"L>ALL"}, {3, "R>ALL"}}}, }, NULL, }; From de498920767c1e3a45f74103e438f7325d4adadf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 22:47:35 +1100 Subject: [PATCH 322/469] Rotate Macros were leaving garbage bits if length was not multiple of standard type --- avr/cores/megacommand/MCL/ExtSeqTrack.cpp | 24 +++++++++++++++-------- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 20 ++++++++++--------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp index 89b8973d7..29b432058 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp @@ -330,16 +330,17 @@ void ExtSeqTrack::clear_track() { void ExtSeqTrack::rotate_left() { - for (uint8_t a = 0; a < 4; a++) { - ROTATE_LEFT(lock_masks[a], length); - } - int8_t new_pos = 0; ExtSeqTrackData temp_data; memcpy(&temp_data, this, sizeof(ExtSeqTrackData)); + for (uint8_t a = 0; a < 4; a++) { + lock_masks[a] = 0; + } + //oneshot_mask = 0; + for (uint8_t n = 0; n < length; n++) { if (n == 0) { new_pos = length - 1; } else { new_pos = n - 1; } @@ -347,6 +348,9 @@ void ExtSeqTrack::rotate_left() { for (uint8_t a = 0; a < 4; a++) { notes[a][new_pos] = temp_data.notes[a][n]; locks[a][new_pos] = temp_data.locks[a][n]; + if (IS_BIT_SET64(temp_data.lock_masks[a], n)) { + SET_BIT64(temp_data.lock_masks[a],new_pos); + } } conditional[new_pos] = temp_data.conditional[n]; @@ -355,16 +359,17 @@ void ExtSeqTrack::rotate_left() { } void ExtSeqTrack::rotate_right() { - for (uint8_t a = 0; a < 4; a++) { - ROTATE_RIGHT(lock_masks[a], length); - } - int8_t new_pos = 0; ExtSeqTrackData temp_data; memcpy(&temp_data, this, sizeof(ExtSeqTrackData)); + for (uint8_t a = 0; a < 4; a++) { + lock_masks[a] = 0; + } + //oneshot_mask = 0; + for (uint8_t n = 0; n < length; n++) { if (n == length - 1) { new_pos = 0; } else { new_pos = n + 1; } @@ -372,6 +377,9 @@ void ExtSeqTrack::rotate_right() { for (uint8_t a = 0; a < 4; a++) { notes[a][new_pos] = temp_data.notes[a][n]; locks[a][new_pos] = temp_data.locks[0][n]; + if (IS_BIT_SET64(temp_data.lock_masks[a], n)) { + SET_BIT64(temp_data.lock_masks[a],new_pos); + } } conditional[new_pos] = temp_data.conditional[n]; diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index cca0c57ff..30e1a7283 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -496,15 +496,14 @@ void MDSeqTrack::merge_from_md(MDTrack *md_track) { void MDSeqTrack::rotate_left() { - ROTATE_LEFT(lock_mask, length); - ROTATE_LEFT(pattern_mask, length); - ROTATE_LEFT(oneshot_mask, length); - int8_t new_pos = 0; MDSeqTrackData temp_data; memcpy(&temp_data, this, sizeof(MDSeqTrackData)); + oneshot_mask = 0; + pattern_mask = 0; + lock_mask = 0; for (uint8_t n = 0; n < length; n++) { if (n == 0) { new_pos = length - 1; } @@ -515,21 +514,22 @@ void MDSeqTrack::rotate_left() { } conditional[new_pos] = temp_data.conditional[n]; timing[new_pos] = temp_data.timing[n]; + if (IS_BIT_SET64(temp_data.pattern_mask, n)) { SET_BIT64(pattern_mask, new_pos); } + if (IS_BIT_SET64(temp_data.lock_mask, n)) { SET_BIT64(lock_mask, new_pos); } } } void MDSeqTrack::rotate_right() { - ROTATE_RIGHT(lock_mask, length); - ROTATE_RIGHT(pattern_mask, length); - ROTATE_RIGHT(oneshot_mask, length); - int8_t new_pos = 0; MDSeqTrackData temp_data; memcpy(&temp_data, this, sizeof(MDSeqTrackData)); + oneshot_mask = 0; + pattern_mask = 0; + lock_mask = 0; for (uint8_t n = 0; n < length; n++) { if (n == length - 1) { new_pos = 0; } @@ -541,6 +541,8 @@ void MDSeqTrack::rotate_right() { conditional[new_pos] = temp_data.conditional[n]; timing[new_pos] = temp_data.timing[n]; - } + if (IS_BIT_SET64(temp_data.pattern_mask, n)) { SET_BIT64(pattern_mask, new_pos); } + if (IS_BIT_SET64(temp_data.lock_mask, n)) { SET_BIT64(lock_mask, new_pos); } + } } From f7af908ba379364725bd06f44af78a48ddc3ad95 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 17 Nov 2019 23:11:25 +1100 Subject: [PATCH 323/469] Page indicator now responds to track length correctly --- avr/cores/megacommand/MCL/SeqPage.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index e0ed981fb..e37a7371d 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -677,9 +677,15 @@ void SeqPage::draw_page_index(bool show_page_index) { // draw page index uint8_t pidx_x = pidx_x0; bool blink = MidiClock.getBlinkHint(true); - // XXX should retrieve true track length - uint8_t playing_idx = (MidiClock.bar_counter - 1) % page_count; - uint8_t w = pidx_w; + + uint8_t playing_idx; + if (midi_device == DEVICE_MD) { + playing_idx = (mcl_seq.md_tracks[last_md_track].step_count) / 16; + } + else { + playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count) / 16; + } + uint8_t w = pidx_w; if (page_count == 8) { w /= 2; pidx_x -= 1; From 2ed808bb97312d31ec7e213025ef55f5a2aeedda Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 18 Nov 2019 16:42:45 +1100 Subject: [PATCH 324/469] Add step_menu, add routines for copying/pasting notes --- avr/cores/megacommand/MCL/MCLClipBoard.h | 4 +- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 22 ++++ avr/cores/megacommand/MCL/MDSeqTrack.h | 2 + avr/cores/megacommand/MCL/MDSeqTrackData.h | 10 ++ avr/cores/megacommand/MCL/SeqPage.cpp | 136 +++++++++++++-------- avr/cores/megacommand/MCL/SeqPage.h | 4 + avr/cores/megacommand/MCL/SeqPages.cpp | 14 +++ avr/cores/megacommand/MCL/SeqPages.h | 7 +- avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 +- 9 files changed, 147 insertions(+), 54 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.h b/avr/cores/megacommand/MCL/MCLClipBoard.h index e5f91c155..23f685191 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.h +++ b/avr/cores/megacommand/MCL/MCLClipBoard.h @@ -5,7 +5,7 @@ #include "Grid.h" #include "SdFat.h" #include "Shared.h" - +#include "MDSeqTrackData.h" #define FILENAME_CLIPBOARD "clipboard.tmp" class MCLClipBoard { @@ -19,6 +19,8 @@ class MCLClipBoard { File file; + MDSeqStep step; + bool init(); bool open(); bool close(); diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index 30e1a7283..edebd837d 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -546,3 +546,25 @@ void MDSeqTrack::rotate_right() { } } + +void MDSeqTrack::copy_step(uint8_t n, MDSeqStep *step) { + step->active = true; + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + step->locks[a] = locks[a][n]; + } + step->conditional = conditional[n]; + step->timing = timing[n]; + step->lock_mask = IS_BIT_SET64(lock_mask,n); + step->pattern_mask = IS_BIT_SET64(pattern_mask,n); +} + +void MDSeqTrack::paste_step(uint8_t n, MDSeqStep *step) { + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + locks[a][n] = step->locks[a]; + } + conditional[n] = step->conditional; + timing[n] = step->timing; + if (step->lock_mask) { SET_BIT64(lock_mask,n); } + if (step->pattern_mask) { SET_BIT64(pattern_mask,n); } +} + diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.h b/avr/cores/megacommand/MCL/MDSeqTrack.h index 46647d264..6da946460 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.h +++ b/avr/cores/megacommand/MCL/MDSeqTrack.h @@ -71,6 +71,8 @@ class MDSeqTrack : public MDSeqTrackData { void rotate_left(); void rotate_right(); + void copy_step(uint8_t n, MDSeqStep *step); + void paste_step(uint8_t n, MDSeqStep *step); }; #endif /* MDSEQTRACK_H__ */ diff --git a/avr/cores/megacommand/MCL/MDSeqTrackData.h b/avr/cores/megacommand/MCL/MDSeqTrackData.h index b620b82fe..19b15764a 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrackData.h +++ b/avr/cores/megacommand/MCL/MDSeqTrackData.h @@ -5,6 +5,16 @@ #define NUM_MD_LOCKS 4 +class MDSeqStep { +public: + bool active; + uint8_t locks[NUM_MD_LOCKS]; + uint8_t conditional; + uint8_t timing; + bool lock_mask; + bool pattern_mask; +}; + class MDSeqTrackData { public: uint8_t length; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index e37a7371d..fa64c87dc 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -7,6 +7,8 @@ uint8_t SeqPage::midi_device = DEVICE_MD; uint8_t SeqPage::page_count = 4; bool SeqPage::show_seq_menu = false; +bool SeqPage::show_step_menu = false; +uint8_t SeqPage::step_select = 255; uint8_t opt_resolution = 1; uint8_t opt_trackid = 1; @@ -145,37 +147,65 @@ bool SeqPage::handleEvent(gui_event_t *event) { #ifdef OLED_DISPLAY // activate show_seq_menu only if S2 press is not a key combination - if (EVENT_PRESSED(event, Buttons.BUTTON3) && !show_seq_menu && - !BUTTON_DOWN(Buttons.BUTTON4)) { - show_seq_menu = true; - // capture current midi_device value - opt_midi_device_capture = midi_device; - // capture current page. - opt_seqpage_capture = this; + if (EVENT_PRESSED(event, Buttons.BUTTON3) && !BUTTON_DOWN(Buttons.BUTTON4)) { + // If MD trig is held and BUTTON3 is pressed, launch note menu + if (!note_interface.notes_all_off_md() && (!show_step_menu)) { + uint8_t note = 255; + for (uint8_t n = 0; n < NUM_MD_TRACKS && note == 255; n++) { + if (note_interface.notes[n] == 1) { + note = n; + } + } + if (note == 255) { + return false; + } + step_select = note; + + opt_param1_capture = (MCLEncoder *)encoders[0]; + opt_param2_capture = (MCLEncoder *)encoders[1]; + encoders[0] = &step_menu_value_encoder; + encoders[1] = &step_menu_entry_encoder; + step_menu_page.init(); + show_step_menu = true; + } else if (!show_seq_menu) { + show_seq_menu = true; + // capture current midi_device value + opt_midi_device_capture = midi_device; + // capture current page. + opt_seqpage_capture = this; + + if (opt_midi_device_capture == DEVICE_MD) { + DEBUG_PRINTLN("okay using MD for length update"); + opt_trackid = last_md_track + 1; + opt_resolution = (mcl_seq.md_tracks[last_md_track].resolution); + } else { + opt_trackid = last_ext_track + 1; + opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); + } - if (opt_midi_device_capture == DEVICE_MD) { - DEBUG_PRINTLN("okay using MD for length update"); - opt_trackid = last_md_track + 1; - opt_resolution = (mcl_seq.md_tracks[last_md_track].resolution); - } else { - opt_trackid = last_ext_track + 1; - opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); + opt_param1_capture = (MCLEncoder *)encoders[0]; + opt_param2_capture = (MCLEncoder *)encoders[1]; + encoders[0] = &seq_menu_value_encoder; + encoders[1] = &seq_menu_entry_encoder; + seq_menu_page.init(); + return true; } - - opt_param1_capture = (MCLEncoder *)encoders[0]; - opt_param2_capture = (MCLEncoder *)encoders[1]; - encoders[0] = &seq_menu_value_encoder; - encoders[1] = &seq_menu_entry_encoder; - seq_menu_page.init(); - return true; } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { encoders[0] = opt_param1_capture; encoders[1] = opt_param2_capture; oled_display.clearDisplay(); + void (*row_func)(); + if (show_seq_menu) { + void (*row_func)() = + seq_menu_page.menu.get_row_function(seq_menu_page.encoders[1]->cur); + + } else if (show_step_menu) { + void (*row_func)() = + step_menu_page.menu.get_row_function(step_menu_page.encoders[1]->cur); + } show_seq_menu = false; - void (*row_func)() = - seq_menu_page.menu.get_row_function(seq_menu_page.encoders[1]->cur); + show_step_menu = false; if (row_func != NULL) { (*row_func)(); return true; @@ -562,21 +592,19 @@ void opt_clear_all_locks_handler() { void opt_copy_track_handler() { if (opt_copy == 2) { - if (opt_midi_device_capture == DEVICE_MD) { - mcl_clipboard.copy_sequencer(); - } - else { - mcl_clipboard.copy_sequencer(NUM_MD_TRACKS); + if (opt_midi_device_capture == DEVICE_MD) { + mcl_clipboard.copy_sequencer(); + } else { + mcl_clipboard.copy_sequencer(NUM_MD_TRACKS); } } if (opt_copy == 1) { if (opt_midi_device_capture == DEVICE_MD) { - mcl_clipboard.copy_track = last_md_track; - mcl_clipboard.copy_sequencer_track(last_md_track); - } - else { - mcl_clipboard.copy_track = last_ext_track + NUM_MD_TRACKS; - mcl_clipboard.copy_sequencer_track(last_ext_track + NUM_MD_TRACKS); + mcl_clipboard.copy_track = last_md_track; + mcl_clipboard.copy_sequencer_track(last_md_track); + } else { + mcl_clipboard.copy_track = last_ext_track + NUM_MD_TRACKS; + mcl_clipboard.copy_sequencer_track(last_ext_track + NUM_MD_TRACKS); } } opt_copy = 0; @@ -585,26 +613,31 @@ void opt_copy_track_handler() { void opt_paste_track_handler() { if (opt_paste == 2) { if (opt_midi_device_capture == DEVICE_MD) { - mcl_clipboard.paste_sequencer(); - } - else { - mcl_clipboard.paste_sequencer(NUM_MD_TRACKS); + mcl_clipboard.paste_sequencer(); + } else { + mcl_clipboard.paste_sequencer(NUM_MD_TRACKS); } - } if (opt_paste == 1) { if (opt_midi_device_capture == DEVICE_MD) { - mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, - last_md_track); + mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, + last_md_track); + } else { + mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, + last_ext_track + NUM_MD_TRACKS); } - else { - mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, - last_ext_track + NUM_MD_TRACKS); - } } opt_paste = 0; } +void opt_copy_step_handler() { + mcl_seq.md_tracks[last_md_track].copy_step(SeqPage::step_select, &mcl_clipboard.step); +} + +void opt_paste_step_handler() { + mcl_seq.md_tracks[last_md_track].paste_step(SeqPage::step_select, &mcl_clipboard.step); +} + void opt_shift_track_handler() { switch (opt_shift) { case 0: @@ -671,6 +704,10 @@ void SeqPage::loop() { } return; } + else if (show_step_menu) { + step_menu_page.loop(); + } + } void SeqPage::draw_page_index(bool show_page_index) { @@ -680,12 +717,11 @@ void SeqPage::draw_page_index(bool show_page_index) { uint8_t playing_idx; if (midi_device == DEVICE_MD) { - playing_idx = (mcl_seq.md_tracks[last_md_track].step_count) / 16; - } - else { - playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count) / 16; + playing_idx = (mcl_seq.md_tracks[last_md_track].step_count) / 16; + } else { + playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count) / 16; } - uint8_t w = pidx_w; + uint8_t w = pidx_w; if (page_count == 8) { w /= 2; pidx_x -= 1; diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index dada41e3c..e3ae12c38 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -31,6 +31,8 @@ extern void opt_clear_locks_handler(); extern void opt_copy_track_handler(); extern void opt_paste_track_handler(); extern void opt_shift_track_handler(); +extern void opt_paste_step_handler(); +extern void opt_copy_step_handler(); class SeqPage : public LightPage { public: @@ -38,7 +40,9 @@ class SeqPage : public LightPage { static uint8_t page_select; static uint8_t page_count; static uint8_t midi_device; + static uint8_t step_select; static bool show_seq_menu; + static bool show_step_menu; bool recording = false; bool display_page_index = true; diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index b36584e07..55e95df65 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -44,4 +44,18 @@ MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder seq_menu_entry_encoder(0, 9, ENCODER_RES_PAT); MenuPage<7> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); +const menu_t<2> step_menu_layout PROGMEM = { + "SEQ", + { + {"COPY STEP", 0, 0, 0, (uint8_t *)&opt_copy, (Page *)NULL, opt_copy_step_handler, {}}, + {"PASTE STEP", 0, 0, 0, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_step_handler, {}}, + }, + NULL, +}; + +MCLEncoder step_menu_value_encoder(0, 16, ENCODER_RES_PAT); +MCLEncoder step_menu_entry_encoder(0, 9, ENCODER_RES_PAT); +MenuPage<2> step_menu_page(&step_menu_layout, &step_menu_value_encoder, &step_menu_entry_encoder); + + //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 49143ac73..da6659981 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -4,8 +4,8 @@ #define SEQPAGES_H__ #include "MCLEncoder.h" -#include "MCLMenus.h" #include "MCLMemory.h" +#include "MCLMenus.h" #ifdef OLED_DISPLAY #define ENCODER_RES_SEQ 2 @@ -25,7 +25,6 @@ extern MCLEncoder seq_param4; extern MCLEncoder seq_lock1; extern MCLEncoder seq_lock2; - #include "SeqParamPage.h" #include "SeqPtcPage.h" #include "SeqRlckPage.h" @@ -57,6 +56,10 @@ extern MCLEncoder seq_menu_value_encoder; extern MCLEncoder seq_menu_entry_encoder; extern MenuPage<7> seq_menu_page; +extern MCLEncoder step_menu_value_encoder; +extern MCLEncoder step_menu_entry_encoder; +extern MenuPage<2> step_menu_page; + extern void mcl_save_sound(); extern void mcl_load_sound(); diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 65bfdde0a..c9faf8f0a 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -238,7 +238,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { midi_device = device; if (event->mask == EVENT_BUTTON_PRESSED) { if (device == DEVICE_A4) { - // GUI.setPage(&seq_extstep_page); + GUI.setPage(&seq_extstep_page); return true; } show_pitch = true; From 540511cc3b888d37dea13a3ae75777f989fcc0d9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 18 Nov 2019 16:45:43 +1100 Subject: [PATCH 325/469] forgot return true --- avr/cores/megacommand/MCL/SeqPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index fa64c87dc..ab30b6a28 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -167,6 +167,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { encoders[1] = &step_menu_entry_encoder; step_menu_page.init(); show_step_menu = true; + return true; } else if (!show_seq_menu) { show_seq_menu = true; // capture current midi_device value From baaf9c3e2aa223da8791313d6facb68070fe3644 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 18 Nov 2019 22:40:47 +1100 Subject: [PATCH 326/469] draw step menu --- avr/cores/megacommand/MCL/SeqPage.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index ab30b6a28..30fbf95ca 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -800,11 +800,16 @@ void SeqPage::display() { // draw info lines mcl_gui.draw_panel_labels(info1, info2); - if (show_seq_menu) { + if (show_seq_menu || show_step_menu) { constexpr uint8_t width = 52; oled_display.setFont(&TomThumb); oled_display.fillRect(128 - width - 2, 0, width + 2, 32, BLACK); + if (show_step_menu) { + step_menu_page.draw_menu(128 - width, 8, width); + } + if (show_seq_menu) { seq_menu_page.draw_menu(128 - width, 8, width); + } } } #endif From 30ac00aac256fde9627fd56e5fff99dc2d90e335 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 18 Nov 2019 23:09:33 +1100 Subject: [PATCH 327/469] bug fixes --- avr/cores/megacommand/MCL/SeqPage.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 30fbf95ca..24877950b 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -149,7 +149,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { // activate show_seq_menu only if S2 press is not a key combination if (EVENT_PRESSED(event, Buttons.BUTTON3) && !BUTTON_DOWN(Buttons.BUTTON4)) { // If MD trig is held and BUTTON3 is pressed, launch note menu - if (!note_interface.notes_all_off_md() && (!show_step_menu)) { + if ((note_interface.notes_count_off() != 0) && (!show_step_menu)) { uint8_t note = 255; for (uint8_t n = 0; n < NUM_MD_TRACKS && note == 255; n++) { if (note_interface.notes[n] == 1) { @@ -205,13 +205,17 @@ bool SeqPage::handleEvent(gui_event_t *event) { void (*row_func)() = step_menu_page.menu.get_row_function(step_menu_page.encoders[1]->cur); } - show_seq_menu = false; - show_step_menu = false; - if (row_func != NULL) { + if (row_func != NULL) { (*row_func)(); + show_seq_menu = false; + show_step_menu = false; return true; } - seq_menu_page.enter(); + if (show_seq_menu) { seq_menu_page.enter(); } + if (show_step_menu) { step_menu_page.enter(); } + + show_seq_menu = false; + show_step_menu = false; return true; } #else From e48702e8f41d198b688906d4abe64695197657a2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 18 Nov 2019 23:21:59 +1100 Subject: [PATCH 328/469] Don't change midi_device on SeqStepPage Previously we swapped in to ExtStep page if MIDI from port 2 received. However, we use port 2 to input to set pitch data for steps. --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index c9faf8f0a..7298502cd 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -235,10 +235,9 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { uint8_t trackid = event->source - 128; uint8_t step = trackid + (page_select * 16); - midi_device = device; if (event->mask == EVENT_BUTTON_PRESSED) { if (device == DEVICE_A4) { - GUI.setPage(&seq_extstep_page); +// GUI.setPage(&seq_extstep_page); return true; } show_pitch = true; From 65062d079f87ab8427a90a762fae96f64b0b03df Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 18 Nov 2019 23:25:58 +1100 Subject: [PATCH 329/469] Tentative: Don't clear locks when setting trig. Workflow, sometimes you want lockless trigs, and other times you want trigless locks. Probably best to leave lock clearing to the lock page. --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 7298502cd..86f36a650 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -319,7 +319,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { active_track.conditional[step] = condition; active_track.timing[step] = utiming; // upper - active_track.clear_step_locks(step); + //active_track.clear_step_locks(step); SET_BIT64(active_track.pattern_mask, step); } else { DEBUG_PRINTLN("Trying to clear"); From 57b76d9408e0bc5b6676e1b52278409e106c8726 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 12:27:19 +1100 Subject: [PATCH 330/469] Add trig mutes. Selected step must use page_select in calc. --- avr/cores/megacommand/MCL/SeqPage.cpp | 17 +++++++++++++++-- avr/cores/megacommand/MCL/SeqPage.h | 2 ++ avr/cores/megacommand/MCL/SeqPages.cpp | 12 +++++++----- avr/cores/megacommand/MCL/SeqPages.h | 2 +- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 24877950b..fa0c66760 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -636,13 +636,26 @@ void opt_paste_track_handler() { } void opt_copy_step_handler() { - mcl_seq.md_tracks[last_md_track].copy_step(SeqPage::step_select, &mcl_clipboard.step); + mcl_seq.md_tracks[last_md_track].copy_step(SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); } void opt_paste_step_handler() { - mcl_seq.md_tracks[last_md_track].paste_step(SeqPage::step_select, &mcl_clipboard.step); + mcl_seq.md_tracks[last_md_track].paste_step(SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); } +void opt_mute_step_handler() { + for (uint8_t n = 0; n < note_interface.notes[n]; n++) { + if (n == 1) { SET_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } + } +} + +void opt_umute_step_handler() { + for (uint8_t n = 0; n < note_interface.notes[n]; n++) { + if (n == 1) { CLEAR_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } + } +} + + void opt_shift_track_handler() { switch (opt_shift) { case 0: diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index e3ae12c38..da719d0eb 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -33,6 +33,8 @@ extern void opt_paste_track_handler(); extern void opt_shift_track_handler(); extern void opt_paste_step_handler(); extern void opt_copy_step_handler(); +extern void opt_mute_step_handler(); +extern void opt_unmute_step_handler(); class SeqPage : public LightPage { public: diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 55e95df65..9afee8261 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -44,18 +44,20 @@ MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder seq_menu_entry_encoder(0, 9, ENCODER_RES_PAT); MenuPage<7> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); -const menu_t<2> step_menu_layout PROGMEM = { - "SEQ", +const menu_t<4> step_menu_layout PROGMEM = { + "STP", { - {"COPY STEP", 0, 0, 0, (uint8_t *)&opt_copy, (Page *)NULL, opt_copy_step_handler, {}}, - {"PASTE STEP", 0, 0, 0, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_step_handler, {}}, + {"COPY STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_copy_step_handler, {}}, + {"PASTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_paste_step_handler, {}}, + {"MUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_mute_step_handler, {}}, + {"UNMUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_unmute_step_handler, {}}, }, NULL, }; MCLEncoder step_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder step_menu_entry_encoder(0, 9, ENCODER_RES_PAT); -MenuPage<2> step_menu_page(&step_menu_layout, &step_menu_value_encoder, &step_menu_entry_encoder); +MenuPage<4> step_menu_page(&step_menu_layout, &step_menu_value_encoder, &step_menu_entry_encoder); //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index da6659981..341c4b52f 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -58,7 +58,7 @@ extern MenuPage<7> seq_menu_page; extern MCLEncoder step_menu_value_encoder; extern MCLEncoder step_menu_entry_encoder; -extern MenuPage<2> step_menu_page; +extern MenuPage<4> step_menu_page; extern void mcl_save_sound(); extern void mcl_load_sound(); From 3cfb3682947eca7c227a570487004dda836696de Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 16:15:35 +1100 Subject: [PATCH 331/469] Fix linking, add reverse_track() Track Menu item --- avr/cores/megacommand/MCL/ExtSeqTrack.cpp | 29 ++++++++++++++++++++++ avr/cores/megacommand/MCL/ExtSeqTrack.h | 1 + avr/cores/megacommand/MCL/MDSeqTrack.cpp | 26 ++++++++++++++++++++ avr/cores/megacommand/MCL/MDSeqTrack.h | 1 + avr/cores/megacommand/MCL/SeqPage.cpp | 30 +++++++++++++++++++++-- avr/cores/megacommand/MCL/SeqPage.h | 2 ++ avr/cores/megacommand/MCL/SeqPages.cpp | 5 ++-- avr/cores/megacommand/MCL/SeqPages.h | 2 +- 8 files changed, 91 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp index 29b432058..69a44589f 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.cpp @@ -386,3 +386,32 @@ void ExtSeqTrack::rotate_right() { timing[new_pos] = temp_data.timing[n]; } } + +void ExtSeqTrack::reverse() { + + int8_t new_pos = 0; + + ExtSeqTrackData temp_data; + + memcpy(&temp_data, this, sizeof(ExtSeqTrackData)); + + for (uint8_t a = 0; a < 4; a++) { + lock_masks[a] = 0; + } + //oneshot_mask = 0; + + for (uint8_t n = 0; n < length; n++) { + new_pos = length - n - 1; + + for (uint8_t a = 0; a < 4; a++) { + notes[a][new_pos] = temp_data.notes[a][n]; + locks[a][new_pos] = temp_data.locks[0][n]; + if (IS_BIT_SET64(temp_data.lock_masks[a], n)) { + SET_BIT64(temp_data.lock_masks[a],new_pos); + } + } + + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; + } +} diff --git a/avr/cores/megacommand/MCL/ExtSeqTrack.h b/avr/cores/megacommand/MCL/ExtSeqTrack.h index dbd51f0ea..426b52901 100644 --- a/avr/cores/megacommand/MCL/ExtSeqTrack.h +++ b/avr/cores/megacommand/MCL/ExtSeqTrack.h @@ -109,6 +109,7 @@ class ExtSeqTrack : public ExtSeqTrackData { void rotate_left(); void rotate_right(); + void reverse(); }; #endif /* EXTSEQTRACK_H__ */ diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index edebd837d..fd62eeee0 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -547,6 +547,32 @@ void MDSeqTrack::rotate_right() { } } +void MDSeqTrack::reverse() { + + int8_t new_pos = 0; + + MDSeqTrackData temp_data; + + memcpy(&temp_data, this, sizeof(MDSeqTrackData)); + oneshot_mask = 0; + pattern_mask = 0; + lock_mask = 0; + + for (uint8_t n = 0; n < length; n++) { + new_pos = length - n - 1; + + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + locks[a][new_pos] = temp_data.locks[a][n]; + } + + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; + if (IS_BIT_SET64(temp_data.pattern_mask, n)) { SET_BIT64(pattern_mask, new_pos); } + if (IS_BIT_SET64(temp_data.lock_mask, n)) { SET_BIT64(lock_mask, new_pos); } + + } +} + void MDSeqTrack::copy_step(uint8_t n, MDSeqStep *step) { step->active = true; for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.h b/avr/cores/megacommand/MCL/MDSeqTrack.h index 6da946460..450610c20 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.h +++ b/avr/cores/megacommand/MCL/MDSeqTrack.h @@ -70,6 +70,7 @@ class MDSeqTrack : public MDSeqTrackData { void rotate_left(); void rotate_right(); + void reverse(); void copy_step(uint8_t n, MDSeqStep *step); void paste_step(uint8_t n, MDSeqStep *step); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index fa0c66760..5c1f1863f 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -16,6 +16,7 @@ uint8_t opt_copy = 0; uint8_t opt_paste = 0; uint8_t opt_clear = 0; uint8_t opt_shift = 0; +uint8_t opt_reverse = 0; static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; @@ -649,13 +650,12 @@ void opt_mute_step_handler() { } } -void opt_umute_step_handler() { +void opt_unmute_step_handler() { for (uint8_t n = 0; n < note_interface.notes[n]; n++) { if (n == 1) { CLEAR_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } } } - void opt_shift_track_handler() { switch (opt_shift) { case 0: @@ -699,6 +699,32 @@ void opt_shift_track_handler() { } } +void opt_reverse_track_handler() { + + if (opt_reverse == 1) { + if (opt_midi_device_capture == DEVICE_MD) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[last_md_track].reverse(); + } + } + else { + for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { + mcl_seq.ext_tracks[last_ext_track].reverse(); + } + } + } + + if (opt_reverse == 0) { + if (opt_midi_device_capture == DEVICE_MD) { + mcl_seq.md_tracks[last_md_track].reverse(); + } + else { + mcl_seq.ext_tracks[last_ext_track].reverse(); + } + } + +} + void SeqPage::config_as_trackedit() { seq_menu_page.menu.enable_entry(2, true); diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index da719d0eb..3bfa76253 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -23,6 +23,7 @@ extern uint8_t opt_copy; extern uint8_t opt_paste; extern uint8_t opt_clear; extern uint8_t opt_shift; +extern uint8_t opt_reverse; extern void opt_trackid_handler(); extern void opt_resolution_handler(); @@ -31,6 +32,7 @@ extern void opt_clear_locks_handler(); extern void opt_copy_track_handler(); extern void opt_paste_track_handler(); extern void opt_shift_track_handler(); +extern void opt_reverse_track_handler(); extern void opt_paste_step_handler(); extern void opt_copy_step_handler(); extern void opt_mute_step_handler(); diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 9afee8261..ae9f1614a 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -26,7 +26,7 @@ SeqExtStepPage seq_extstep_page(&seq_param1, &seq_param2, &seq_param3, SeqPtcPage seq_ptc_page(&ptc_param_oct, &ptc_param_finetune, &ptc_param_len, &ptc_param_scale); -const menu_t<7> seq_menu_layout PROGMEM = { +const menu_t<8> seq_menu_layout PROGMEM = { "SEQ", { {"TRACK SEL:", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, @@ -36,13 +36,14 @@ const menu_t<7> seq_menu_layout PROGMEM = { {"PASTE:", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, {"TRACK RES:", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, {"SHIFT:", 0, 4, 4, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "L"}, {1, "R"}, {2,"L>ALL"}, {3, "R>ALL"}}}, + {"REVERSE:", 0, 2, 2, (uint8_t *)&opt_reverse, (Page *)NULL, opt_reverse_track_handler, { {0, "TRK"}, {1, "ALL"} }}, }, NULL, }; MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder seq_menu_entry_encoder(0, 9, ENCODER_RES_PAT); -MenuPage<7> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); +MenuPage<8> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); const menu_t<4> step_menu_layout PROGMEM = { "STP", diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 341c4b52f..9511de334 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -54,7 +54,7 @@ extern SeqPtcPage seq_ptc_page; extern MCLEncoder seq_menu_value_encoder; extern MCLEncoder seq_menu_entry_encoder; -extern MenuPage<7> seq_menu_page; +extern MenuPage<8> seq_menu_page; extern MCLEncoder step_menu_value_encoder; extern MCLEncoder step_menu_entry_encoder; From e07a2e4b9fe811f7c747af7015f38cde48babd3f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 16:28:30 +1100 Subject: [PATCH 332/469] Draw mute mask on draw_trigs --- avr/cores/megacommand/MCL/MCLGUI.cpp | 7 ++++++- avr/cores/megacommand/MCL/MCLGUI.h | 2 +- avr/cores/megacommand/MCL/SeqPage.cpp | 6 +++--- avr/cores/megacommand/MCL/SeqPage.h | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 800e9a774..bbfec921c 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -444,7 +444,7 @@ void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, uint64_t pattern_mask, uint8_t step_count, - uint8_t length) { + uint8_t length, uint64_t mute_mask) { for (int i = 0; i < 16; i++) { uint8_t idx = i + offset; @@ -471,6 +471,11 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, */ } else { oled_display.drawRect(x, y, seq_w, trig_h, WHITE); + if (IS_BIT_SET64(mute_mask, i + offset)) { + oled_display.drawPixel(x + 1, y + 1, WHITE); + oled_display.drawPixel(x + 2, y + 2, WHITE); + oled_display.drawPixel(x + 3, y + 3, WHITE); + } } } diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index bfc55e451..4df9aef9f 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -62,7 +62,7 @@ class MCLGUI { uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask); void draw_trigs(uint8_t x, uint8_t y, uint8_t offset, uint64_t pattern_mask, - uint8_t step_count, uint8_t length); + uint8_t step_count, uint8_t length, uint64_t mute_mask = 0); void draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, uint8_t ext_trackid, bool show_current_step); void draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 5c1f1863f..847080e6b 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -474,9 +474,9 @@ void SeqPage::draw_lock_mask(uint8_t offset, bool show_current_step) { void SeqPage::draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, - bool show_current_step) { + bool show_current_step, uint64_t mute_mask) { mcl_gui.draw_trigs(MCLGUI::seq_x0, MCLGUI::trig_y, offset, pattern_mask, - step_count, length); + step_count, length, mute_mask); } void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, @@ -485,7 +485,7 @@ void SeqPage::draw_pattern_mask(uint8_t offset, uint8_t device, auto &active_track = mcl_seq.md_tracks[last_md_track]; draw_pattern_mask(offset, active_track.pattern_mask, active_track.step_count, active_track.length, - show_current_step); + show_current_step, active_track.oneshot_mask); } #ifdef EXT_TRACKS else { diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 3bfa76253..36b90e12a 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -64,7 +64,7 @@ class SeqPage : public LightPage { void create_chars_seq(); void draw_lock_mask(uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step = true); void draw_lock_mask(uint8_t offset, bool show_current_step = true); - void draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step = true); + void draw_pattern_mask(uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, bool show_current_step = true, uint64_t mute_mask = 0); void draw_pattern_mask(uint8_t offset, uint8_t device, bool show_current_step = true); void draw_knob_frame(); void draw_knob(uint8_t i, const char* title, const char* text); From 3a8d931cc2cf9c2b91dae3d69f5e9f6d4e5f1705 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 17:34:57 +1100 Subject: [PATCH 333/469] Draw keyboard on ptc select --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 86f36a650..a68f0a1a6 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -2,6 +2,7 @@ #include "MCL.h" #define MIDI_OMNI_MODE 17 +#define NUM_KEYS 32 void SeqStepPage::setup() { SeqPage::setup(); } void SeqStepPage::config() { @@ -172,8 +173,15 @@ void SeqStepPage::display() { draw_knob(3, "PTC", K); } } + if (mcl_gui.show_encoder_value(&seq_param4)) { + uint64_t note_mask = 0; + SET_BIT64(note_mask, seq_param4.cur - 24 * (seq_param4.cur / 24) ); + mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); + } + else { draw_lock_mask((page_select * 16), DEVICE_MD); draw_pattern_mask((page_select * 16), DEVICE_MD); + } SeqPage::display(); oled_display.display(); From 1791ba75a2ca781df01f354ca9dc296a4aec69fe Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 20:58:32 +1100 Subject: [PATCH 334/469] bug fixes for draw_keyboard() --- avr/cores/megacommand/MCL/MCLGUI.h | 7 +++++++ avr/cores/megacommand/MCL/SeqStepPage.cpp | 9 ++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 4df9aef9f..66d15b394 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -77,6 +77,13 @@ class MCLGUI { void draw_knob(uint8_t i, const char *title, const char *text); void draw_knob(uint8_t i, Encoder *enc, const char *name); + void init_encoders_used_clock() { + + for (uint8_t n = 0; n < GUI_NUM_ENCODERS; n++) { + ((LightPage *)GUI.currentPage())->encoders_used_clock[n] = + slowclock - SHOW_VALUE_TIMEOUT - 1; + } + } static constexpr uint8_t seq_w = 5; static constexpr uint8_t led_h = 3; static constexpr uint8_t trig_h = 5; diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index a68f0a1a6..93e8214b0 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -173,9 +173,10 @@ void SeqStepPage::display() { draw_knob(3, "PTC", K); } } - if (mcl_gui.show_encoder_value(&seq_param4)) { + if (mcl_gui.show_encoder_value(&seq_param4) && (seq_param4.cur > 0) && (!note_interface.notes_all_off_md())) { uint64_t note_mask = 0; - SET_BIT64(note_mask, seq_param4.cur - 24 * (seq_param4.cur / 24) ); + uint8_t note = seq_param4.cur - 3; + SET_BIT64(note_mask, note - 24 * (note / 24) ); mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); } else { @@ -320,7 +321,9 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { }*/ // conditional_timing[cur_col][(track + (seq_param2.cur * 16))] = // condition; //lower - + if (note_interface.notes_all_off_md()) { + mcl_gui.init_encoders_used_clock(); + } if (!IS_BIT_SET64(active_track.pattern_mask, step)) { uint8_t utiming = (seq_param2.cur + 0); uint8_t condition = seq_param1.cur; From 9bace66f53e646f3f3f2cafee500fef57e228db3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 21:30:05 +1100 Subject: [PATCH 335/469] Fix bug opening track menu --- avr/cores/megacommand/MCL/NoteInterface.cpp | 10 ++++++++++ avr/cores/megacommand/MCL/NoteInterface.h | 1 + avr/cores/megacommand/MCL/SeqPage.cpp | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/NoteInterface.cpp b/avr/cores/megacommand/MCL/NoteInterface.cpp index 46e91d207..46f8ea38f 100644 --- a/avr/cores/megacommand/MCL/NoteInterface.cpp +++ b/avr/cores/megacommand/MCL/NoteInterface.cpp @@ -109,6 +109,16 @@ bool NoteInterface::notes_all_off() { return all_notes_off; } +uint8_t NoteInterface::notes_count_on() { + uint8_t a = 0; + for (uint8_t i = 0; i < 20; i++) { + if (notes[i] == 1) { + a++; + } + } + return a; +} + uint8_t NoteInterface::notes_count_off() { uint8_t a = 0; for (uint8_t i = 0; i < 20; i++) { diff --git a/avr/cores/megacommand/MCL/NoteInterface.h b/avr/cores/megacommand/MCL/NoteInterface.h index 406300e43..9f9466b6e 100644 --- a/avr/cores/megacommand/MCL/NoteInterface.h +++ b/avr/cores/megacommand/MCL/NoteInterface.h @@ -43,6 +43,7 @@ class NoteInterface { bool notes_all_off_md(); uint8_t notes_count_off(); uint8_t notes_count(); + uint8_t notes_count_on(); NoteInterfaceMidiEvents ni_midi_events; }; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 847080e6b..43ccb3028 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -150,7 +150,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { // activate show_seq_menu only if S2 press is not a key combination if (EVENT_PRESSED(event, Buttons.BUTTON3) && !BUTTON_DOWN(Buttons.BUTTON4)) { // If MD trig is held and BUTTON3 is pressed, launch note menu - if ((note_interface.notes_count_off() != 0) && (!show_step_menu)) { + if ((note_interface.notes_count_on() != 0) && (!show_step_menu)) { uint8_t note = 255; for (uint8_t n = 0; n < NUM_MD_TRACKS && note == 255; n++) { if (note_interface.notes[n] == 1) { From c5df4da66d88865c99f75d52e2bda65e08b70c35 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 21:38:54 +1100 Subject: [PATCH 336/469] Bug fix: reverse all did not work, mute did not work, add -- option to shift/reverse --- avr/cores/megacommand/MCL/SeqPage.cpp | 24 ++++++++++++------------ avr/cores/megacommand/MCL/SeqPages.cpp | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 43ccb3028..6a16f664a 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -645,20 +645,20 @@ void opt_paste_step_handler() { } void opt_mute_step_handler() { - for (uint8_t n = 0; n < note_interface.notes[n]; n++) { - if (n == 1) { SET_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (note_interface.notes[n] == 1) { SET_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } } } void opt_unmute_step_handler() { - for (uint8_t n = 0; n < note_interface.notes[n]; n++) { - if (n == 1) { CLEAR_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (note_interface.notes[n] == 1) { CLEAR_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } } } void opt_shift_track_handler() { switch (opt_shift) { - case 0: + case 1: if (opt_midi_device_capture == DEVICE_MD) { mcl_seq.md_tracks[last_md_track].rotate_left(); } else { @@ -666,14 +666,14 @@ void opt_shift_track_handler() { } break; - case 1: + case 2: if (opt_midi_device_capture == DEVICE_MD) { mcl_seq.md_tracks[last_md_track].rotate_right(); } else { mcl_seq.ext_tracks[last_ext_track].rotate_right(); } break; - case 2: + case 3: if (opt_midi_device_capture == DEVICE_MD) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { mcl_seq.md_tracks[n].rotate_left(); @@ -684,7 +684,7 @@ void opt_shift_track_handler() { } } break; - case 3: + case 4: if (opt_midi_device_capture == DEVICE_MD) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { mcl_seq.md_tracks[n].rotate_right(); @@ -701,20 +701,20 @@ void opt_shift_track_handler() { void opt_reverse_track_handler() { - if (opt_reverse == 1) { + if (opt_reverse == 2) { if (opt_midi_device_capture == DEVICE_MD) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[last_md_track].reverse(); + mcl_seq.md_tracks[n].reverse(); } } else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { - mcl_seq.ext_tracks[last_ext_track].reverse(); + mcl_seq.ext_tracks[n].reverse(); } } } - if (opt_reverse == 0) { + if (opt_reverse == 1) { if (opt_midi_device_capture == DEVICE_MD) { mcl_seq.md_tracks[last_md_track].reverse(); } diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index ae9f1614a..e92619031 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -35,8 +35,8 @@ const menu_t<8> seq_menu_layout PROGMEM = { {"CLEAR:", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {1, "LCKS."}, {2, "ALL"}}}, {"PASTE:", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, {"TRACK RES:", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, - {"SHIFT:", 0, 4, 4, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "L"}, {1, "R"}, {2,"L>ALL"}, {3, "R>ALL"}}}, - {"REVERSE:", 0, 2, 2, (uint8_t *)&opt_reverse, (Page *)NULL, opt_reverse_track_handler, { {0, "TRK"}, {1, "ALL"} }}, + {"SHIFT:", 0, 5, 5, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "--",}, {1, "L"}, {2, "R"}, {3,"L>ALL"}, {4, "R>ALL"}}}, + {"REVERSE:", 0, 3, 3, (uint8_t *)&opt_reverse, (Page *)NULL, opt_reverse_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"} }}, }, NULL, }; From 38f695bbbe92ce6bd77e694a24549ee99d08cecf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 21:52:49 +1100 Subject: [PATCH 337/469] Fix mute handling and display, working --- avr/cores/megacommand/MCL/MCLGUI.cpp | 20 ++--- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 94 ++++++++++++++---------- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index bbfec921c..1cc559b98 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -160,7 +160,7 @@ void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, } if (s_progress_count == s_progress_speed) { s_progress_cookie = bitmask; - ROTATE_LEFT(s_progress_cookie,8); + ROTATE_LEFT(s_progress_cookie, 8); s_progress_count = 0; } s_progress_count++; @@ -459,8 +459,16 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, } else { if (IS_BIT_SET64(pattern_mask, i + offset) && ((i + offset != step_count) || (MidiClock.state != 2))) { + oled_display.drawRect(x, y, seq_w, trig_h, WHITE); + if (IS_BIT_SET64(mute_mask, i + offset)) { + oled_display.drawPixel(x + 1, y + 1, WHITE); + oled_display.drawPixel(x + 2, y + 2, WHITE); + oled_display.drawPixel(x + 3, y + 3, WHITE); + } /*If the bit is set, there is a trigger at this position. */ - oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + else { + oled_display.fillRect(x, y, seq_w, trig_h, WHITE); + } /* oled_display.drawRect(x, y, seq_w, trig_h, WHITE); oled_display.drawPixel(x + 1, y + 1, WHITE); @@ -471,11 +479,6 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, */ } else { oled_display.drawRect(x, y, seq_w, trig_h, WHITE); - if (IS_BIT_SET64(mute_mask, i + offset)) { - oled_display.drawPixel(x + 1, y + 1, WHITE); - oled_display.drawPixel(x + 2, y + 2, WHITE); - oled_display.drawPixel(x + 3, y + 3, WHITE); - } } } @@ -797,8 +800,7 @@ const unsigned char icon_sound[] PROGMEM = { 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc0, 0xe0, 0x01, 0xc3, 0xe0, 0x01, 0xc7, 0xe0, 0x01, 0xc7, 0xe0, 0x07, 0xc7, 0xc0, 0x0f, 0xc3, 0x80, 0x0f, 0xc0, 0x00, - 0x0f, 0x80, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 -}; + 0x0f, 0x80, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00}; // 'md_rev', 34x24px const unsigned char icon_md[] PROGMEM = { diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index fd62eeee0..8e2adce88 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -184,11 +184,11 @@ void MDSeqTrack::trig_conditional(uint8_t condition) { bool send_trig = false; switch (condition) { case 0: - send_trig = true; - break; case 1: - send_trig = true; - break; + if (!IS_BIT_SET64(oneshot_mask, step_count)) { + send_trig = true; + } + break; case 2: if (!IS_BIT_SET(iterations, 0)) { send_trig = true; @@ -506,18 +506,24 @@ void MDSeqTrack::rotate_left() { lock_mask = 0; for (uint8_t n = 0; n < length; n++) { - if (n == 0) { new_pos = length - 1; } - else { new_pos = n - 1; } + if (n == 0) { + new_pos = length - 1; + } else { + new_pos = n - 1; + } - for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { - locks[a][new_pos] = temp_data.locks[a][n]; - } - conditional[new_pos] = temp_data.conditional[n]; - timing[new_pos] = temp_data.timing[n]; - if (IS_BIT_SET64(temp_data.pattern_mask, n)) { SET_BIT64(pattern_mask, new_pos); } - if (IS_BIT_SET64(temp_data.lock_mask, n)) { SET_BIT64(lock_mask, new_pos); } + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + locks[a][new_pos] = temp_data.locks[a][n]; + } + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; + if (IS_BIT_SET64(temp_data.pattern_mask, n)) { + SET_BIT64(pattern_mask, new_pos); + } + if (IS_BIT_SET64(temp_data.lock_mask, n)) { + SET_BIT64(lock_mask, new_pos); + } } - } void MDSeqTrack::rotate_right() { @@ -532,18 +538,24 @@ void MDSeqTrack::rotate_right() { lock_mask = 0; for (uint8_t n = 0; n < length; n++) { - if (n == length - 1) { new_pos = 0; } - else { new_pos = n + 1; } - - for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { - locks[a][new_pos] = temp_data.locks[a][n]; - } + if (n == length - 1) { + new_pos = 0; + } else { + new_pos = n + 1; + } - conditional[new_pos] = temp_data.conditional[n]; - timing[new_pos] = temp_data.timing[n]; - if (IS_BIT_SET64(temp_data.pattern_mask, n)) { SET_BIT64(pattern_mask, new_pos); } - if (IS_BIT_SET64(temp_data.lock_mask, n)) { SET_BIT64(lock_mask, new_pos); } + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + locks[a][new_pos] = temp_data.locks[a][n]; + } + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; + if (IS_BIT_SET64(temp_data.pattern_mask, n)) { + SET_BIT64(pattern_mask, new_pos); + } + if (IS_BIT_SET64(temp_data.lock_mask, n)) { + SET_BIT64(lock_mask, new_pos); + } } } @@ -559,17 +571,20 @@ void MDSeqTrack::reverse() { lock_mask = 0; for (uint8_t n = 0; n < length; n++) { - new_pos = length - n - 1; - - for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { - locks[a][new_pos] = temp_data.locks[a][n]; - } + new_pos = length - n - 1; - conditional[new_pos] = temp_data.conditional[n]; - timing[new_pos] = temp_data.timing[n]; - if (IS_BIT_SET64(temp_data.pattern_mask, n)) { SET_BIT64(pattern_mask, new_pos); } - if (IS_BIT_SET64(temp_data.lock_mask, n)) { SET_BIT64(lock_mask, new_pos); } + for (uint8_t a = 0; a < NUM_MD_LOCKS; a++) { + locks[a][new_pos] = temp_data.locks[a][n]; + } + conditional[new_pos] = temp_data.conditional[n]; + timing[new_pos] = temp_data.timing[n]; + if (IS_BIT_SET64(temp_data.pattern_mask, n)) { + SET_BIT64(pattern_mask, new_pos); + } + if (IS_BIT_SET64(temp_data.lock_mask, n)) { + SET_BIT64(lock_mask, new_pos); + } } } @@ -580,8 +595,8 @@ void MDSeqTrack::copy_step(uint8_t n, MDSeqStep *step) { } step->conditional = conditional[n]; step->timing = timing[n]; - step->lock_mask = IS_BIT_SET64(lock_mask,n); - step->pattern_mask = IS_BIT_SET64(pattern_mask,n); + step->lock_mask = IS_BIT_SET64(lock_mask, n); + step->pattern_mask = IS_BIT_SET64(pattern_mask, n); } void MDSeqTrack::paste_step(uint8_t n, MDSeqStep *step) { @@ -590,7 +605,10 @@ void MDSeqTrack::paste_step(uint8_t n, MDSeqStep *step) { } conditional[n] = step->conditional; timing[n] = step->timing; - if (step->lock_mask) { SET_BIT64(lock_mask,n); } - if (step->pattern_mask) { SET_BIT64(pattern_mask,n); } + if (step->lock_mask) { + SET_BIT64(lock_mask, n); + } + if (step->pattern_mask) { + SET_BIT64(pattern_mask, n); + } } - From 138cf1d50dd1a1c9f62d0249d2883411c24387cf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 21:55:33 +1100 Subject: [PATCH 338/469] Don't display keyboard on selection of trig with ptc --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 34 ++++++++++++----------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 93e8214b0..af560e6ce 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -1,5 +1,5 @@ -#include "SeqStepPage.h" #include "MCL.h" +#include "SeqStepPage.h" #define MIDI_OMNI_MODE 17 #define NUM_KEYS 32 @@ -146,10 +146,10 @@ void SeqStepPage::display() { K[3] = '\0'; if (seq_param2.getValue() == 0) { } else if ((seq_param2.getValue() < 12) && (seq_param2.getValue() != 0)) { - itoa(12 - seq_param2.getValue(), K+1, 10); + itoa(12 - seq_param2.getValue(), K + 1, 10); } else { K[0] = '+'; - itoa(seq_param2.getValue() - 12, K+1, 10); + itoa(seq_param2.getValue() - 12, K + 1, 10); } draw_knob(1, "UTIM", K); @@ -173,15 +173,15 @@ void SeqStepPage::display() { draw_knob(3, "PTC", K); } } - if (mcl_gui.show_encoder_value(&seq_param4) && (seq_param4.cur > 0) && (!note_interface.notes_all_off_md())) { - uint64_t note_mask = 0; - uint8_t note = seq_param4.cur - 3; - SET_BIT64(note_mask, note - 24 * (note / 24) ); - mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); + if (mcl_gui.show_encoder_value(&seq_param4) && (seq_param4.cur > 0) && (!note_interface.notes_all_off_md() && (!show_seq_menu) && (!show_step_menu))) { + uint64_t note_mask = 0; + uint8_t note = seq_param4.cur - 3; + SET_BIT64(note_mask, note - 24 * (note / 24)); + mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); } else { - draw_lock_mask((page_select * 16), DEVICE_MD); - draw_pattern_mask((page_select * 16), DEVICE_MD); + draw_lock_mask((page_select * 16), DEVICE_MD); + draw_pattern_mask((page_select * 16), DEVICE_MD); } SeqPage::display(); @@ -246,7 +246,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_PRESSED) { if (device == DEVICE_A4) { -// GUI.setPage(&seq_extstep_page); + // GUI.setPage(&seq_extstep_page); return true; } show_pitch = true; @@ -277,6 +277,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { } else { seq_param4.cur = note_num; } + seq_param4.old = seq_param4.cur; } // Micro if (utiming == 0) { @@ -322,7 +323,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { // conditional_timing[cur_col][(track + (seq_param2.cur * 16))] = // condition; //lower if (note_interface.notes_all_off_md()) { - mcl_gui.init_encoders_used_clock(); + mcl_gui.init_encoders_used_clock(); } if (!IS_BIT_SET64(active_track.pattern_mask, step)) { uint8_t utiming = (seq_param2.cur + 0); @@ -330,7 +331,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { active_track.conditional[step] = condition; active_track.timing[step] = utiming; // upper - //active_track.clear_step_locks(step); + // active_track.clear_step_locks(step); SET_BIT64(active_track.pattern_mask, step); } else { DEBUG_PRINTLN("Trying to clear"); @@ -351,9 +352,10 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { } // end TI events if (EVENT_PRESSED(event, Buttons.ENCODER1)) { -// if (note_interface.notes_all_off() || (note_interface.notes_count() == 0)) { -// GUI.setPage(&grid_page); -// } + // if (note_interface.notes_all_off() || (note_interface.notes_count() == + // 0)) { + // GUI.setPage(&grid_page); + // } return true; } From a8ccf3a0b2261174763fd66f4a8e6b42bd88a953 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 22:02:54 +1100 Subject: [PATCH 339/469] Consolidate mute/unmute step in to one menu item --- avr/cores/megacommand/MCL/SeqPage.cpp | 8 +------- avr/cores/megacommand/MCL/SeqPage.h | 1 - avr/cores/megacommand/MCL/SeqPages.cpp | 5 ++--- avr/cores/megacommand/MCL/SeqPages.h | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 6a16f664a..7535c1ad1 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -646,13 +646,7 @@ void opt_paste_step_handler() { void opt_mute_step_handler() { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (note_interface.notes[n] == 1) { SET_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } - } -} - -void opt_unmute_step_handler() { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (note_interface.notes[n] == 1) { CLEAR_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } + if (note_interface.notes[n] == 1) { TOGGLE_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } } } diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 36b90e12a..44a780341 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -36,7 +36,6 @@ extern void opt_reverse_track_handler(); extern void opt_paste_step_handler(); extern void opt_copy_step_handler(); extern void opt_mute_step_handler(); -extern void opt_unmute_step_handler(); class SeqPage : public LightPage { public: diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index e92619031..364ef5e21 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -45,20 +45,19 @@ MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder seq_menu_entry_encoder(0, 9, ENCODER_RES_PAT); MenuPage<8> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); -const menu_t<4> step_menu_layout PROGMEM = { +const menu_t<3> step_menu_layout PROGMEM = { "STP", { {"COPY STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_copy_step_handler, {}}, {"PASTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_paste_step_handler, {}}, {"MUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_mute_step_handler, {}}, - {"UNMUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_unmute_step_handler, {}}, }, NULL, }; MCLEncoder step_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder step_menu_entry_encoder(0, 9, ENCODER_RES_PAT); -MenuPage<4> step_menu_page(&step_menu_layout, &step_menu_value_encoder, &step_menu_entry_encoder); +MenuPage<3> step_menu_page(&step_menu_layout, &step_menu_value_encoder, &step_menu_entry_encoder); //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 9511de334..3eab74615 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -58,7 +58,7 @@ extern MenuPage<8> seq_menu_page; extern MCLEncoder step_menu_value_encoder; extern MCLEncoder step_menu_entry_encoder; -extern MenuPage<4> step_menu_page; +extern MenuPage<3> step_menu_page; extern void mcl_save_sound(); extern void mcl_load_sound(); From 8c3100dd6dd6a6be677f6d15f69cbd2140ad7767 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 22:07:32 +1100 Subject: [PATCH 340/469] clear mute/oneshot mask on new step --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index af560e6ce..e0c8911e4 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -332,6 +332,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { active_track.conditional[step] = condition; active_track.timing[step] = utiming; // upper // active_track.clear_step_locks(step); + CLEAR_BIT64(active_track.oneshot_mask, step); SET_BIT64(active_track.pattern_mask, step); } else { DEBUG_PRINTLN("Trying to clear"); From 57eaef30e034ea05bd7c99fa5fc3f32faa1d56b3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 22:23:14 +1100 Subject: [PATCH 341/469] Select menu item 0 when switching tracks with trig interface --- avr/cores/megacommand/MCL/MenuPage.h | 4 ++++ avr/cores/megacommand/MCL/SeqPage.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/avr/cores/megacommand/MCL/MenuPage.h b/avr/cores/megacommand/MCL/MenuPage.h index 614815a1d..c2fad2a26 100644 --- a/avr/cores/megacommand/MCL/MenuPage.h +++ b/avr/cores/megacommand/MCL/MenuPage.h @@ -29,6 +29,10 @@ class MenuPageBase : public LightPage { void draw_item(uint8_t item_n, uint8_t row); void draw_menu(uint8_t x_offset, uint8_t y_offset, uint8_t width = MENU_WIDTH); + void select_item(uint8_t item = 0) { + cur_row = 0; + encoders[1]->cur = 0; + } void loop(); void display(); void setup(); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 7535c1ad1..866f89f4e 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -84,6 +84,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { opt_trackid = track + 1; select_track(device, track); redisplay = true; + seq_menu_page.select_item(0); } return true; From bc12dd27e619f60b10786d6dd1dbf96a3aeb325d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 19 Nov 2019 22:58:41 +1100 Subject: [PATCH 342/469] Fix octave and keyboard offset --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index e0c8911e4..a2116cd0b 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -104,7 +104,7 @@ void SeqStepPage::display() { uint8_t base = tuning->base; uint8_t notenum = seq_param4.cur + base; MusicalNotes number_to_note; - uint8_t oct = notenum / 12; + uint8_t oct = (notenum / 12) - 1; uint8_t note = notenum - 12 * (notenum / 12); GUI.put_string_at(10, number_to_note.notes_upper[note]); GUI.put_value_at1(12, oct); @@ -156,15 +156,15 @@ void SeqStepPage::display() { itoa(seq_param3.getValue(), K, 10); draw_knob(2, "LEN", K); + tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); if (show_pitch) { - tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); if (tuning != NULL) { strcpy(K, "--"); if (seq_param4.cur != 0) { uint8_t base = tuning->base; uint8_t notenum = seq_param4.cur + base; MusicalNotes number_to_note; - uint8_t oct = notenum / 12; + uint8_t oct = notenum / 12 - 1; uint8_t note = notenum - 12 * (notenum / 12); strcpy(K, number_to_note.notes_upper[note]); K[2] = oct + '0'; @@ -173,13 +173,14 @@ void SeqStepPage::display() { draw_knob(3, "PTC", K); } } - if (mcl_gui.show_encoder_value(&seq_param4) && (seq_param4.cur > 0) && (!note_interface.notes_all_off_md() && (!show_seq_menu) && (!show_step_menu))) { + if (mcl_gui.show_encoder_value(&seq_param4) && (seq_param4.cur > 0) && + (!note_interface.notes_all_off_md()) && (!show_seq_menu) && + (!show_step_menu) && (tuning != NULL)) { uint64_t note_mask = 0; - uint8_t note = seq_param4.cur - 3; + uint8_t note = seq_param4.cur + tuning->base; SET_BIT64(note_mask, note - 24 * (note / 24)); mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); - } - else { + } else { draw_lock_mask((page_select * 16), DEVICE_MD); draw_pattern_mask((page_select * 16), DEVICE_MD); } From 2cf616cf9e33a6561c52f8be375fb0106ccd8c87 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 00:03:20 +1100 Subject: [PATCH 343/469] Don't open step menu on SeqPtcPage, allow for track switching using TI --- avr/cores/megacommand/MCL/SeqPage.cpp | 2 +- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 85 +++++++++++++----------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 866f89f4e..8256d334b 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -151,7 +151,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { // activate show_seq_menu only if S2 press is not a key combination if (EVENT_PRESSED(event, Buttons.BUTTON3) && !BUTTON_DOWN(Buttons.BUTTON4)) { // If MD trig is held and BUTTON3 is pressed, launch note menu - if ((note_interface.notes_count_on() != 0) && (!show_step_menu)) { + if ((note_interface.notes_count_on() != 0) && (!show_step_menu) && (GUI.currentPage() != &seq_ptc_page)) { uint8_t note = 255; for (uint8_t n = 0; n < NUM_MD_TRACKS && note == 255; n++) { if (note_interface.notes[n] == 1) { diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index ace7add82..af16d3246 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -1,8 +1,8 @@ -#include "SeqPtcPage.h" #include "MCL.h" +#include "SeqPtcPage.h" #define MIDI_LOCAL_MODE 0 -#define NUM_KEYS 32 +#define NUM_KEYS 24 scale_t *scales[16]{ &chromaticScale, &ionianScale, @@ -37,12 +37,8 @@ scale_t *scales[16]{ typedef char scale_name_t[4]; const scale_name_t scale_names[] PROGMEM = { - "---", "ION", - "PHR", - "mHA", "mME", - "MPE", "mPE", "sPE", - "ISS", "BLU", - "MAJ", "MIN", "MM7", "Mm7", "mm7", "M79", + "---", "ION", "PHR", "mHA", "mME", "MPE", "mPE", "sPE", + "ISS", "BLU", "MAJ", "MIN", "MM7", "Mm7", "mm7", "M79", }; void SeqPtcPage::setup() { @@ -66,8 +62,7 @@ void SeqPtcPage::config_encoders() { #ifdef EXT_TRACKS else { ptc_param_len.max = (uint8_t)128; - ptc_param_len.cur = - mcl_seq.ext_tracks[last_ext_track].length; + ptc_param_len.cur = mcl_seq.ext_tracks[last_ext_track].length; } #endif } @@ -119,12 +114,26 @@ void SeqPtcPage::config() { ptc_param_oct.cur = 1; // config info labels - const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); - const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); - constexpr uint8_t len1 = sizeof(info1); - char buf[len1] = {'\0'}; + char *str1; + char *str2; + if (midi_device == DEVICE_MD) { + str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); + str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); + } else { + char str_first[3] = "--"; + if (Analog4.connected) { + strcpy(str_first, "A4"); + } else { + strcpy(str_first, "MI"); + } + char str_second[3] = "T "; + str_second[1] = '0' + last_ext_track; + str1 = &str_first[0]; + str2 = &str_second[0]; + } + m_strncpy_p(buf, str1, len1); strncpy(info1, buf, len1); strncat(info1, ">", len1); @@ -182,7 +191,8 @@ void SeqPtcPage::loop() { } #endif - if (ptc_param_oct.hasChanged() || ptc_param_finetune.hasChanged() || ptc_param_len.hasChanged() || ptc_param_scale.hasChanged()) { + if (ptc_param_oct.hasChanged() || ptc_param_finetune.hasChanged() || + ptc_param_len.hasChanged() || ptc_param_scale.hasChanged()) { queue_redraw(); } @@ -191,7 +201,8 @@ void SeqPtcPage::loop() { redisplay = true; } - if (deferred_timer != 0 && clock_diff(deferred_timer, slowclock) > render_defer_time) { + if (deferred_timer != 0 && + clock_diff(deferred_timer, slowclock) > render_defer_time) { deferred_timer = 0; redisplay = true; } @@ -320,7 +331,6 @@ void SeqPtcPage::display() { // draw TI keyboard mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); - SeqPage::display(); oled_display.display(); oled_display.setFont(oldfont); @@ -384,8 +394,8 @@ uint8_t SeqPtcPage::get_machine_pitch(uint8_t track, uint8_t pitch) { pitch = tuning->len - 1; } - uint8_t machine_pitch = - pgm_read_byte(&tuning->tuning[pitch]) + ptc_param_finetune.getValue() - 32; + uint8_t machine_pitch = pgm_read_byte(&tuning->tuning[pitch]) + + ptc_param_finetune.getValue() - 32; return machine_pitch; } @@ -428,12 +438,17 @@ void SeqPtcPage::trig_md_fromext(uint8_t note_num) { } } -void SeqPtcPage::queue_redraw() { - deferred_timer = slowclock; -} +void SeqPtcPage::queue_redraw() { deferred_timer = slowclock; } bool SeqPtcPage::handleEvent(gui_event_t *event) { + if (SeqPage::handleEvent(event)) { + if (show_seq_menu) { + seq_ptc_page.queue_redraw(); + return true; + } + } + if (note_interface.is_event(event)) { uint8_t mask = event->mask; uint8_t port = event->port; @@ -469,12 +484,12 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { recording = !recording; return true; } -/* - if (EVENT_PRESSED(event, Buttons.ENCODER4)) { - GUI.setPage(&grid_page); - return true; - } -*/ + /* + if (EVENT_PRESSED(event, Buttons.ENCODER4)) { + GUI.setPage(&grid_page); + return true; + } + */ if (EVENT_RELEASED(event, Buttons.BUTTON4)) { @@ -499,11 +514,6 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { return true; } - if (SeqPage::handleEvent(event)) { - seq_ptc_page.queue_redraw(); - return true; - } - return false; } @@ -516,13 +526,13 @@ uint8_t SeqPtcPage::seq_ext_pitch(uint8_t note_num) { uint8_t oct = note_num / 12; // if (pos >= scales[seq_param5.cur]->size) { oct += pos / scales[ptc_param_scale.cur]->size; - pos = pos - - scales[ptc_param_scale.cur]->size * (pos / scales[ptc_param_scale.cur]->size); + pos = pos - scales[ptc_param_scale.cur]->size * + (pos / scales[ptc_param_scale.cur]->size); // } // if (seq_param5.getValue() > 0) { - pitch = octave_to_pitch() + - scales[ptc_param_scale.cur]->pitches[pos] + oct * 12; + pitch = + octave_to_pitch() + scales[ptc_param_scale.cur]->pitches[pos] + oct * 12; // } return pitch; @@ -672,4 +682,3 @@ void SeqPtcMidiEvents::remove_callbacks() { state = false; } - From 5c476b819599f39a273bb2c6beb8ef61d7d0b84f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 00:13:49 +1100 Subject: [PATCH 344/469] fix redisplay when track menu is open on SeqPtcPage --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index af16d3246..73f5a100f 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -444,9 +444,10 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { if (SeqPage::handleEvent(event)) { if (show_seq_menu) { - seq_ptc_page.queue_redraw(); + redisplay = true; return true; } + queue_redraw(); } if (note_interface.is_event(event)) { From 4eb70dbe43634e6d028fcbed37e23bad0720005a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 12:22:22 +1100 Subject: [PATCH 345/469] getKit without block, fix track selection and md_exploit behaviour. PageSelect page requires latest Kit before entering sub pages. Don't block for the Kit. Instead, setup a callback to detect that the kitSysex has been received then on page_select exit, process the sysex message. Now the MDExploit is enabled for majority of the time, we should not getCurrentTrack if exploit is enabled, as this track will be the deferred track (track_with_no_locks). To preserve originally behaviour, currentTrack will be detected from GridPage before entering PageSelcet. --- avr/cores/megacommand/MCL/GridPage.cpp | 1 - avr/cores/megacommand/MCL/MDExploit.cpp | 39 +++++++------- avr/cores/megacommand/MCL/PageSelectPage.cpp | 52 ++++++++++++++++--- avr/cores/megacommand/MCL/PageSelectPage.h | 4 ++ avr/cores/megacommand/MCL/SeqPage.cpp | 5 +- avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 +- avr/cores/megacommand/MD/MD.cpp | 3 ++ avr/cores/megacommand/MD/MD.h | 54 +++++++++++--------- 8 files changed, 103 insertions(+), 57 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 2773a5542..0f8c528c5 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -865,7 +865,6 @@ bool GridPage::handleEvent(gui_event_t *event) { return true; } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { - prepare(); GUI.setPage(&page_select_page); return true; } diff --git a/avr/cores/megacommand/MCL/MDExploit.cpp b/avr/cores/megacommand/MCL/MDExploit.cpp index cafde5b53..d6c85f2ed 100644 --- a/avr/cores/megacommand/MCL/MDExploit.cpp +++ b/avr/cores/megacommand/MCL/MDExploit.cpp @@ -99,40 +99,37 @@ bool MDExploit::on(bool switch_tracks) { state = true; if (ignore_last_track_once) { ignore_last_track_once = false; - } else { + } else if (switch_tracks) { last_md_track = MD.getCurrentTrack(CALLBACK_TIMEOUT); } // if (MidiClock.state == 2) { // last_md_track = MD.currentTrack; MD.clear_all_windows(); delay(20); - if ((switch_tracks)) { - uint8_t n = NUM_MD_TRACKS; - track_with_nolocks = 255; - while (n > 0 && (track_with_nolocks == 255)) { - n--; - if (n != last_md_track) { - if ((MD.kit.models[n] > CTR_DX_MODEL) || - (MD.kit.models[n] < MID_MODEL)) { - - if (mcl_seq.md_tracks[n].lock_mask == 0) { - track_with_nolocks = n; - for (uint8_t a = 0; a < mcl_seq.num_lfo_tracks; a++) { - for (uint8_t b = 0; b < NUM_LFO_PARAMS; b++) { - if (mcl_seq.lfo_tracks[a].params[b].dest - 1 == n) { - track_with_nolocks = 255; - } + uint8_t n = NUM_MD_TRACKS; + track_with_nolocks = 255; + while (n > 0 && (track_with_nolocks == 255)) { + n--; + if (n != last_md_track) { + if ((MD.kit.models[n] > CTR_DX_MODEL) || (MD.kit.models[n] < MID_MODEL)) { + + if (mcl_seq.md_tracks[n].lock_mask == 0) { + track_with_nolocks = n; + for (uint8_t a = 0; a < mcl_seq.num_lfo_tracks; a++) { + for (uint8_t b = 0; b < NUM_LFO_PARAMS; b++) { + if (mcl_seq.lfo_tracks[a].params[b].dest - 1 == n) { + track_with_nolocks = 255; } } } } } } - if (track_with_nolocks == 255) { - track_with_nolocks = 15; - } - MD.setStatus(0x22, track_with_nolocks); } + if (track_with_nolocks == 255) { + track_with_nolocks = 15; + } + MD.setStatus(0x22, track_with_nolocks); //} // MD.getBlockingGlobal(0); diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index bb8230519..f940e78a3 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -32,13 +32,13 @@ const PageSelectEntry Entries[] PROGMEM = { {"LFO", &lfo_page, 3, 0, 24, 24, (uint8_t *)icon_lfo}, {"STEP EDIT", &seq_step_page, 4, 1, 24, 25, (uint8_t *)icon_step}, - {"RECORD", &seq_rtrk_page, 5, 1, 24, 15, (uint8_t*) icon_rec}, + {"RECORD", &seq_rtrk_page, 5, 1, 24, 15, (uint8_t *)icon_rec}, {"LOCKS", &seq_param_page[0], 6, 1, 24, 19, (uint8_t *)icon_para}, {"CHROMA", &seq_ptc_page, 7, 1, 24, 25, (uint8_t *)icon_chroma}, {"SOUND MANAGER", &sound_browser, 8, 2, 24, 19, (uint8_t *)icon_sound}, - {"WAV DESIGNER", &wd.pages[0], 9, 2, 24, 19, (uint8_t*)icon_wavd}, - {"LOUDNESS", &loudness_page, 10, 2, 24, 16, (uint8_t*)icon_loudness}, + {"WAV DESIGNER", &wd.pages[0], 9, 2, 24, 19, (uint8_t *)icon_wavd}, + {"LOUDNESS", &loudness_page, 10, 2, 24, 16, (uint8_t *)icon_loudness}, {"DELAY", &fx_page_a, 12, 3, 24, 25, (uint8_t *)icon_rhytmecho}, {"REVERB", &fx_page_b, 13, 3, 24, 25, (uint8_t *)icon_gatebox}, @@ -130,11 +130,47 @@ void PageSelectPage::init() { oled_display.print(str); } #endif - md_exploit.on(); + bool switch_tracks = false; + if (!md_exploit.state) { + last_md_track = MD.getCurrentTrack(CALLBACK_TIMEOUT); + } + md_exploit.off(switch_tracks); + md_prepare(); + + md_exploit.on(switch_tracks); note_interface.state = true; classic_display = false; } -void PageSelectPage::cleanup() { note_interface.init_notes(); } + +void PageSelectPage::md_prepare() { + + kit_cb.init(); + + MDSysexListener.addOnKitMessageCallback( + &kit_cb, + (md_callback_ptr_t)&MDBlockCurrentStatusCallback::onSysexReceived); + + if (MD.connected) { + MD.currentKit = MD.getCurrentKit(CALLBACK_TIMEOUT); + if ((mcl_cfg.auto_save == 1)) { + MD.saveCurrentKit(MD.currentKit); + MD.requestKit(MD.currentKit); + } + } +} + +void PageSelectPage::cleanup() { + if (kit_cb.received) { + MD.kit.fromSysex(MD.midi); + if (MidiClock.state == 2) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].update_kit_params(); + } + } + } + MDSysexListener.removeOnKitMessageCallback(&kit_cb); + note_interface.init_notes(); +} uint8_t PageSelectPage::get_nextpage_down() { for (int8_t i = page_select - 1; i >= 0; i--) { @@ -217,16 +253,16 @@ void PageSelectPage::display() { char str[16]; const uint8_t *icon; uint8_t iconw, iconh; - uint8_t pageidx; + uint8_t pageidx; uint8_t catidx; pageidx = get_pageidx(page_select); get_page_icon(pageidx, icon, iconw, iconh); get_page(pageidx, str); - if(pageidx < n_entry) { + if (pageidx < n_entry) { catidx = pgm_read_byte(&Entries[pageidx].CategoryId); - }else { + } else { catidx = 0xFF; } diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index 56a45d5be..6fd241bed 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -4,12 +4,14 @@ #define PageSelectPAGE_H__ #include "GUI.h" +#include "MD.h" extern MCLEncoder page_select_param1; extern MCLEncoder page_select_param2; class PageSelectPage : public LightPage { public: + MDCallback kit_cb; uint8_t page_select; PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) @@ -23,6 +25,8 @@ class PageSelectPage : public LightPage { virtual void init(); virtual void loop(); virtual void cleanup(); + virtual void md_prepare(); + virtual bool handleEvent(gui_event_t *event); // get a page in the current category. diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 8256d334b..ef89d67de 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -141,9 +141,12 @@ bool SeqPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { + if (route_page.hasChanged) { route_page.update_globals(); - md_exploit.off(); + bool switch_tracks = false; + md_exploit.off(false); md_exploit.on(); + } GUI.setPage(&page_select_page); } diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index a2116cd0b..10a9a0530 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -2,7 +2,7 @@ #include "SeqStepPage.h" #define MIDI_OMNI_MODE 17 -#define NUM_KEYS 32 +#define NUM_KEYS 24 void SeqStepPage::setup() { SeqPage::setup(); } void SeqStepPage::config() { diff --git a/avr/cores/megacommand/MD/MD.cpp b/avr/cores/megacommand/MD/MD.cpp index eb7e65ef2..3384cabfc 100644 --- a/avr/cores/megacommand/MD/MD.cpp +++ b/avr/cores/megacommand/MD/MD.cpp @@ -516,6 +516,9 @@ bool MDClass::waitBlocking(MDBlockCurrentStatusCallback *cb, uint16_t timeout) { return cb->received; } +//Perform checks on current sysex buffer to see if it Sysex. +// + uint8_t MDClass::getBlockingStatus(uint8_t type, uint16_t timeout) { MDBlockCurrentStatusCallback cb(type); diff --git a/avr/cores/megacommand/MD/MD.h b/avr/cores/megacommand/MD/MD.h index f2f011374..3b9c4b98f 100644 --- a/avr/cores/megacommand/MD/MD.h +++ b/avr/cores/megacommand/MD/MD.h @@ -24,7 +24,34 @@ * MD Callback class, inherit from this class if you want to use callbacks on MD *events. **/ -class MDCallback {}; + +class MDCallback { + public: + uint8_t type; + uint8_t value; + bool received; + + MDCallback(uint8_t _type = 0) { + type = _type; + init(); + } + + void init() { + received = false; + value = 255; + } + void onStatusResponseCallback(uint8_t _type, uint8_t param) { + + // GUI.printf_fill("eHHHH C%h N%h ",value, param); + if (type == _type) { + value = param; + received = true; + } + } + + virtual void onSysexReceived() { received = true; } + +}; /** * Standard method prototype for argument-less MD callbacks. @@ -44,34 +71,11 @@ typedef void (MDCallback::*md_status_callback_ptr_t)(uint8_t type, * from the MachineDrum. **/ class MDBlockCurrentStatusCallback : public MDCallback { - /** - * \addtogroup md_callbacks - * @{ - **/ public: - - uint8_t type; - uint8_t value; - bool received; - - MDBlockCurrentStatusCallback(uint8_t _type = 0) { - type = _type; - received = false; - value = 255; + MDBlockCurrentStatusCallback(uint8_t _type = 0) : MDCallback(_type) { } - void onStatusResponseCallback(uint8_t _type, uint8_t param) { - - // GUI.printf_fill("eHHHH C%h N%h ",value, param); - if (type == _type) { - value = param; - received = true; - } - } - - void onSysexReceived() { received = true; } - /* @} */ }; From 95247d0cfda0922fc5198c78a3528bcd63d21663 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 12:56:38 +1100 Subject: [PATCH 346/469] Small delay required for exploit to work --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index f940e78a3..37901aa6c 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -136,7 +136,7 @@ void PageSelectPage::init() { } md_exploit.off(switch_tracks); md_prepare(); - + delay(10); md_exploit.on(switch_tracks); note_interface.state = true; classic_display = false; From 1b0197cccecc8b08107173be866f2dfebbc0e927 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 13:07:51 +1100 Subject: [PATCH 347/469] Add flag to compare performance, blocking kit is too slow --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 17 ++++++++++++++--- avr/cores/megacommand/MCL/PageSelectPage.h | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 37901aa6c..9ffbf62c5 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -136,30 +136,40 @@ void PageSelectPage::init() { } md_exploit.off(switch_tracks); md_prepare(); - delay(10); md_exploit.on(switch_tracks); note_interface.state = true; classic_display = false; } void PageSelectPage::md_prepare() { - + #ifndef USE_BLOCKINGKIT kit_cb.init(); MDSysexListener.addOnKitMessageCallback( &kit_cb, (md_callback_ptr_t)&MDBlockCurrentStatusCallback::onSysexReceived); - + #endif if (MD.connected) { MD.currentKit = MD.getCurrentKit(CALLBACK_TIMEOUT); if ((mcl_cfg.auto_save == 1)) { MD.saveCurrentKit(MD.currentKit); + #ifdef USE_BLOCKINGKIT + MD.getBlockingKit(MD.currentKit, CALLBACK_TIMEOUT); + if (MidiClock.state == 2) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].update_kit_params(); + } + } + #else MD.requestKit(MD.currentKit); + delay(10); + #endif } } } void PageSelectPage::cleanup() { + #ifndef USE_BLOCKINGKIT if (kit_cb.received) { MD.kit.fromSysex(MD.midi); if (MidiClock.state == 2) { @@ -169,6 +179,7 @@ void PageSelectPage::cleanup() { } } MDSysexListener.removeOnKitMessageCallback(&kit_cb); + #endif note_interface.init_notes(); } diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index 6fd241bed..5ea06a6b3 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -11,7 +11,9 @@ extern MCLEncoder page_select_param2; class PageSelectPage : public LightPage { public: + #ifndef USE_BLOCKINGKIT MDCallback kit_cb; + #endif uint8_t page_select; PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) From d7fb734471b97ffaadd94f4b0fee05d5930586a5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 13:25:55 +1100 Subject: [PATCH 348/469] increase delay to 20ms --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 9ffbf62c5..59280750f 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -162,7 +162,7 @@ void PageSelectPage::md_prepare() { } #else MD.requestKit(MD.currentKit); - delay(10); + delay(20); #endif } } From f62467a9b5e9a58ffb8d19a4c3bccb176606dede Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 13:58:50 +1100 Subject: [PATCH 349/469] Update LFO params_offset on kit refresh --- avr/cores/megacommand/MCL/MCLSeq.cpp | 7 ++++++- avr/cores/megacommand/MCL/MCLSeq.h | 1 + avr/cores/megacommand/MCL/PageSelectPage.cpp | 8 ++------ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index 1bce140a9..626bc57f3 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -73,13 +73,18 @@ void MCLSeq::disable() { state = false; } -void MCLSeq::onMidiContinueCallback() { +void MCLSeq::update_params() { for (uint8_t i = 0; i < num_md_tracks; i++) { md_tracks[i].update_params(); } for (uint8_t i = 0; i < num_lfo_tracks; i++) { lfo_tracks[i].update_params_offset(); } + +} + +void MCLSeq::onMidiContinueCallback() { + update_params(); } void MCLSeq::onMidiStartImmediateCallback() { diff --git a/avr/cores/megacommand/MCL/MCLSeq.h b/avr/cores/megacommand/MCL/MCLSeq.h index 14b795780..752dcef4b 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.h +++ b/avr/cores/megacommand/MCL/MCLSeq.h @@ -48,6 +48,7 @@ class MCLSeq : public ClockCallback { void enable(); void disable(); + void update_params(); void onMidiStartCallback(); void onMidiStartImmediateCallback(); void onMidiContinueCallback(); diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 59280750f..13290b4b9 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -156,9 +156,7 @@ void PageSelectPage::md_prepare() { #ifdef USE_BLOCKINGKIT MD.getBlockingKit(MD.currentKit, CALLBACK_TIMEOUT); if (MidiClock.state == 2) { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].update_kit_params(); - } + mcl_seq.update_params(); } #else MD.requestKit(MD.currentKit); @@ -173,9 +171,7 @@ void PageSelectPage::cleanup() { if (kit_cb.received) { MD.kit.fromSysex(MD.midi); if (MidiClock.state == 2) { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].update_kit_params(); - } + mcl_seq.update_params(); } } MDSysexListener.removeOnKitMessageCallback(&kit_cb); From d301c7d04d8e6397f607bd646b18e3c5c8704c60 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 15:00:27 +1100 Subject: [PATCH 350/469] Ignore PageSelect/Track menu on track length change --- avr/cores/megacommand/MCL/SeqPage.cpp | 85 +++++++++++++++------------ 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index ef89d67de..d1eee4886 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -107,6 +107,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { } } mcl_seq.md_tracks[last_md_track].length = step; + } #ifdef EXT_TRACKS else { @@ -121,7 +122,10 @@ bool SeqPage::handleEvent(gui_event_t *event) { encoders[2]->cur = step; if (event->mask == EVENT_BUTTON_RELEASED) { note_interface.notes[track] = 0; - GUI.ignoreNextEvent(event->source); + } + GUI.ignoreNextEvent(Buttons.BUTTON4); + if (BUTTON_DOWN(Buttons.BUTTON3)) { + GUI.ignoreNextEvent(Buttons.BUTTON3); } return true; } @@ -142,10 +146,10 @@ bool SeqPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON2)) { if (route_page.hasChanged) { - route_page.update_globals(); - bool switch_tracks = false; - md_exploit.off(false); - md_exploit.on(); + route_page.update_globals(); + bool switch_tracks = false; + md_exploit.off(false); + md_exploit.on(); } GUI.setPage(&page_select_page); } @@ -154,7 +158,8 @@ bool SeqPage::handleEvent(gui_event_t *event) { // activate show_seq_menu only if S2 press is not a key combination if (EVENT_PRESSED(event, Buttons.BUTTON3) && !BUTTON_DOWN(Buttons.BUTTON4)) { // If MD trig is held and BUTTON3 is pressed, launch note menu - if ((note_interface.notes_count_on() != 0) && (!show_step_menu) && (GUI.currentPage() != &seq_ptc_page)) { + if ((note_interface.notes_count_on() != 0) && (!show_step_menu) && + (GUI.currentPage() != &seq_ptc_page)) { uint8_t note = 255; for (uint8_t n = 0; n < NUM_MD_TRACKS && note == 255; n++) { if (note_interface.notes[n] == 1) { @@ -210,14 +215,18 @@ bool SeqPage::handleEvent(gui_event_t *event) { void (*row_func)() = step_menu_page.menu.get_row_function(step_menu_page.encoders[1]->cur); } - if (row_func != NULL) { + if (row_func != NULL) { (*row_func)(); show_seq_menu = false; show_step_menu = false; return true; } - if (show_seq_menu) { seq_menu_page.enter(); } - if (show_step_menu) { step_menu_page.enter(); } + if (show_seq_menu) { + seq_menu_page.enter(); + } + if (show_step_menu) { + step_menu_page.enter(); + } show_seq_menu = false; show_step_menu = false; @@ -641,16 +650,21 @@ void opt_paste_track_handler() { } void opt_copy_step_handler() { - mcl_seq.md_tracks[last_md_track].copy_step(SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); + mcl_seq.md_tracks[last_md_track].copy_step( + SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); } void opt_paste_step_handler() { - mcl_seq.md_tracks[last_md_track].paste_step(SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); + mcl_seq.md_tracks[last_md_track].paste_step( + SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); } void opt_mute_step_handler() { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - if (note_interface.notes[n] == 1) { TOGGLE_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, n + SeqPage::page_select * 16); } + if (note_interface.notes[n] == 1) { + TOGGLE_BIT64(mcl_seq.md_tracks[last_md_track].oneshot_mask, + n + SeqPage::page_select * 16); + } } } @@ -699,28 +713,25 @@ void opt_shift_track_handler() { void opt_reverse_track_handler() { - if (opt_reverse == 2) { - if (opt_midi_device_capture == DEVICE_MD) { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].reverse(); - } - } - else { - for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { - mcl_seq.ext_tracks[n].reverse(); - } - } - } - - if (opt_reverse == 1) { - if (opt_midi_device_capture == DEVICE_MD) { - mcl_seq.md_tracks[last_md_track].reverse(); - } - else { - mcl_seq.ext_tracks[last_ext_track].reverse(); - } - } + if (opt_reverse == 2) { + if (opt_midi_device_capture == DEVICE_MD) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].reverse(); + } + } else { + for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { + mcl_seq.ext_tracks[n].reverse(); + } + } + } + if (opt_reverse == 1) { + if (opt_midi_device_capture == DEVICE_MD) { + mcl_seq.md_tracks[last_md_track].reverse(); + } else { + mcl_seq.ext_tracks[last_ext_track].reverse(); + } + } } void SeqPage::config_as_trackedit() { @@ -745,11 +756,9 @@ void SeqPage::loop() { seq_menu_value_encoder.cur = opt_trackid; } return; - } - else if (show_step_menu) { + } else if (show_step_menu) { step_menu_page.loop(); } - } void SeqPage::draw_page_index(bool show_page_index) { @@ -846,10 +855,10 @@ void SeqPage::display() { oled_display.setFont(&TomThumb); oled_display.fillRect(128 - width - 2, 0, width + 2, 32, BLACK); if (show_step_menu) { - step_menu_page.draw_menu(128 - width, 8, width); + step_menu_page.draw_menu(128 - width, 8, width); } if (show_seq_menu) { - seq_menu_page.draw_menu(128 - width, 8, width); + seq_menu_page.draw_menu(128 - width, 8, width); } } } From be5f264534a7ce0c47568a9c0ac3cc954a34e5c7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 17:54:40 +1100 Subject: [PATCH 351/469] Fix regression in kit paramter updating --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 13290b4b9..a48d72ad4 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -156,8 +156,11 @@ void PageSelectPage::md_prepare() { #ifdef USE_BLOCKINGKIT MD.getBlockingKit(MD.currentKit, CALLBACK_TIMEOUT); if (MidiClock.state == 2) { - mcl_seq.update_params(); - } + //Restore kit param values that are being modulaated by locks + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].update_kit_params(); + } + } #else MD.requestKit(MD.currentKit); delay(20); @@ -171,7 +174,9 @@ void PageSelectPage::cleanup() { if (kit_cb.received) { MD.kit.fromSysex(MD.midi); if (MidiClock.state == 2) { - mcl_seq.update_params(); + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].update_kit_params(); + } } } MDSysexListener.removeOnKitMessageCallback(&kit_cb); From 6e0d6152ec9b1e76ea09e61ee175c1ac28f5e661 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 18:29:52 +1100 Subject: [PATCH 352/469] Add update kit params routine to restore lfo and sequencer params to pre-lock/lfo value when kit is refreshed --- avr/cores/megacommand/MCL/GridPage.cpp | 4 +-- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 28 ++++++++++++++++++ avr/cores/megacommand/MCL/LFOSeqTrack.h | 2 ++ avr/cores/megacommand/MCL/MCLSeq.cpp | 15 ++++++---- avr/cores/megacommand/MCL/MCLSeq.h | 1 + avr/cores/megacommand/MCL/PageSelectPage.cpp | 30 +++++++++----------- 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 0f8c528c5..d9ac8430e 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -592,9 +592,7 @@ void GridPage::prepare() { MD.saveCurrentKit(MD.currentKit); MD.getBlockingKit(MD.currentKit); if (MidiClock.state == 2) { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].update_kit_params(); - } + mcl_seq.update_kit_params(); } } } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index 08a0aea7d..63afc17eb 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -105,6 +105,34 @@ void LFOSeqTrack::update_params_offset() { params[n].update_offset(); } } + +void LFOSeqTrack::update_kit_params() { + for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { + params[n].update_kit(); + } +} + +void LFOSeqParam::update_kit() { + if (dest <= NUM_MD_TRACKS) { + MD.kit.params[dest - 1][param] = offset; + } else { + switch (dest - NUM_MD_TRACKS - 1) { + case MD_FX_ECHO - MD_FX_ECHO: + MD.kit.delay[param] = offset; + break; + case MD_FX_DYN - MD_FX_ECHO: + MD.kit.dynamics[param] = offset; + break; + case MD_FX_REV - MD_FX_ECHO: + MD.kit.reverb[param] = offset; + break; + case MD_FX_EQ - MD_FX_ECHO: + MD.kit.eq[param] = offset; + break; + } + } +} + void LFOSeqParam::update_offset() { offset = get_param_offset(dest, param); } void LFOSeqParam::reset_param_offset() { reset_param(dest, param, offset); } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index 007cbc4c5..a826ea8a9 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -32,6 +32,7 @@ class LFOSeqParam { void reset_param(uint8_t dest, uint8_t param, uint8_t value); void reset_param_offset(); void update_offset(); + void update_kit(); }; class LFOSeqTrack { @@ -54,6 +55,7 @@ class LFOSeqTrack { LFOSeqParam params[NUM_LFO_PARAMS]; LFOSeqTrack() { init(); }; ALWAYS_INLINE() uint8_t get_wav_value(uint8_t sample_count, uint8_t param); + void update_kit_params(); void update_params_offset(); void reset_params_offset(); void check_and_update_params_offset(uint8_t dest, uint8_t value); diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index 626bc57f3..213dea7b5 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -72,7 +72,15 @@ void MCLSeq::disable() { MidiClock.removeOn192Callback(this, (midi_clock_callback_ptr_t)&MCLSeq::seq); state = false; } - +// restore kit params +void MCLSeq::update_kit_params() { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + mcl_seq.md_tracks[n].update_kit_params(); + } + for (uint8_t n = 0; n < NUM_LFO_TRACKS; n++) { + mcl_seq.lfo_tracks[n].update_kit_params(); + } +} void MCLSeq::update_params() { for (uint8_t i = 0; i < num_md_tracks; i++) { md_tracks[i].update_params(); @@ -80,12 +88,9 @@ void MCLSeq::update_params() { for (uint8_t i = 0; i < num_lfo_tracks; i++) { lfo_tracks[i].update_params_offset(); } - } -void MCLSeq::onMidiContinueCallback() { - update_params(); -} +void MCLSeq::onMidiContinueCallback() { update_params(); } void MCLSeq::onMidiStartImmediateCallback() { #ifdef EXT_TRACKS diff --git a/avr/cores/megacommand/MCL/MCLSeq.h b/avr/cores/megacommand/MCL/MCLSeq.h index 752dcef4b..ca04a8660 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.h +++ b/avr/cores/megacommand/MCL/MCLSeq.h @@ -48,6 +48,7 @@ class MCLSeq : public ClockCallback { void enable(); void disable(); + void update_kit_params(); void update_params(); void onMidiStartCallback(); void onMidiStartImmediateCallback(); diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index a48d72ad4..84244ddd5 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -132,7 +132,7 @@ void PageSelectPage::init() { #endif bool switch_tracks = false; if (!md_exploit.state) { - last_md_track = MD.getCurrentTrack(CALLBACK_TIMEOUT); + last_md_track = MD.getCurrentTrack(CALLBACK_TIMEOUT); } md_exploit.off(switch_tracks); md_prepare(); @@ -142,45 +142,41 @@ void PageSelectPage::init() { } void PageSelectPage::md_prepare() { - #ifndef USE_BLOCKINGKIT +#ifndef USE_BLOCKINGKIT kit_cb.init(); MDSysexListener.addOnKitMessageCallback( &kit_cb, (md_callback_ptr_t)&MDBlockCurrentStatusCallback::onSysexReceived); - #endif +#endif if (MD.connected) { MD.currentKit = MD.getCurrentKit(CALLBACK_TIMEOUT); if ((mcl_cfg.auto_save == 1)) { MD.saveCurrentKit(MD.currentKit); - #ifdef USE_BLOCKINGKIT +#ifdef USE_BLOCKINGKIT MD.getBlockingKit(MD.currentKit, CALLBACK_TIMEOUT); - if (MidiClock.state == 2) { - //Restore kit param values that are being modulaated by locks - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].update_kit_params(); - } - } - #else + if (MidiClock.state == 2) { + // Restore kit param values that are being modulaated by locks + mcl_seq.update_kit_params(); + } +#else MD.requestKit(MD.currentKit); delay(20); - #endif +#endif } } } void PageSelectPage::cleanup() { - #ifndef USE_BLOCKINGKIT +#ifndef USE_BLOCKINGKIT if (kit_cb.received) { MD.kit.fromSysex(MD.midi); if (MidiClock.state == 2) { - for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { - mcl_seq.md_tracks[n].update_kit_params(); - } + mcl_seq.update_kit_params(); } } MDSysexListener.removeOnKitMessageCallback(&kit_cb); - #endif +#endif note_interface.init_notes(); } From 0df2011c62689f20d009133d1ae612d25a162dba Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 21:52:22 +1100 Subject: [PATCH 353/469] Fix updating of LFO param values. Add lfo param updating from FXPages --- avr/cores/megacommand/MCL/FXPage.cpp | 3 +++ avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 4 ++-- avr/cores/megacommand/MCL/LFOSeqTrack.h | 2 +- avr/cores/megacommand/MCL/MCLSeq.cpp | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 49a10cf95..50137ba83 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -88,6 +88,9 @@ void FXPage::loop() { MD.kit.eq[fx_param] = encoders[i]->cur; break; } + for (uint8_t n = 0; n < mcl_seq.num_lfo_tracks; n++) { + mcl_seq.lfo_tracks[n].check_and_update_params_offset(MD_FX_ECHO + fx_type, fx_param, encoders[i]->cur); + } } } } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index 63afc17eb..db438044b 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -86,9 +86,9 @@ void LFOSeqTrack::seq() { } } -void LFOSeqTrack::check_and_update_params_offset(uint8_t dest, uint8_t value) { +void LFOSeqTrack::check_and_update_params_offset(uint8_t track, uint8_t dest, uint8_t value) { for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { - if (params[n].dest == dest) { + if ((params[n].dest - 1 == track) && (params[n].param == dest)) { params[n].offset = value; } } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index a826ea8a9..37a3bda0a 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -58,7 +58,7 @@ class LFOSeqTrack { void update_kit_params(); void update_params_offset(); void reset_params_offset(); - void check_and_update_params_offset(uint8_t dest, uint8_t value); + void check_and_update_params_offset(uint8_t dest, uint8_t param, uint8_t value); void init() { for (uint8_t a = 0; a < NUM_LFO_PARAMS; a++) { params[a].dest = 255; diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index 213dea7b5..e66668483 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -176,7 +176,7 @@ void MCLSeqMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { MD.parseCC(channel, param, &track, &track_param); mcl_seq.md_tracks[track].update_param(track_param, value); for (uint8_t n = 0; n < mcl_seq.num_lfo_tracks; n++) { - mcl_seq.lfo_tracks[n].check_and_update_params_offset(track_param, value); + mcl_seq.lfo_tracks[n].check_and_update_params_offset(track, track_param, value); } } } From 9d967613f3c06cbbf25c01e982884ac9c89b7a32 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 22:02:23 +1100 Subject: [PATCH 354/469] clear mute_until_start in case there chain mode is in bad state --- avr/cores/megacommand/MCL/MCLSeq.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index e66668483..af14a6374 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -98,6 +98,7 @@ void MCLSeq::onMidiStartImmediateCallback() { // ext_tracks[i].start_clock32th = 0; ext_tracks[i].step_count = 0; ext_tracks[i].iterations = 1; + ext_tracks[i].mute_until_start = false; } #endif for (uint8_t i = 0; i < num_md_tracks; i++) { @@ -106,6 +107,7 @@ void MCLSeq::onMidiStartImmediateCallback() { md_tracks[i].step_count = 0; md_tracks[i].iterations = 1; md_tracks[i].oneshot_mask = 0; + md_tracks[i].mute_until_start = false; } for (uint8_t i = 0; i < num_lfo_tracks; i++) { From a385d03412059238273a30b0049854cb334aaae9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 20 Nov 2019 22:27:25 +1100 Subject: [PATCH 355/469] draw_page_index calculation was actually wrong, fixed --- avr/cores/megacommand/MCL/LFOPage.cpp | 2 +- avr/cores/megacommand/MCL/SeqPage.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 74fdcfd63..bcef120ad 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -272,7 +272,7 @@ void LFOPage::display() { uint8_t lfo_track_num = lfo_track->track_number; mcl_gui.draw_panel_number(lfo_track_num); mcl_gui.draw_panel_toggle("ON", "OFF", lfo_track->enable); - draw_page_index(false); + draw_page_index(false, lfo_track->step_count / 4); uint8_t x = mcl_gui.knob_x0 + 5; uint8_t y = 8; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index d1eee4886..222271403 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -761,16 +761,20 @@ void SeqPage::loop() { } } -void SeqPage::draw_page_index(bool show_page_index) { +void SeqPage::draw_page_index(bool show_page_index, uint8_t _playing_idx) { // draw page index uint8_t pidx_x = pidx_x0; bool blink = MidiClock.getBlinkHint(true); uint8_t playing_idx; - if (midi_device == DEVICE_MD) { - playing_idx = (mcl_seq.md_tracks[last_md_track].step_count) / 16; + if (_playing_idx == 255) { + if (midi_device == DEVICE_MD) { + playing_idx = (mcl_seq.md_tracks[last_md_track].step_count - ((mcl_seq.md_tracks[last_md_track].step_count) / 16) * 16) / 4; + } else { + playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count - ((mcl_seq.ext_tracks[last_ext_track].step_count) / 16) * 16) /4; + } } else { - playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count) / 16; + playing_idx = _playing_idx; } uint8_t w = pidx_w; if (page_count == 8) { From ad8fea6090212bf6aee1d35520dffcf45b0fee6c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 21 Nov 2019 09:11:39 +1100 Subject: [PATCH 356/469] forgot to check-in --- avr/cores/megacommand/MCL/SeqPage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 44a780341..ee9c52242 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -68,7 +68,7 @@ class SeqPage : public LightPage { void draw_knob_frame(); void draw_knob(uint8_t i, const char* title, const char* text); void draw_knob(uint8_t i, Encoder* enc, const char* name); - void draw_page_index(bool show_page_index = true); + void draw_page_index(bool show_page_index = true, uint8_t _playing_idx = 255); void select_track(uint8_t device, uint8_t track); virtual bool handleEvent(gui_event_t *event); From a047191704a263086c0a797a51afc6a9e10ab98a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 21 Nov 2019 15:10:11 +1100 Subject: [PATCH 357/469] Refactor LFOPage code and fix LFO initialisation bug --- avr/cores/megacommand/MCL/LFOPage.cpp | 110 ++++++++-------------- avr/cores/megacommand/MCL/LFOPage.h | 1 - avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 43 +++++++++ avr/cores/megacommand/MCL/LFOSeqTrack.h | 24 +++++ avr/cores/megacommand/MCL/MCLSeq.cpp | 4 +- 5 files changed, 109 insertions(+), 73 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index bcef120ad..9f1186381 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -15,6 +15,11 @@ void LFOPage::setup() { // lfo_track = &mcl_seq.lfo_tracks[0]; + + lfo_track->params[0].update_offset(); + lfo_track->params[1].update_offset(); + lfo_track->wav_table_state[0] = false; + lfo_track->wav_table_state[1] = false; DEBUG_PRINT_FN(); } @@ -50,17 +55,16 @@ void LFOPage::update_encoders() { ((MCLEncoder *)encoders[3])->max = 23; if (encoders[0]->cur > NUM_MD_TRACKS) { - ((MCLEncoder *)encoders[1])->max = 7; - } else { - ((MCLEncoder *)encoders[1])->max = 23; - } + ((MCLEncoder *)encoders[1])->max = 7; + } else { + ((MCLEncoder *)encoders[1])->max = 23; + } if (encoders[2]->cur > NUM_MD_TRACKS) { - ((MCLEncoder *)encoders[3])->max = 7; - } else { - ((MCLEncoder *)encoders[3])->max = 23; + ((MCLEncoder *)encoders[3])->max = 7; + } else { + ((MCLEncoder *)encoders[3])->max = 23; } - } if (page_mode == LFO_SETTINGS) { encoders[0]->cur = waveform; @@ -75,7 +79,7 @@ void LFOPage::update_encoders() { encoders[3]->cur = lfo_track->params[1].depth; ((MCLEncoder *)encoders[3])->max = 127; } -// loop(); + // loop(); for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { encoders[i]->old = encoders[i]->cur; @@ -105,8 +109,8 @@ void LFOPage::loop() { SET_LOCK(); lfo_track->params[0].reset_param_offset(); lfo_track->params[0].param = encoders[1]->cur; - lfo_track->params[0].offset = lfo_track->params[0].get_param_offset( - encoders[0]->cur, encoders[1]->cur); + // lfo_track->params[0].offset = lfo_track->params[0].get_param_offset( + // encoders[0]->cur, encoders[1]->cur); lfo_track->params[0].update_offset(); CLEAR_LOCK(); } @@ -129,74 +133,38 @@ void LFOPage::loop() { SET_LOCK(); lfo_track->params[1].reset_param_offset(); lfo_track->params[1].param = encoders[3]->cur; - lfo_track->params[1].offset = lfo_track->params[1].get_param_offset( - encoders[2]->cur, encoders[3]->cur); + // lfo_track->params[1].offset = lfo_track->params[1].get_param_offset( + // encoders[2]->cur, encoders[3]->cur); lfo_track->params[1].update_offset(); CLEAR_LOCK(); } } + // wav_tables need to be recalculated when depth or waveform changes. + if (page_mode == LFO_SETTINGS) { if (encoders[0]->hasChanged()) { - waveform = encoders[0]->cur; - load_wavetable(waveform, lfo_track, 0, lfo_track->params[0].depth); - load_wavetable(waveform, lfo_track, 1, lfo_track->params[1].depth); + lfo_track->set_wav_type(encoders[0]->cur); } if (encoders[1]->hasChanged()) { - lfo_track->speed = encoders[1]->cur; + lfo_track->set_speed(encoders[1]->cur); } if (encoders[2]->hasChanged()) { - lfo_track->params[0].depth = encoders[2]->cur; - load_wavetable(waveform, lfo_track, 0, encoders[2]->cur); + lfo_track->set_depth(0, encoders[2]->cur); } if (encoders[3]->hasChanged()) { - lfo_track->params[1].depth = encoders[3]->cur; - load_wavetable(waveform, lfo_track, 1, encoders[3]->cur); + lfo_track->set_depth(1, encoders[3]->cur); } } -} -void LFOPage::load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, - uint8_t param, uint8_t depth) { - SinLFO sin_lfo; - TriLFO tri_lfo; - RampLFO ramp_lfo; - IRampLFO iramp_lfo; - ExpLFO exp_lfo; - IExpLFO iexp_lfo; - LFO *lfo; - switch (waveform) { - case SIN_WAV: - lfo = (LFO *)&sin_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; - break; - case TRI_WAV: - lfo = (LFO *)&tri_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_CENTRE; - break; - case IRAMP_WAV: - lfo = (LFO *)&iramp_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_MAX; - break; - case RAMP_WAV: - lfo = (LFO *)&ramp_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_MAX; - break; - case EXP_WAV: - lfo = (LFO *)&exp_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_MAX; - break; - case IEXP_WAV: - lfo = (LFO *)&iexp_lfo; - lfo_track->offset_behaviour = LFO_OFFSET_MAX; - break; + if (!lfo_track->wav_table_up_to_date(0)) { + lfo_track->load_wav_table(0); } - lfo->amplitude = depth; - // ExpLFO exp_lfo(20); - for (uint8_t n = 0; n < LFO_LENGTH; n++) { - lfo_track->wav_table[param][n] = (float)lfo->get_sample(n); + + if (!lfo_track->wav_table_up_to_date(1)) { + lfo_track->load_wav_table(1); } } @@ -290,7 +258,9 @@ void LFOPage::display() { draw_param(3, encoders[2]->cur, encoders[3]->cur); } if (page_mode == LFO_SETTINGS) { - load_wavetable(waveform, &temp_track, 0, lfo_height); + temp_track.set_wav_type(lfo_track->wav_type); + temp_track.set_depth(0, lfo_height); + temp_track.load_wav_table(0); uint8_t inc = LFO_LENGTH / width; for (uint8_t n = 0; n < LFO_LENGTH; n += inc, x++) { if (n < LFO_LENGTH) { @@ -308,8 +278,8 @@ void LFOPage::display() { draw_knob(3, encoders[3], "DEP2"); } oled_display.setFont(&TomThumb); - const char* info1; - const char* info2; + const char *info1; + const char *info2; if (page_mode) { info1 = "LFO>DST"; @@ -403,13 +373,13 @@ bool LFOPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_RELEASED) { return true; } -/* if (EVENT_PRESSED(event, Buttons.ENCODER1) || - EVENT_PRESSED(event, Buttons.ENCODER2) || - EVENT_PRESSED(event, Buttons.ENCODER3) || - EVENT_PRESSED(event, Buttons.ENCODER4)) { - GUI.setPage(&grid_page); - } -*/ + /* if (EVENT_PRESSED(event, Buttons.ENCODER1) || + EVENT_PRESSED(event, Buttons.ENCODER2) || + EVENT_PRESSED(event, Buttons.ENCODER3) || + EVENT_PRESSED(event, Buttons.ENCODER4)) { + GUI.setPage(&grid_page); + } + */ if (EVENT_PRESSED(event, Buttons.BUTTON1)) { page_mode = !(page_mode); update_encoders(); diff --git a/avr/cores/megacommand/MCL/LFOPage.h b/avr/cores/megacommand/MCL/LFOPage.h index a497ff939..0dfbbf91b 100644 --- a/avr/cores/megacommand/MCL/LFOPage.h +++ b/avr/cores/megacommand/MCL/LFOPage.h @@ -45,7 +45,6 @@ class LFOPage : public SeqPage, MidiCallback { void loop(); void cleanup(); void update_encoders(); - void load_wavetable(uint8_t waveform, LFOSeqTrack *lfo_track, uint8_t param, uint8_t depth); void setup_callbacks(); void remove_callbacks(); diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index db438044b..232f88c29 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -2,6 +2,48 @@ #include "LFOSeqTrack.h" #include "MCL.h" +void LFOSeqTrack::load_wav_table(uint8_t table) { + SinLFO sin_lfo; + TriLFO tri_lfo; + RampLFO ramp_lfo; + IRampLFO iramp_lfo; + ExpLFO exp_lfo; + IExpLFO iexp_lfo; + LFO *lfo; + switch (wav_type) { + case SIN_WAV: + lfo = (LFO *)&sin_lfo; + offset_behaviour = LFO_OFFSET_CENTRE; + break; + case TRI_WAV: + lfo = (LFO *)&tri_lfo; + offset_behaviour = LFO_OFFSET_CENTRE; + break; + case IRAMP_WAV: + lfo = (LFO *)&iramp_lfo; + offset_behaviour = LFO_OFFSET_MAX; + break; + case RAMP_WAV: + lfo = (LFO *)&ramp_lfo; + offset_behaviour = LFO_OFFSET_MAX; + break; + case EXP_WAV: + lfo = (LFO *)&exp_lfo; + offset_behaviour = LFO_OFFSET_MAX; + break; + case IEXP_WAV: + lfo = (LFO *)&iexp_lfo; + offset_behaviour = LFO_OFFSET_MAX; + break; + } + lfo->amplitude = params[table].depth; + // ExpLFO exp_lfo(20); + for (uint8_t n = 0; n < LFO_LENGTH; n++) { + wav_table[table][n] = (float)lfo->get_sample(n); + } + wav_table_state[table] = true; +} + uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { uint8_t offset = params[param].offset; uint8_t depth = params[param].depth; @@ -89,6 +131,7 @@ void LFOSeqTrack::seq() { void LFOSeqTrack::check_and_update_params_offset(uint8_t track, uint8_t dest, uint8_t value) { for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { if ((params[n].dest - 1 == track) && (params[n].param == dest)) { + wav_table_state[n] = false; params[n].offset = value; } } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index 37a3bda0a..e3fc73427 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -38,7 +38,9 @@ class LFOSeqParam { class LFOSeqTrack { public: uint8_t track_number; + uint8_t wav_type; uint8_t wav_table[2][WAV_LENGTH]; + bool wav_table_state[2]; uint8_t sample_count; uint8_t sample_hold = 0; @@ -58,12 +60,34 @@ class LFOSeqTrack { void update_kit_params(); void update_params_offset(); void reset_params_offset(); + + bool wav_table_up_to_date(uint8_t n) { + return wav_table_state[2]; + } + void check_and_update_params_offset(uint8_t dest, uint8_t param, uint8_t value); void init() { for (uint8_t a = 0; a < NUM_LFO_PARAMS; a++) { params[a].dest = 255; } } + void set_wav_type(uint8_t _wav_type) { + if (wav_type != _wav_type) { + wav_type = _wav_type; + wav_table_state[0] = false; + wav_table_state[1] = false; + } + } + void set_speed(uint8_t _speed) { + speed = _speed; + } + void set_depth(uint8_t param, uint8_t depth) { + if (params[param].depth != depth) { + params[param].depth = depth; + wav_table_state[param] = false; + } + } + void load_wav_table(uint8_t table); ALWAYS_INLINE() void seq(); }; diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index af14a6374..4a638a6d1 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -30,8 +30,8 @@ void MCLSeq::setup() { if (i == 0) { lfo_tracks[i].params[0].dest = 17; lfo_tracks[i].params[1].dest = 18; - lfo_tracks[i].params[0].param = 8; - lfo_tracks[i].params[1].param = 8; + lfo_tracks[i].params[0].param = 7; + lfo_tracks[i].params[1].param = 7; } } #ifdef EXT_TRACKS From 906a0834d252700e7730f698f9956eee28259084 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 21 Nov 2019 20:27:21 +1100 Subject: [PATCH 358/469] Don't show keyboard if no trigs are pressed --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 10a9a0530..1f485b1a0 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -174,7 +174,7 @@ void SeqStepPage::display() { } } if (mcl_gui.show_encoder_value(&seq_param4) && (seq_param4.cur > 0) && - (!note_interface.notes_all_off_md()) && (!show_seq_menu) && + (note_interface.notes_count_on() > 0) && (!show_seq_menu) && (!show_step_menu) && (tuning != NULL)) { uint64_t note_mask = 0; uint8_t note = seq_param4.cur + tuning->base; From 6a9cf4adb255a1a734504e76ccf5d4e697010ae6 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 22 Nov 2019 15:40:25 +1100 Subject: [PATCH 359/469] Add draw_microtiming popup --- avr/cores/megacommand/MCL/MCLGUI.cpp | 46 +++++++++++++++++++++++ avr/cores/megacommand/MCL/MCLGUI.h | 1 + avr/cores/megacommand/MCL/SeqStepPage.cpp | 15 ++++++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 1cc559b98..01e3f2589 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -390,6 +390,52 @@ void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, oled_display.setFont(oldfont); } +void MCLGUI::draw_microtiming(uint8_t resolution, uint8_t timing) { + auto oldfont = oled_display.getFont(); + oled_display.setFont(&TomThumb); + + oled_display.setTextColor(WHITE); + if (resolution == 0) { resolution = 1; } + if (resolution > 2) { resolution = 1; } + uint8_t degrees = 24 / resolution; + uint8_t heights[12]; + + if (resolution == 1) { + uint8_t heights_highres[12] = { 12, 2, 4, 8, 6, 2, 10, 2, 6, 8, 4, 2 }; + memcpy(&heights, &heights_highres, 12); + } + else { + uint8_t heights_lowres[6] = { 12, 4, 6, 10, 4, 8 }; + memcpy(&heights, &heights_lowres, 6); + } + uint8_t y_pos = 11; + uint8_t a = 0; + uint8_t w = 96; + uint8_t x_pos = 64 - (w / 2); + uint8_t x_w = (w / (degrees)); + uint8_t x = x_pos; + + oled_display.fillRect(8,2,128 - 16, 32 - 2,BLACK); + oled_display.drawRect(8 + 1, 2 + 1, 128 - 16 - 2, 32 - 2 - 2, WHITE); + + oled_display.setCursor(x_pos + 34, 10); + oled_display.print("uTIMING"); + + oled_display.drawLine(x, y_pos + heights[0], x + w, y_pos + heights[0], WHITE); + for (uint8_t n = 0; n <= degrees; n++) { + oled_display.drawLine(x, y_pos + heights[0], x, y_pos + heights[0] - heights[a] , WHITE); + a++; + if (n == timing) { + oled_display.fillRect(x - 1, y_pos + heights[0] + 3, 3, 3, WHITE); + oled_display.drawPixel(x, y_pos + heights[0] + 2, WHITE); + } + + if (a == degrees / 2) { a = 0; } + x += x_w; + } +oled_display.setFont(oldfont); +} + void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index 66d15b394..f303b91ca 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -40,6 +40,7 @@ class MCLGUI { uint8_t x_pos = s_progress_x, uint8_t y_pos = s_progress_y); + void draw_microtiming(uint8_t resolution, uint8_t timing); void clear_leftpane(); void clear_rightpane(); diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 1f485b1a0..a59ff4ddc 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -39,6 +39,7 @@ void SeqStepPage::init() { seq_param2.max = 23; seq_param2.min = 1; seq_param2.cur = 12; + seq_param2.old = 12; seq_param1.cur = 0; seq_param3.max = 64; midi_events.setup_callbacks(); @@ -124,7 +125,7 @@ void SeqStepPage::display() { void SeqStepPage::display() { oled_display.clearDisplay(); auto *oldfont = oled_display.getFont(); - + SeqPage::display(); draw_knob_frame(); char K[4]; @@ -180,12 +181,17 @@ void SeqStepPage::display() { uint8_t note = seq_param4.cur + tuning->base; SET_BIT64(note_mask, note - 24 * (note / 24)); mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); - } else { + } + + else if (mcl_gui.show_encoder_value(&seq_param2) && + (note_interface.notes_count_on() > 0) && (!show_seq_menu) && + (!show_step_menu)) { + mcl_gui.draw_microtiming(mcl_seq.md_tracks[last_md_track].resolution, seq_param2.cur); + } + else { draw_lock_mask((page_select * 16), DEVICE_MD); draw_pattern_mask((page_select * 16), DEVICE_MD); } - - SeqPage::display(); oled_display.display(); oled_display.setFont(oldfont); } @@ -285,6 +291,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { utiming = 12; } seq_param2.cur = utiming; + seq_param2.old = utiming; } if (event->mask == EVENT_BUTTON_RELEASED) { From 84292b60b69ab6ddc6aa64f852928a0e7cc1ff77 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 22 Nov 2019 15:58:38 +1100 Subject: [PATCH 360/469] fix display order for rendering menus --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index a59ff4ddc..8b335a43c 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -125,7 +125,6 @@ void SeqStepPage::display() { void SeqStepPage::display() { oled_display.clearDisplay(); auto *oldfont = oled_display.getFont(); - SeqPage::display(); draw_knob_frame(); char K[4]; @@ -183,14 +182,17 @@ void SeqStepPage::display() { mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); } - else if (mcl_gui.show_encoder_value(&seq_param2) && - (note_interface.notes_count_on() > 0) && (!show_seq_menu) && - (!show_step_menu)) { - mcl_gui.draw_microtiming(mcl_seq.md_tracks[last_md_track].resolution, seq_param2.cur); - } else { draw_lock_mask((page_select * 16), DEVICE_MD); - draw_pattern_mask((page_select * 16), DEVICE_MD); + draw_pattern_mask((page_select * 16), DEVICE_MD); + SeqPage::display(); + if (mcl_gui.show_encoder_value(&seq_param2) && + (note_interface.notes_count_on() > 0) && (!show_seq_menu) && + (!show_step_menu)) { + + mcl_gui.draw_microtiming(mcl_seq.md_tracks[last_md_track].resolution, + seq_param2.cur); + } } oled_display.display(); oled_display.setFont(oldfont); From e2df55b1c6eb303f1274f2a02b43c819745c7a9c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 22 Nov 2019 16:05:30 +1100 Subject: [PATCH 361/469] render SeqPage when keyboard is active (regression) --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 8b335a43c..9ca23e22e 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -180,11 +180,12 @@ void SeqStepPage::display() { uint8_t note = seq_param4.cur + tuning->base; SET_BIT64(note_mask, note - 24 * (note / 24)); mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); + SeqPage::display(); } else { draw_lock_mask((page_select * 16), DEVICE_MD); - draw_pattern_mask((page_select * 16), DEVICE_MD); + draw_pattern_mask((page_select * 16), DEVICE_MD); SeqPage::display(); if (mcl_gui.show_encoder_value(&seq_param2) && (note_interface.notes_count_on() > 0) && (!show_seq_menu) && From 1890b225b1931af016acd000fddb343abead7df5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 23 Nov 2019 16:36:31 +1100 Subject: [PATCH 362/469] initialise filename buffer with white space, previously could be random data --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index febd25c1e..8e7d0421d 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -280,6 +280,9 @@ void FileBrowserPage::_handle_filemenu() { memcpy_bank1(&buf1[0], ptr, sizeof(buf1)); char *suffix_pos = strchr(buf1, '.'); char buf2[32] = {'\0'}; + for (uint8_t n = 1; n < 32; n++) { + buf2[n] = ' '; + } uint8_t name_length = 8; switch (file_menu_page.menu.get_item_index(file_menu_encoder.cur)) { From b19b863fc31980f2494eac0642074cde690270a2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 23 Nov 2019 16:50:18 +1100 Subject: [PATCH 363/469] fix font glitching when exiting text input --- avr/cores/megacommand/MCL/TextInputPage.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 4744745fe..1abfce941 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -33,7 +33,6 @@ void TextInputPage::init() { GUI.lines[0].flashActive = false; GUI.lines[1].flashActive = false; oled_display.setTextColor(WHITE, BLACK); - oled_display.setFont(); #endif } @@ -150,7 +149,7 @@ void TextInputPage::display_normal() { encoders[1]->old = encoders[1]->cur; text[cursor_position] = _getchar(encoders[1]->getValue()); } - + auto oldfont = oled_display.getFont(); auto time = clock_diff(last_clock, slowclock); #ifdef OLED_DISPLAY @@ -171,7 +170,7 @@ void TextInputPage::display_normal() { if (time > FLASH_SPEED * 2) { last_clock = slowclock; } - + oled_display.setFont(oldfont); oled_display.display(); #else GUI.setLine(GUI.LINE1); From c2d2d0c3d90efd6f8b61a90747418ee39ce9b5a8 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 23 Nov 2019 17:20:31 +1100 Subject: [PATCH 364/469] some improvements to new project creation on fresh sd card or version upgrade --- avr/cores/megacommand/MCL/MCL.cpp | 3 ++- avr/cores/megacommand/MCL/MCLGfx.cpp | 1 + avr/cores/megacommand/MCL/NewProjectPage.cpp | 15 +++++++++------ avr/cores/megacommand/MCL/TextInputPage.cpp | 2 ++ avr/cores/megacommand/MCL/TextInputPage.h | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCL.cpp b/avr/cores/megacommand/MCL/MCL.cpp index e1d55a225..747130dd9 100644 --- a/avr/cores/megacommand/MCL/MCL.cpp +++ b/avr/cores/megacommand/MCL/MCL.cpp @@ -27,8 +27,9 @@ void MCL::setup() { gfx.splashscreen(); // if (!ret) { } - + text_input_page.no_escape = true; ret = mcl_sd.load_init(); + text_input_page.no_escape = false; DEBUG_PRINTLN("tempo:"); DEBUG_PRINTLN(mcl_cfg.tempo); MidiClock.setTempo(mcl_cfg.tempo); diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index 14dd954e8..c05ed06b5 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -98,6 +98,7 @@ void MCLGfx::alert(const char *str1, const char *str2) { mcl_gui.draw_infobox(str1, str2); oled_display.display(); delay(700); + oled_display.clearDisplay(); #else GUI.flash_strings_fill(str1, str2); GUI.display(); diff --git a/avr/cores/megacommand/MCL/NewProjectPage.cpp b/avr/cores/megacommand/MCL/NewProjectPage.cpp index 3d183b038..2dd84bd86 100644 --- a/avr/cores/megacommand/MCL/NewProjectPage.cpp +++ b/avr/cores/megacommand/MCL/NewProjectPage.cpp @@ -1,10 +1,10 @@ #include "NewProjectPage.h" #include "MCL.h" -void NewProjectPage::setup() {} +void NewProjectPage::setup() { +} void NewProjectPage::init() { - DEBUG_PRINTLN("New project page"); char my_string[sizeof(newprj)] = "project___"; @@ -12,6 +12,8 @@ void NewProjectPage::init() { my_string[7 + 1] = (mcl_cfg.number_projects % 100) / 10 + '0'; my_string[7 + 2] = (mcl_cfg.number_projects % 10) + '0'; m_strncpy(newprj, my_string, sizeof(newprj)); + gui_event_t event; + handleEvent(&event); } void NewProjectPage::display() { @@ -19,6 +21,7 @@ void NewProjectPage::display() { bool NewProjectPage::handleEvent(gui_event_t *event) { // don't handle any events + again: if (mcl_gui.wait_for_input(newprj, "New Project:", sizeof(newprj))) { char full_path[sizeof(newprj) + 5] = {'\0'}; @@ -30,9 +33,8 @@ bool NewProjectPage::handleEvent(gui_event_t *event) { DEBUG_PRINTLN(full_path); if (SD.exists(full_path)) { - gfx.alert("PROJECT EXISTS", ""); - DEBUG_PRINTLN("Project exists"); - return false; + gfx.alert("ERROR", "PROJECT EXISTS"); + goto again; } bool ret = proj.new_project(full_path); @@ -41,7 +43,8 @@ bool NewProjectPage::handleEvent(gui_event_t *event) { grid_page.reload_slot_models = false; GUI.setPage(&grid_page); } else { - gfx.alert("SD FAILURE", "--"); + gfx.alert("ERROR", "SD ERROR"); + goto again; } return false; } diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 1abfce941..e7022a84b 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -243,9 +243,11 @@ bool TextInputPage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON1)) { + if (!no_escape) { return_state = false; GUI.ignoreNextEvent(event->source); GUI.popPage(); + } return true; } diff --git a/avr/cores/megacommand/MCL/TextInputPage.h b/avr/cores/megacommand/MCL/TextInputPage.h index 642726bc8..02cd9db5b 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.h +++ b/avr/cores/megacommand/MCL/TextInputPage.h @@ -18,7 +18,7 @@ class TextInputPage : public LightPage { uint16_t last_clock; bool normal_mode; uint8_t cursor_position; - + bool no_escape = false; TextInputPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) {} From 42ea618a11cd4e587e379286090edbac9ffcbf36 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 23 Nov 2019 17:49:38 +1100 Subject: [PATCH 365/469] switch to grid_page after project load --- avr/cores/megacommand/MCL/MCL.cpp | 2 + avr/cores/megacommand/MCL/MCLSd.cpp | 56 ++++++++++----------------- avr/cores/megacommand/MCL/Project.cpp | 1 + 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCL.cpp b/avr/cores/megacommand/MCL/MCL.cpp index 747130dd9..680302bc0 100644 --- a/avr/cores/megacommand/MCL/MCL.cpp +++ b/avr/cores/megacommand/MCL/MCL.cpp @@ -30,6 +30,8 @@ void MCL::setup() { text_input_page.no_escape = true; ret = mcl_sd.load_init(); text_input_page.no_escape = false; + if (ret) { GUI.setPage(&grid_page); } + DEBUG_PRINTLN("tempo:"); DEBUG_PRINTLN(mcl_cfg.tempo); MidiClock.setTempo(mcl_cfg.tempo); diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index 377e14d5d..88f3ef335 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -1,5 +1,5 @@ -#include "MCLSd.h" #include "MCL.h" +#include "MCLSd.h" /* Function for initialising the SD Card */ @@ -9,7 +9,7 @@ bool MCLSd::sd_init() { bool ret = false; DEBUG_PRINT_FN(); DEBUG_PRINTLN("Initializing SD Card"); - //File file("/test.mcl",O_WRITE); + // File file("/test.mcl",O_WRITE); /*Configuration file used to store settings when Minicommand is turned off*/ for (uint8_t n = 0; n < SD_MAX_RETRIES && ret == false; n++) { ret = SD.begin(SD_CS, SPI_FULL_SPEED); @@ -17,7 +17,7 @@ bool MCLSd::sd_init() { delay(50); } } - if (ret == false) { + if (ret == false) { sd_state = false; DEBUG_PRINTLN("SD Init fail"); return false; @@ -26,7 +26,6 @@ bool MCLSd::sd_init() { DEBUG_PRINTLN("SD Init okay"); return true; - } bool MCLSd::load_init() { bool ret = false; @@ -37,13 +36,14 @@ bool MCLSd::load_init() { if (mcl_cfg.cfgfile.open("/config.mcls", O_RDWR)) { DEBUG_PRINTLN("Config file open: success"); - if (read_data(( uint8_t*)&mcl_cfg, sizeof(MCLSysConfigData), &mcl_cfg.cfgfile)) { + if (read_data((uint8_t *)&mcl_cfg, sizeof(MCLSysConfigData), + &mcl_cfg.cfgfile)) { DEBUG_PRINTLN("Config file read: success"); if (mcl_cfg.version != CONFIG_VERSION) { DEBUG_PRINTLN("Incompatible config version"); if (!mcl_cfg.cfg_init()) { -DEBUG_PRINTLN("Could not init cfg"); + DEBUG_PRINTLN("Could not init cfg"); return false; } GUI.setPage(&new_proj_page); @@ -54,24 +54,20 @@ DEBUG_PRINTLN("Could not init cfg"); else if (mcl_cfg.number_projects > 0) { DEBUG_PRINTLN("Project count greater than 0, try to load existing"); if (!proj.load_project(mcl_cfg.project)) { - + DEBUG_PRINTLN("error loading project"); GUI.setPage(&new_proj_page); return true; - } - else { - DEBUG_PRINTLN("Project loaded successfully, load grid"); - GUI.setPage(&grid_page); + } else { + DEBUG_PRINTLN("Project loaded successfully, load grid"); + return true; } return true; - } - else { + } else { GUI.setPage(&new_proj_page); return true; - } - } - else { + } else { DEBUG_PRINTLN("Could not read cfg file."); if (!mcl_cfg.cfg_init()) { @@ -79,24 +75,20 @@ DEBUG_PRINTLN("Could not init cfg"); } GUI.setPage(&new_proj_page); return true; - } - } - else { + } else { DEBUG_PRINTLN("Could not open cfg file. Let's try to create it"); if (!mcl_cfg.cfg_init()) { return false; } GUI.setPage(&new_proj_page); return true; - } return true; } - + return false; } - bool MCLSd::write_data(void *data, size_t len, FatFile *filep) { size_t b; @@ -108,9 +100,7 @@ bool MCLSd::write_data(void *data, size_t len, FatFile *filep) { for (n = 0; n < SD_MAX_RETRIES && pass == false; n++) { - - b = filep->write(( uint8_t*) data, len); - + b = filep->write((uint8_t *)data, len); if (b != len) { write_fail++; @@ -121,16 +111,14 @@ bool MCLSd::write_data(void *data, size_t len, FatFile *filep) { DEBUG_PRINTLN("Could not seek, failing"); return false; } - } - else { + } else { pass = true; } } if (pass) { return true; - } - else { + } else { DEBUG_PRINT_FN(); DEBUG_PRINTLN("Total write failures"); @@ -154,9 +142,7 @@ bool MCLSd::read_data(void *data, size_t len, FatFile *filep) { for (n = 0; n < SD_MAX_RETRIES && pass == false; n++) { - - b = filep->read(( uint8_t*) data, len); - + b = filep->read((uint8_t *)data, len); if (b != len) { read_fail++; @@ -168,16 +154,14 @@ bool MCLSd::read_data(void *data, size_t len, FatFile *filep) { return false; } pass = false; - } - else { + } else { pass = true; } } if (pass) { return true; - } - else { + } else { DEBUG_PRINT_FN(); DEBUG_PRINTLN("Total read failures"); DEBUG_PRINTLN(read_fail); diff --git a/avr/cores/megacommand/MCL/Project.cpp b/avr/cores/megacommand/MCL/Project.cpp index 47a49b614..c734068dc 100644 --- a/avr/cores/megacommand/MCL/Project.cpp +++ b/avr/cores/megacommand/MCL/Project.cpp @@ -32,6 +32,7 @@ bool Project::load_project(const char *projectname) { ret = mcl_cfg.write_cfg(); if (!ret) { + DEBUG_PRINTLN("could not write cfg"); return false; } From 333a7ac2b3014f81b5454dfcd193a8a10826078e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 24 Nov 2019 23:02:51 +1100 Subject: [PATCH 366/469] Fixes for loudness pages. Should work correctly --- avr/cores/megacommand/MCL/LoudnessPage.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/avr/cores/megacommand/MCL/LoudnessPage.cpp b/avr/cores/megacommand/MCL/LoudnessPage.cpp index 9e25994b5..859991a2b 100644 --- a/avr/cores/megacommand/MCL/LoudnessPage.cpp +++ b/avr/cores/megacommand/MCL/LoudnessPage.cpp @@ -125,12 +125,9 @@ void LoudnessPage::scale_vol(float inc) { grid_page.prepare(); - MD.getCurrentPattern(CALLBACK_TIMEOUT); - MD.getBlockingPattern(MD.currentPattern); uint8_t seq_mute_states[NUM_MD_TRACKS]; for (uint8_t a = 0; a < NUM_MD_TRACKS; a++) { seq_mute_states[a] = mcl_seq.md_tracks[a].mute_state; - mcl_seq.md_tracks[a].mute_state = SEQ_MUTE_ON; } for (uint8_t n = 0; n < 16; n++) { md_track->get_track_from_sysex(n, n); @@ -139,16 +136,12 @@ void LoudnessPage::scale_vol(float inc) { md_track->scale_vol(inc); memcpy(&mcl_seq.md_tracks[n], &(md_track->seq_data), sizeof(md_track->seq_data)); - md_track->place_track_in_pattern(n, n, &MD.pattern); + mcl_seq.md_tracks[n].mute_state = SEQ_MUTE_ON; + // md_track->place_track_in_pattern(n, n, &MD.pattern); md_track->place_track_in_kit(n, n, &MD.kit); + MD.setMachine(n, &(md_track->machine)); } - mcl_actions.md_setsysex_recpos(8, MD.pattern.origPosition); - MD.pattern.toSysex(); - mcl_actions.md_setsysex_recpos(4, MD.kit.origPosition); - MD.kit.toSysex(); - - MD.loadKit(MD.pattern.kit); for (uint8_t a = 0; a < NUM_MD_TRACKS; a++) { mcl_seq.md_tracks[a].mute_state = seq_mute_states[a]; } @@ -328,5 +321,5 @@ bool LoudnessPage::handleEvent(gui_event_t *event) { return false; } -MCLEncoder loudness_param1(0, 255, 2); +MCLEncoder loudness_param1(1, 255, 2); LoudnessPage loudness_page(&loudness_param1); From 466c52a768579b55042664063155ea97c733314b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 24 Nov 2019 23:08:57 +1100 Subject: [PATCH 367/469] Fix text rendering on WD OscMixerPage --- avr/cores/megacommand/MCL/OscMixerPage.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/avr/cores/megacommand/MCL/OscMixerPage.cpp b/avr/cores/megacommand/MCL/OscMixerPage.cpp index 622899cc3..614be7003 100644 --- a/avr/cores/megacommand/MCL/OscMixerPage.cpp +++ b/avr/cores/megacommand/MCL/OscMixerPage.cpp @@ -19,12 +19,16 @@ void OscMixerPage::init() { void OscMixerPage::cleanup() {} bool OscMixerPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON4)) { +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif GUI.setLine(GUI.LINE1); GUI.put_string_at(0, "Render.."); LCD.goLine(0); LCD.puts(GUI.lines[0].data); #ifdef OLED_DISPLAY oled_display.display(); + oled_display.clearDisplay(); #endif wd.render(); GUI.put_string_at(0, "Sending.."); From 3de83e24713052eab6c19c1ba355e9eadc757f5b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 24 Nov 2019 23:38:37 +1100 Subject: [PATCH 368/469] Add clear step locks option --- avr/cores/megacommand/MCL/SeqPage.cpp | 32 +++++++++++++++++++++++--- avr/cores/megacommand/MCL/SeqPage.h | 2 ++ avr/cores/megacommand/MCL/SeqPages.cpp | 13 ++++++----- avr/cores/megacommand/MCL/SeqPages.h | 2 +- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 222271403..d7527199d 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -1,7 +1,7 @@ #include "MCL.h" #include "SeqPage.h" -uint8_t SeqPage::page_select = 0; + uint8_t SeqPage::page_select = 0; uint8_t SeqPage::midi_device = DEVICE_MD; @@ -17,6 +17,7 @@ uint8_t opt_paste = 0; uint8_t opt_clear = 0; uint8_t opt_shift = 0; uint8_t opt_reverse = 0; +uint8_t opt_clear_step = 0; static uint8_t opt_midi_device_capture = DEVICE_MD; static SeqPage *opt_seqpage_capture = nullptr; @@ -230,6 +231,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { show_seq_menu = false; show_step_menu = false; + mcl_gui.init_encoders_used_clock(); return true; } #else @@ -668,6 +670,24 @@ void opt_mute_step_handler() { } } +void opt_clear_step_locks_handler() { + if (opt_clear_step == 1) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (note_interface.notes[n] == 1) { + + if (opt_midi_device_capture == DEVICE_MD) { + mcl_seq.md_tracks[last_md_track].clear_step_locks( + SeqPage::step_select + SeqPage::page_select * 16); + } else { + // mcl_seq.ext_tracks[last_ext_track].clear_step_locks( + // SeqPage::step_select + SeqPage::page_select * 16); + } + } + } + } + opt_clear_step = 0; +} + void opt_shift_track_handler() { switch (opt_shift) { case 1: @@ -769,9 +789,15 @@ void SeqPage::draw_page_index(bool show_page_index, uint8_t _playing_idx) { uint8_t playing_idx; if (_playing_idx == 255) { if (midi_device == DEVICE_MD) { - playing_idx = (mcl_seq.md_tracks[last_md_track].step_count - ((mcl_seq.md_tracks[last_md_track].step_count) / 16) * 16) / 4; + playing_idx = + (mcl_seq.md_tracks[last_md_track].step_count - + ((mcl_seq.md_tracks[last_md_track].step_count) / 16) * 16) / + 4; } else { - playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count - ((mcl_seq.ext_tracks[last_ext_track].step_count) / 16) * 16) /4; + playing_idx = + (mcl_seq.ext_tracks[last_ext_track].step_count - + ((mcl_seq.ext_tracks[last_ext_track].step_count) / 16) * 16) / + 4; } } else { playing_idx = _playing_idx; diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index ee9c52242..f3776acc6 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -24,6 +24,7 @@ extern uint8_t opt_paste; extern uint8_t opt_clear; extern uint8_t opt_shift; extern uint8_t opt_reverse; +extern uint8_t opt_clear_step; extern void opt_trackid_handler(); extern void opt_resolution_handler(); @@ -36,6 +37,7 @@ extern void opt_reverse_track_handler(); extern void opt_paste_step_handler(); extern void opt_copy_step_handler(); extern void opt_mute_step_handler(); +extern void opt_clear_step_locks_handler(); class SeqPage : public LightPage { public: diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 364ef5e21..ca54e0573 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -30,10 +30,10 @@ const menu_t<8> seq_menu_layout PROGMEM = { "SEQ", { {"TRACK SEL:", 1, 17, 0, (uint8_t *)&opt_trackid, (Page *)NULL, opt_trackid_handler, {}}, - {"COPY:", 0, 3, 3, (uint8_t *)&opt_copy, (Page *)NULL, opt_copy_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"CLEAR:", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, - {"CLEAR:", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {1, "LCKS."}, {2, "ALL"}}}, - {"PASTE:", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK."}, {2, "ALL"}}}, + {"COPY:", 0, 3, 3, (uint8_t *)&opt_copy, (Page *)NULL, opt_copy_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"}}}, + {"CLEAR:", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"}}}, + {"CLEAR:", 0, 3, 3, (uint8_t *)&opt_clear, (Page *)NULL, opt_clear_locks_handler, { {0, "--",}, {1, "LCKS"}, {2, "ALL"}}}, + {"PASTE:", 0, 3, 3, (uint8_t *)&opt_paste, (Page *)NULL, opt_paste_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"}}}, {"TRACK RES:", 1, 3, 2, (uint8_t *)&opt_resolution, (Page *)NULL, opt_resolution_handler, { {1, "2x"}, {2, "1x"} }}, {"SHIFT:", 0, 5, 5, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "--",}, {1, "L"}, {2, "R"}, {3,"L>ALL"}, {4, "R>ALL"}}}, {"REVERSE:", 0, 3, 3, (uint8_t *)&opt_reverse, (Page *)NULL, opt_reverse_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"} }}, @@ -45,9 +45,10 @@ MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder seq_menu_entry_encoder(0, 9, ENCODER_RES_PAT); MenuPage<8> seq_menu_page(&seq_menu_layout, &seq_menu_value_encoder, &seq_menu_entry_encoder); -const menu_t<3> step_menu_layout PROGMEM = { +const menu_t<4> step_menu_layout PROGMEM = { "STP", { + {"CLEAR:", 0, 2, 2, (uint8_t *)&opt_clear_step, (Page *)NULL, opt_clear_step_locks_handler, { {0, "--",}, {1, "LCKS"}}}, {"COPY STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_copy_step_handler, {}}, {"PASTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_paste_step_handler, {}}, {"MUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_mute_step_handler, {}}, @@ -57,7 +58,7 @@ const menu_t<3> step_menu_layout PROGMEM = { MCLEncoder step_menu_value_encoder(0, 16, ENCODER_RES_PAT); MCLEncoder step_menu_entry_encoder(0, 9, ENCODER_RES_PAT); -MenuPage<3> step_menu_page(&step_menu_layout, &step_menu_value_encoder, &step_menu_entry_encoder); +MenuPage<4> step_menu_page(&step_menu_layout, &step_menu_value_encoder, &step_menu_entry_encoder); //SeqLFOPage seq_lfo_page[NUM_LFO_PAGES]; diff --git a/avr/cores/megacommand/MCL/SeqPages.h b/avr/cores/megacommand/MCL/SeqPages.h index 3eab74615..9511de334 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -58,7 +58,7 @@ extern MenuPage<8> seq_menu_page; extern MCLEncoder step_menu_value_encoder; extern MCLEncoder step_menu_entry_encoder; -extern MenuPage<3> step_menu_page; +extern MenuPage<4> step_menu_page; extern void mcl_save_sound(); extern void mcl_load_sound(); From 643b562152381435ef2a7ff2a52c84f0b212986e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 24 Nov 2019 23:38:51 +1100 Subject: [PATCH 369/469] Set MCL version 2.50! --- avr/cores/megacommand/MCL/MCL.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index ad24ab910..de89971de 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -89,8 +89,8 @@ #include "Fonts/Elektrothic.h" #endif -#define VERSION 2040 -#define VERSION_STR "2.40" +#define VERSION 2050 +#define VERSION_STR "2.50" #define CALLBACK_TIMEOUT 500 #define GUI_NAME_TIMEOUT 800 From 6eeb7cf82538d9ce08dfd988d18e26b0e988a281 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 25 Nov 2019 11:38:58 +1100 Subject: [PATCH 370/469] cleanup, remove vim folder --- avr/cores/megacommand/.vim/coc-settings.json | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 avr/cores/megacommand/.vim/coc-settings.json diff --git a/avr/cores/megacommand/.vim/coc-settings.json b/avr/cores/megacommand/.vim/coc-settings.json deleted file mode 100644 index 86b3286a4..000000000 --- a/avr/cores/megacommand/.vim/coc-settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "languageserver": { - "ccls": { - "command": "ccls", - "filetypes": ["c", "cpp", "objc", "objcpp", "cuda"], - "rootPatterns": [".ccls", "compile_commands.json", ".vim/", ".git/", ".hg/"], - "initializationOptions": { - "cache": { - "directory": "../../../.ccls-cache" - }, - "highlight": { "lsRanges" : true } - } - } - } -} From 24346f583e7aac0ee22695f0757f5d8902e6845d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 25 Nov 2019 11:40:19 +1100 Subject: [PATCH 371/469] cleanup, remove main.bak --- avr/cores/megacommand/main.bak | 392 --------------------------------- 1 file changed, 392 deletions(-) delete mode 100644 avr/cores/megacommand/main.bak diff --git a/avr/cores/megacommand/main.bak b/avr/cores/megacommand/main.bak deleted file mode 100644 index c8ee33c65..000000000 --- a/avr/cores/megacommand/main.bak +++ /dev/null @@ -1,392 +0,0 @@ - -#include -#include -//#include "../../Libraries/Midi/src/Midi.h" -//#include <../../Libraries/Midi/src/MidiClock.h> - -//#include <../../Libraries/CommonTools/src/helpers.h> -#include -//#include -extern "C" { -#include -#include -#include -#include -#include -#include -} -//extern MidiClockClass MidiClock; -//extern volatile uint16_t clock = 0; -//extern volatile uint16_t slowclock = 0; -//uint8_t sysexBuf[8192]; -//MidiClass Midi(&MidiUart, sysexBuf, sizeof(sysexBuf)); -//uint8_t sysexBuf2[512]; -//MidiClass Midi2(&MidiUart2, sysexBuf2, sizeof(sysexBuf2)); - - -void timer_init(void) { -/* - TCCR0A = _BV(CS01); - // TIMSK |= _BV(TOIE0); - - TCCR1A = _BV(WGM10); // | _BV(COM1A1) | _BV(COM1B1); - TCCR1B |= _BV(CS10) | _BV(WGM12); // every cycle -#ifdef MIDIDUINO_MIDI_CLOCK - TIMSK1 |= _BV(TOIE1); -#endif - - TCCR2A = _BV(WGM20) | _BV(WGM21) | _BV(CS20) | _BV(CS21); // ) | _BV(CS21); // | _BV(COM21); - TIMSK1 |= _BV(TOIE2); -*/ - } - -void x_init(void) { - /** Disable watchdog. **/ - wdt_disable(); - // wdt_enable(WDTO_15MS); - - // Configure Port C as 8 channels of output. disable pullup resistors. - DDRC = 0xFF; - PORTC = 0x00; - - /* move interrupts to bootloader section */ - MCUCR = _BV(IVCE); - MCUCR = _BV(SRE); - - // activate lever converter - SET_BIT(DDRD, PD4); - SET_BIT(PORTD, PD4); - - // activate background pwm - TCCR3B = _BV(WGM32) | _BV(CS30); - TCCR3A = _BV(WGM30) | _BV(COM3A1); - OCR3A = 160; - - DDRE |= _BV(PE4) | _BV(PE5); - // DDRB |= _BV(PB0); - // DDRC |= _BV(PC3); - - timer_init(); -} - - -//void (*jump_to_boot)(void) = (void(*)(void))0xFF11; - -void start_bootloader(void) { -/* - cli(); - eeprom_write_word(START_MAIN_APP_ADDR, 0); - wdt_enable(WDTO_30MS); - while(1) {}; -*/ - } - -void setup(); -void loop(); -void handleGui(); - -#define PHASE_FACTOR 16 -static inline uint32_t phase_mult(uint32_t val) { - return (val * PHASE_FACTOR) >> 8; -} - - -ISR(TIMER1_OVF_vect) { - - clock++; -#ifdef MIDIDUINO_MIDI_CLOCK -// if (MidiClock.state == MidiClock.STARTED) { - // MidiClock.handleTimerInt(); - // } -#endif - - // clearLed2(); -} - -// XXX CMP to have better time - -static uint16_t oldsr = 0; - -void gui_poll() { - static bool inGui = false; - if (inGui) { - return; - } else { - inGui = true; - } - sei(); // reentrant interrupt - - uint16_t sr = SR165.read16(); - if (sr != oldsr) { - Buttons.clear(); - Buttons.poll(sr >> 8); - Encoders.poll(sr); - oldsr = sr; - pollEventGUI(); - } - inGui = false; -} - -uint16_t lastRunningStatusReset = 0; - -#define OUTPUTPORT PORTD -#define OUTPUTDDR DDRD -#define OUTPUTPIN PD0 - -//extern uint16_t myvar; -ISR(TIMER2_OVF_vect) { - slowclock++; - if (abs(slowclock - lastRunningStatusReset) > 3000) { - MidiUart.resetRunningStatus(); - lastRunningStatusReset = slowclock; - } - - MidiUart.tickActiveSense(); - //MidiUart2.tickActiveSense(); - - // SET_BIT(OUTPUTPORT, OUTPUTPIN); - -#ifdef MIDIDUINO_POLL_GUI_IRQ - gui_poll(); -#endif - // CLEAR_BIT(OUTPUTPORT, OUTPUTPIN); -} - -void handleIncomingMidi() { - while (MidiUart.avail()) { - Midi.handleByte(MidiUart.m_getc()); - } - //Disable non realtime midi - while (MidiUart2.avail()) { - Midi2.handleByte(MidiUart2.m_getc()); - } -} - -void __mainInnerLoop(bool callLoop) { - // SET_BIT(OUTPUTPORT, OUTPUTPIN); - // setLed2(); - if ((MidiClock.mode == MidiClock.EXTERNAL_UART1 || - MidiClock.mode == MidiClock.EXTERNAL_UART2)) { - MidiClock.updateClockInterval(); - } - - // CLEAR_BIT(OUTPUTPORT, OUTPUTPIN); - handleIncomingMidi(); - - if (callLoop) { - GUI.loop(); - } -} - -void setupEventHandlers(); -void setupMidiCallbacks(); -//void setupClockCallbacks(); -void initVariant() __attribute__((weak)); -void initVariant() { } - -void setupUSB() __attribute__((weak)); -void setupUSB() { } - -void init() -{ - // this needs to be called before setup() or some functions won't - // work there - sei(); -XMCRA = _BV(SRE); - // on the ATmega168, timer 0 is also used for fast hardware pwm - // (using phase-correct PWM would mean that timer 0 overflowed half as often - // resulting in different millis() behavior on the ATmega8 and ATmega168) -#if defined(TCCR0A) && defined(WGM01) - sbi(TCCR0A, WGM01); - sbi(TCCR0A, WGM00); -#endif - - // set timer 0 prescale factor to 64 -#if defined(__AVR_ATmega128__) - // CPU specific: different values for the ATmega128 - sbi(TCCR0, CS02); -#elif defined(TCCR0) && defined(CS01) && defined(CS00) - // this combination is for the standard atmega8 - sbi(TCCR0, CS01); - sbi(TCCR0, CS00); -#elif defined(TCCR0B) && defined(CS01) && defined(CS00) - // this combination is for the standard 168/328/1280/2560 - sbi(TCCR0B, CS01); - sbi(TCCR0B, CS00); -#elif defined(TCCR0A) && defined(CS01) && defined(CS00) - // this combination is for the __AVR_ATmega645__ series - sbi(TCCR0A, CS01); - sbi(TCCR0A, CS00); -#else - #error Timer 0 prescale factor 64 not set correctly -#endif - - // enable timer 0 overflow interrupt -#if defined(TIMSK) && defined(TOIE0) - sbi(TIMSK, TOIE0); -#elif defined(TIMSK0) && defined(TOIE0) - sbi(TIMSK0, TOIE0); -#else - #error Timer 0 overflow interrupt not set correctly -#endif - - // timers 1 and 2 are used for phase-correct hardware pwm - // this is better for motors as it ensures an even waveform - // note, however, that fast pwm mode can achieve a frequency of up - // 8 MHz (with a 16 MHz clock) at 50% duty cycle - -#if defined(TCCR1B) && defined(CS11) && defined(CS10) - TCCR1B = 0; - - // set timer 1 prescale factor to 64 - sbi(TCCR1B, CS11); -#if F_CPU >= 8000000L - sbi(TCCR1B, CS10); -#endif -#elif defined(TCCR1) && defined(CS11) && defined(CS10) - sbi(TCCR1, CS11); -#if F_CPU >= 8000000L - sbi(TCCR1, CS10); -#endif -#endif - // put timer 1 in 8-bit phase correct pwm mode -#if defined(TCCR1A) && defined(WGM10) - sbi(TCCR1A, WGM10); -#endif - - // set timer 2 prescale factor to 64 -#if defined(TCCR2) && defined(CS22) - sbi(TCCR2, CS22); -#elif defined(TCCR2B) && defined(CS22) - sbi(TCCR2B, CS22); -//#else - // Timer 2 not finished (may not be present on this CPU) -#endif - - // configure timer 2 for phase correct pwm (8-bit) -#if defined(TCCR2) && defined(WGM20) - sbi(TCCR2, WGM20); -#elif defined(TCCR2A) && defined(WGM20) - sbi(TCCR2A, WGM20); -//#else - // Timer 2 not finished (may not be present on this CPU) -#endif - -#if defined(TCCR3B) && defined(CS31) && defined(WGM30) - sbi(TCCR3B, CS31); // set timer 3 prescale factor to 64 - sbi(TCCR3B, CS30); - sbi(TCCR3A, WGM30); // put timer 3 in 8-bit phase correct pwm mode -#endif - -#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */ - sbi(TCCR4B, CS42); // set timer4 prescale factor to 64 - sbi(TCCR4B, CS41); - sbi(TCCR4B, CS40); - sbi(TCCR4D, WGM40); // put timer 4 in phase- and frequency-correct PWM mode - sbi(TCCR4A, PWM4A); // enable PWM mode for comparator OCR4A - sbi(TCCR4C, PWM4D); // enable PWM mode for comparator OCR4D -#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */ -#if defined(TCCR4B) && defined(CS41) && defined(WGM40) - sbi(TCCR4B, CS41); // set timer 4 prescale factor to 64 - sbi(TCCR4B, CS40); - sbi(TCCR4A, WGM40); // put timer 4 in 8-bit phase correct pwm mode -#endif -#endif /* end timer4 block for ATMEGA1280/2560 and similar */ - -#if defined(TCCR5B) && defined(CS51) && defined(WGM50) - sbi(TCCR5B, CS51); // set timer 5 prescale factor to 64 - sbi(TCCR5B, CS50); - sbi(TCCR5A, WGM50); // put timer 5 in 8-bit phase correct pwm mode -#endif - -#if defined(ADCSRA) - // set a2d prescaler so we are inside the desired 50-200 KHz range. - #if F_CPU >= 16000000 // 16 MHz / 128 = 125 KHz - sbi(ADCSRA, ADPS2); - sbi(ADCSRA, ADPS1); - sbi(ADCSRA, ADPS0); - #elif F_CPU >= 8000000 // 8 MHz / 64 = 125 KHz - sbi(ADCSRA, ADPS2); - sbi(ADCSRA, ADPS1); - cbi(ADCSRA, ADPS0); - #elif F_CPU >= 4000000 // 4 MHz / 32 = 125 KHz - sbi(ADCSRA, ADPS2); - cbi(ADCSRA, ADPS1); - sbi(ADCSRA, ADPS0); - #elif F_CPU >= 2000000 // 2 MHz / 16 = 125 KHz - sbi(ADCSRA, ADPS2); - cbi(ADCSRA, ADPS1); - cbi(ADCSRA, ADPS0); - #elif F_CPU >= 1000000 // 1 MHz / 8 = 125 KHz - cbi(ADCSRA, ADPS2); - sbi(ADCSRA, ADPS1); - sbi(ADCSRA, ADPS0); - #else // 128 kHz / 2 = 64 KHz -> This is the closest you can get, the prescaler is 2 - cbi(ADCSRA, ADPS2); - cbi(ADCSRA, ADPS1); - sbi(ADCSRA, ADPS0); - #endif - // enable a2d conversions - sbi(ADCSRA, ADEN); -#endif - - // the bootloader connects pins 0 and 1 to the USART; disconnect them - // here so they can be used as normal digital i/o; they will be - // reconnected in Serial.begin() -#if defined(UCSRB) - UCSRB = 0; -#elif defined(UCSR0B) - UCSR0B = 0; -#endif -} -int main(void) -{ - init(); - - initVariant(); - -#if defined(USBCON) - USBDevice.attach(); -#endif - - setup(); - - for (;;) { - loop(); - if (serialEventRun) serialEventRun(); - } - - return 0; -} - -int m_main(void) { - delay(100); - init(); - clearLed(); - clearLed2(); - - uint16_t sr = SR165.read16(); - Buttons.clear(); - Buttons.poll(sr >> 8); - Encoders.poll(sr); - oldsr = sr; - - MidiSysex.addSysexListener(&MididuinoSysexListener); - - OUTPUTDDR |= _BV(OUTPUTPIN); - setup(); - setupEventHandlers(); - setupMidiCallbacks(); -// setupClockCallbacks(); - sei(); - - for (;;) { - __mainInnerLoop(); - } - return 0; -} - -void handleGui() { - pollEventGUI(); -} - From 07fd30b830d1cabd8271a183e59aef332ba5dbee Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 25 Nov 2019 12:26:38 +1100 Subject: [PATCH 372/469] Update changelog for 2.50 --- Changelog | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/Changelog b/Changelog index 169486fd8..8501daa74 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,80 @@ +MCL 2.50 01/12/2019 + +Note: HD44780 16x2 LCD display is not supported in this release. + +Complete GUI overhaul: + +Button assignment has been improved to provide consistency across all pages. + + [ Save ] button: Cancel/Exit/No + [ Write ] button: Confirm/Enter/Yes + [ Shift1 ] button now opens PageSelect menu in all sub-pages. + [ Shift2 ] button opens an options menu on sequencer pages. + + Encoder buttons no are no longer used for navigating the GUI and instead + work as fast rotate mode, as per the Elektron devices. + + PageSelect layout has been improved: + [ Encoder 1] to scroll through all available pages + [ Encoder 2] to scroll through page categories + [ Save ] to exit back to grid + +Enahanced Graphics: + New graphics for Sequencer Pages. + Visual Chromatic Keyboard + Visual uTIMING. + Graphical icons for PageSelect. + Trimetric Spites. + GUI popups. + Loading screens. + Graphical Encoders. + +Improved Project and File navigation. + Abiltiy to rename/delete files/projects + +Improved Text Input: + Text input page now features a Char Select Pane. + +Sequencer Track Menu and Trig Menu. + Track menu accessible by holding shift 2. + Allows for changing current tracks. + Copying/pasting/deleting/shifting/reversing tracks. + + + Trig menu accessible by pressing a trig and then holding shift 2. Allows + copying/pasting of trigs, adding/remove trig mutes clearing locks. + +Grid Page: + Slot menu, provides copy + paste options. Encoders 3 and 4 are used to + select a rectangular region of tracks/slots to be copied. + They can be pasted between projects. + +SD Drive: + Poor man's plus drive. Backup/restore MD's entire memory including pattern + + kits + globals. All stored on the SD Card for retrieval. + +RAM Pages: + Two RAM pages are provided Page Select pages 15 + 16. + These machines take the hastle out of RAM record + playback by automating RAM machine sampling of the MD output or external input. + Recordings can be sliced, and maniputated using a variety of effects. + +FX Pages: + Two FX pages used to control Delay or Reverb. + +LFO Page: + A single global LFO (Lower frequency oscillator) page is provided which can + be used to manipulate any MD parameter, either a Track Param or Global FX. + Two destinations are selectable and a variatey of wave shapes available. + The LFO can be triggered by a dedicated 16 step sequencer track, or be set + to run freely. + +Firmware Performance & MIDI Timing : + Improvements to the low level MIDI interrupt code means reduced latency and + tighter sequencer performance. + Faster GUI routines, means higher framerate and snappier performance. + Improved latency coompensation for realtime recording on Chromatic and + Record pages. + Device peering now works from any page. + MCL 2.40 25/06/2019 Important: From b38a01b024bf23f75bf592cbe63eee76d492157a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 25 Nov 2019 10:58:40 +0800 Subject: [PATCH 373/469] update .gitignore --- avr/cores/megacommand/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 avr/cores/megacommand/.gitignore diff --git a/avr/cores/megacommand/.gitignore b/avr/cores/megacommand/.gitignore new file mode 100644 index 000000000..55d00c567 --- /dev/null +++ b/avr/cores/megacommand/.gitignore @@ -0,0 +1 @@ +.vim From 21835ad7da6d1a0c468e70119f8f26bac9946489 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 25 Nov 2019 12:51:53 +0800 Subject: [PATCH 374/469] fix #84 --- avr/cores/megacommand/MCL/TextInputPage.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index e7022a84b..8b63fa818 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -257,18 +257,20 @@ bool TextInputPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON3)) { - if (cursor_position == 0) { - return false; - } - if (cursor_position == length - 1 && isspace(text[cursor_position])) { + if (cursor_position == length - 1 && !isspace(text[cursor_position])) { // delete last text[cursor_position] = ' '; } else { + if (cursor_position == 0) { + // move the cursor, and delete first + cursor_position = 1; + } // backspace for (uint8_t i = cursor_position - 1; i < length - 1; ++i) { text[i] = text[i + 1]; } + text[length - 1] = ' '; --cursor_position; } config_normal(); From 0becd96cd2135815cfb4a25649d089e7fe66a2b7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 11:05:44 +1100 Subject: [PATCH 375/469] Update mc_display mirror to take screenshot when pressing '9' on keyboard --- python/mc_display_mirror.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/python/mc_display_mirror.py b/python/mc_display_mirror.py index 42192552c..4b46151c8 100755 --- a/python/mc_display_mirror.py +++ b/python/mc_display_mirror.py @@ -6,7 +6,7 @@ import serial import pygame - +import os def main(): @@ -23,7 +23,7 @@ def main(): size = [(128 + 2) * 7, (32 + 2) * 7] screen = pygame.display.set_mode(size) done = False - + file_count = 0; BG = WHITE FG = BLACK import serial.tools.list_ports @@ -79,16 +79,6 @@ def main(): y = 0 # 7bit decode, 0 command bytes, 1 data bytes. while not done: - for event in pygame.event.get(): # User did something - if event.type == pygame.QUIT: # If user clicked close - done = True # Flag that we are done so we exit this loop - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_2: - BG = BLACK - FG = WHITE - if event.key == pygame.K_1: - FG = BLACK - BG = WHITE draw = True n = 0 c = 255 @@ -124,6 +114,19 @@ def main(): pygame.draw.rect( screen, FG, [(x + 1) * w, (y + 1) * w, w, w]) pygame.display.flip() + for event in pygame.event.get(): # User did something + if event.type == pygame.QUIT: # If user clicked close + done = True # Flag that we are done so we exit this loop + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_2: + BG = BLACK + FG = WHITE + if event.key == pygame.K_1: + FG = BLACK + BG = WHITE + if event.key == pygame.K_9: + pygame.image.save(screen, os.path.expanduser("~/Desktop/") + "mcl_screenshot" + str(file_count) + ".png"); + file_count += 1 screen.fill(BG) From 7bfca967c5cdcdf08dc7bcb5bf97a338000432e0 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 12:06:28 +1100 Subject: [PATCH 376/469] Switch LFO save/write button --- avr/cores/megacommand/MCL/LFOPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 9f1186381..f25458c96 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -380,12 +380,12 @@ bool LFOPage::handleEvent(gui_event_t *event) { GUI.setPage(&grid_page); } */ - if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { page_mode = !(page_mode); update_encoders(); } - if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { if (lfo_track->mode >= LFO_MODE_ONE) { lfo_track->mode = 0; } else { From c040c23c28b68ea0ac41a5020ae8b584a9555dbd Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 12:07:41 +1100 Subject: [PATCH 377/469] Fix: Encoder 3 no longer changes quantization on route page --- avr/cores/megacommand/MCL/AuxPages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/AuxPages.cpp b/avr/cores/megacommand/MCL/AuxPages.cpp index 1cf4422e0..6b99cfbcf 100644 --- a/avr/cores/megacommand/MCL/AuxPages.cpp +++ b/avr/cores/megacommand/MCL/AuxPages.cpp @@ -17,7 +17,7 @@ extern MCLEncoder fx_param4(0, 127); MixerPage mixer_page(&mixer_param1, &mixer_param2, &mixer_param3, &mixer_param4); -RoutePage route_page(&route_param1, &route_param2, &route_param2); +RoutePage route_page(&route_param1, &route_param2); fx_param_t fx_echo_params[8] = { {MD_FX_ECHO, MD_ECHO_TIME}, From fea2949ac7c3339e861cad609c775c3eb148dc74 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 12:19:45 +1100 Subject: [PATCH 378/469] Fix yes/no rectangles --- avr/cores/megacommand/MCL/QuestionDialogPage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp index 02d2348ab..b15def198 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -17,11 +17,11 @@ void QuestionDialogPage::init(const char* title, const char* text) { oled_display.setCursor(MCLGUI::dlg_info_x2 - 55, MCLGUI::dlg_info_y1 + 24); oled_display.print("W YES"); - oled_display.drawRect(MCLGUI::dlg_info_x2 - 88, MCLGUI::dlg_info_y1 + 17, 21, 8, WHITE); - oled_display.drawRect(MCLGUI::dlg_info_x2 - 57, MCLGUI::dlg_info_y1 + 17, 19, 8, WHITE); + oled_display.drawRect(MCLGUI::dlg_info_x2 - 88, MCLGUI::dlg_info_y1 + 16, 21, 9, WHITE); + oled_display.drawRect(MCLGUI::dlg_info_x2 - 57, MCLGUI::dlg_info_y1 + 16, 21, 9, WHITE); - oled_display.fillRect(MCLGUI::dlg_info_x2 - 87, MCLGUI::dlg_info_y1 + 18, 5, 6, INVERT); - oled_display.fillRect(MCLGUI::dlg_info_x2 - 56, MCLGUI::dlg_info_y1 + 18, 5, 6, INVERT); + oled_display.fillRect(MCLGUI::dlg_info_x2 - 87, MCLGUI::dlg_info_y1 + 17, 5, 7, INVERT); + oled_display.fillRect(MCLGUI::dlg_info_x2 - 56, MCLGUI::dlg_info_y1 + 17, 5, 7, INVERT); oled_display.setFont(oldfont); oled_display.display(); From a968545378b620ea709cfafcc979e934281c3395 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 21:19:06 +1100 Subject: [PATCH 379/469] Constructor typo --- avr/cores/megacommand/Midi/MidiID.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/Midi/MidiID.hh b/avr/cores/megacommand/Midi/MidiID.hh index 585f7bd77..570833763 100644 --- a/avr/cores/megacommand/Midi/MidiID.hh +++ b/avr/cores/megacommand/Midi/MidiID.hh @@ -15,7 +15,7 @@ public: uint8_t family_code[2] = {DEVICE_NULL, DEVICE_NULL}; uint8_t family_member[2]; char software_revision[4]; - MidiId() { init(); } + MidiID() { init(); } void init(); void send_id_request(uint8_t id, uint8_t port); bool getBlockingId(uint8_t id, uint8_t port, uint16_t timeout); From 0901dea108e61547464932cfd2d06587a6ae9dc6 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 21:30:45 +1100 Subject: [PATCH 380/469] Declare oled_display even when OLED_DISPLAY is not defined This makes prevents us from having to #ifdef for all functions that use oled_display. --- avr/cores/megacommand/MCL/MCL.h | 2 -- avr/cores/megacommand/MCL/MidiActivePeering.cpp | 4 ++++ avr/cores/megacommand/OLED.h | 2 -- avr/cores/megacommand/main.cpp | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index de89971de..d0cd5378e 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -84,10 +84,8 @@ #include "Shared.h" //MCL Fonts -#ifdef OLED_DISPLAY #include "Fonts/TomThumb.h" #include "Fonts/Elektrothic.h" -#endif #define VERSION 2050 #define VERSION_STR "2.50" diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.cpp b/avr/cores/megacommand/MCL/MidiActivePeering.cpp index a3392a27d..7531bd340 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.cpp +++ b/avr/cores/megacommand/MCL/MidiActivePeering.cpp @@ -97,7 +97,9 @@ void MidiActivePeering::md_setup() { } MidiIDSysexListener.cleanup(); +#ifdef OLED_DISPLAY oled_display.setFont(oldfont); +#endif } void MidiActivePeering::a4_setup() { @@ -136,7 +138,9 @@ void MidiActivePeering::a4_setup() { GUI.flash_strings_fill("MIDI DEVICE", "CONNECTED"); #endif } +#ifdef OLED_DISPLAY oled_display.setFont(oldfont); +#endif } void MidiActivePeering::run() { char str[16]; diff --git a/avr/cores/megacommand/OLED.h b/avr/cores/megacommand/OLED.h index 05ea92553..932bd53e4 100644 --- a/avr/cores/megacommand/OLED.h +++ b/avr/cores/megacommand/OLED.h @@ -13,8 +13,6 @@ #define OLED_DC 44 #define OLED_RESET 38 -#ifdef OLED_DISPLAY extern Adafruit_SSD1305 oled_display; -#endif #endif /* OLED_H__ */ diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index c90c9ad2c..2fd1ef01a 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -23,9 +23,7 @@ extern "C" { #define OLED_DC 44 #define OLED_RESET 38 -#ifdef OLED_DISPLAY Adafruit_SSD1305 oled_display(OLED_DC, OLED_RESET, OLED_CS); -#endif // extern MidiClockClass MidiClock; // extern volatile uint16_t clock = 0; From ecebb949ba12c9dddc0ff6593146d55d19eee0ca Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 21:43:23 +1100 Subject: [PATCH 381/469] Revert "Declare oled_display even when OLED_DISPLAY is not defined" This reverts commit 0901dea108e61547464932cfd2d06587a6ae9dc6. Actually not a good idea for users that want to compile for the original Minicommand HW, where space is a concern. --- avr/cores/megacommand/MCL/MCL.h | 2 ++ avr/cores/megacommand/MCL/MidiActivePeering.cpp | 4 ---- avr/cores/megacommand/OLED.h | 2 ++ avr/cores/megacommand/main.cpp | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index d0cd5378e..de89971de 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -84,8 +84,10 @@ #include "Shared.h" //MCL Fonts +#ifdef OLED_DISPLAY #include "Fonts/TomThumb.h" #include "Fonts/Elektrothic.h" +#endif #define VERSION 2050 #define VERSION_STR "2.50" diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.cpp b/avr/cores/megacommand/MCL/MidiActivePeering.cpp index 7531bd340..a3392a27d 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.cpp +++ b/avr/cores/megacommand/MCL/MidiActivePeering.cpp @@ -97,9 +97,7 @@ void MidiActivePeering::md_setup() { } MidiIDSysexListener.cleanup(); -#ifdef OLED_DISPLAY oled_display.setFont(oldfont); -#endif } void MidiActivePeering::a4_setup() { @@ -138,9 +136,7 @@ void MidiActivePeering::a4_setup() { GUI.flash_strings_fill("MIDI DEVICE", "CONNECTED"); #endif } -#ifdef OLED_DISPLAY oled_display.setFont(oldfont); -#endif } void MidiActivePeering::run() { char str[16]; diff --git a/avr/cores/megacommand/OLED.h b/avr/cores/megacommand/OLED.h index 932bd53e4..05ea92553 100644 --- a/avr/cores/megacommand/OLED.h +++ b/avr/cores/megacommand/OLED.h @@ -13,6 +13,8 @@ #define OLED_DC 44 #define OLED_RESET 38 +#ifdef OLED_DISPLAY extern Adafruit_SSD1305 oled_display; +#endif #endif /* OLED_H__ */ diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index 2fd1ef01a..c90c9ad2c 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -23,7 +23,9 @@ extern "C" { #define OLED_DC 44 #define OLED_RESET 38 +#ifdef OLED_DISPLAY Adafruit_SSD1305 oled_display(OLED_DC, OLED_RESET, OLED_CS); +#endif // extern MidiClockClass MidiClock; // extern volatile uint16_t clock = 0; From d0a0b226f1667e27d420276e71dc68f36a1608c5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 22:02:06 +1100 Subject: [PATCH 382/469] Hide references to oled_display for HD44780. Compiles now --- avr/cores/megacommand/GUI/GUI.cpp | 2 +- avr/cores/megacommand/MCL/MCLGUI.cpp | 56 +++++++++++++++++-- .../megacommand/MCL/MidiActivePeering.cpp | 7 ++- .../megacommand/MCL/QuestionDialogPage.cpp | 4 ++ avr/cores/megacommand/MCL/SeqPage.cpp | 6 +- avr/cores/megacommand/MCL/TextInputPage.cpp | 15 ++--- 6 files changed, 75 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/GUI/GUI.cpp b/avr/cores/megacommand/GUI/GUI.cpp index 757ccbcd2..02ed504a2 100644 --- a/avr/cores/megacommand/GUI/GUI.cpp +++ b/avr/cores/megacommand/GUI/GUI.cpp @@ -217,12 +217,12 @@ void GuiClass::display() { n = n + 7; } } -#endif #endif if (page->classic_display) { oled_display.display(); } #endif +#endif } char hex2c(uint8_t hex) { diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 01e3f2589..810f6ae5f 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -21,24 +21,31 @@ bool MCLGUI::wait_for_confirm(const char *title, const char *text) { } void MCLGUI::draw_vertical_dashline(uint8_t x, uint8_t from, uint8_t to) { +#ifdef OLED_DISPLAY for (uint8_t y = from; y < to; y += 2) { oled_display.drawPixel(x, y, WHITE); } +#endif } void MCLGUI::draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to) { +#ifdef OLED_DISPLAY for (uint8_t x = from; x < to; x += 2) { oled_display.drawPixel(x, y, WHITE); } +#endif } void MCLGUI::draw_horizontal_arrow(uint8_t x, uint8_t y, uint8_t w) { +#ifdef OLED_DISPLAY oled_display.drawFastHLine(x, y, w, WHITE); oled_display.drawFastVLine(x + w - 2, y - 1, 3, WHITE); oled_display.drawFastVLine(x + w - 3, y - 2, 5, WHITE); +#endif } void MCLGUI::draw_vertical_separator(uint8_t x) { +#ifdef OLED_DISPLAY auto x_ = x + 2; for (uint8_t y = 0; y < 32; y += 2) { oled_display.drawPixel(x, y, WHITE); @@ -48,24 +55,29 @@ void MCLGUI::draw_vertical_separator(uint8_t x) { for (uint8_t y = 1; y < 32; y += 2) { oled_display.drawPixel(x_, y, WHITE); } +#endif } void MCLGUI::draw_vertical_scrollbar(uint8_t x, uint8_t n_items, uint8_t n_window, uint8_t n_current) { +#ifdef OLED_DISPLAY uint8_t length = round(((float)(n_window - 1) / (float)(n_items - 1)) * 32); uint8_t y = round(((float)(n_current) / (float)(n_items - 1)) * 32); mcl_gui.draw_vertical_separator(x + 1); oled_display.fillRect(x + 1, y + 1, 3, length - 2, BLACK); oled_display.drawRect(x, y, 5, length, WHITE); +#endif } void MCLGUI::draw_knob_frame() { +#ifdef OLED_DISPLAY for (uint8_t x = knob_x0; x <= knob_xend; x += knob_w) { mcl_gui.draw_vertical_dashline(x, 0, knob_y); oled_display.drawPixel(x, knob_y, WHITE); oled_display.drawPixel(x, knob_y + 1, WHITE); } mcl_gui.draw_horizontal_dashline(knob_y, knob_x0 + 1, knob_xend + 1); +#endif } void MCLGUI::draw_knob(uint8_t i, const char *title, const char *text) { @@ -82,7 +94,7 @@ static char title_buf[16]; // ref: Design/popup_menu.png void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { - +#ifdef OLED_DISPLAY strcpy(title_buf, title); m_toupper(title_buf); @@ -111,13 +123,16 @@ void MCLGUI::draw_popup(const char *title, bool deferred_display, uint8_t h) { if (!deferred_display) { oled_display.display(); } +#endif } void MCLGUI::clear_popup(uint8_t h) { +#ifdef OLED_DISPLAY if (h == 0) { h = s_menu_h; } oled_display.fillRect(s_menu_x + 1, s_menu_y + 4, s_menu_w - 2, h - 5, BLACK); +#endif } void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, @@ -129,7 +144,7 @@ void MCLGUI::draw_progress(const char *msg, uint8_t cur, uint8_t _max, } void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, uint8_t x_pos, uint8_t y_pos) { - +#ifdef OLED_DISPLAY oled_display.fillRect(x_pos + 1, y_pos + 1, s_progress_w - 2, s_progress_h - 2, BLACK); @@ -169,11 +184,13 @@ void MCLGUI::draw_progress_bar(uint8_t cur, uint8_t _max, bool deferred_display, if (!deferred_display) { oled_display.display(); } +#endif } // ref: Design/infobox.png void MCLGUI::draw_infobox(const char *line1, const char *line2, const int line2_offset) { +#ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); oled_display.fillRect(dlg_info_x1 - 1, dlg_info_y1 - 1, dlg_info_w + 3, @@ -203,10 +220,11 @@ void MCLGUI::draw_infobox(const char *line1, const char *line2, oled_display.println(line2); oled_display.setFont(oldfont); +#endif } void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { - +#ifdef OLED_DISPLAY bool vert_flip = false; bool horiz_flip = false; uint8_t image_w = 11; @@ -260,6 +278,7 @@ void MCLGUI::draw_encoder(uint8_t x, uint8_t y, uint8_t value) { oled_display.drawBitmap(x, y, encoder_small_6, image_w, image_h, WHITE, vert_flip, horiz_flip); } +#endif } void MCLGUI::draw_encoder(uint8_t x, uint8_t y, Encoder *encoder) { @@ -290,6 +309,7 @@ bool MCLGUI::show_encoder_value(Encoder *encoder) { void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, const char *value) { +#ifdef OLED_DISPLAY oled_display.setFont(&TomThumb); oled_display.setTextColor(WHITE); oled_display.setCursor(x + 4, y + 6); @@ -298,6 +318,7 @@ void MCLGUI::draw_text_encoder(uint8_t x, uint8_t y, const char *name, oled_display.setFont(); oled_display.setCursor(x + 4, y + 8); oled_display.print(value); +#endif } void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, @@ -308,7 +329,7 @@ void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, Encoder *encoder, void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value) { - +#ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); uint8_t image_w = 11; @@ -347,6 +368,7 @@ void MCLGUI::draw_md_encoder(uint8_t x, uint8_t y, uint8_t value, } oled_display.setFont(oldfont); +#endif } void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, @@ -357,6 +379,7 @@ void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, Encoder *encoder, void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, const char *name, bool show_value) { +#ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); oled_display.setFont(&TomThumb); @@ -388,9 +411,11 @@ void MCLGUI::draw_light_encoder(uint8_t x, uint8_t y, uint8_t value, draw_encoder(x, y, value); oled_display.setFont(oldfont); +#endif } void MCLGUI::draw_microtiming(uint8_t resolution, uint8_t timing) { +#ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); oled_display.setFont(&TomThumb); @@ -433,12 +458,14 @@ void MCLGUI::draw_microtiming(uint8_t resolution, uint8_t timing) { if (a == degrees / 2) { a = 0; } x += x_w; } -oled_display.setFont(oldfont); + oled_display.setFont(oldfont); +#endif } void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, uint8_t note_height, uint8_t num_of_notes, uint64_t note_mask) { +#ifdef OLED_DISPLAY const uint16_t chromatic = 0b0000010101001010; const uint8_t half = note_height / 2; const uint8_t y2 = y + note_height - 1; @@ -486,11 +513,13 @@ void MCLGUI::draw_keyboard(uint8_t x, uint8_t y, uint8_t note_width, note_type = 0; } } +#endif } void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, uint64_t pattern_mask, uint8_t step_count, uint8_t length, uint64_t mute_mask) { +#ifdef OLED_DISPLAY for (int i = 0; i < 16; i++) { uint8_t idx = i + offset; @@ -530,10 +559,12 @@ void MCLGUI::draw_trigs(uint8_t x, uint8_t y, uint8_t offset, x += seq_w + 1; } +#endif } void MCLGUI::draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, uint8_t ext_trackid, bool show_current_step) { +#ifdef OLED_DISPLAY #ifdef EXT_TRACKS int8_t note_held = 0; auto &active_track = mcl_seq.ext_tracks[ext_trackid]; @@ -585,11 +616,13 @@ void MCLGUI::draw_ext_track(uint8_t x, uint8_t y, uint8_t offset, x += seq_w + 1; } #endif // EXT_TRACKS +#endif } void MCLGUI::draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, uint8_t step_count, uint8_t length, bool show_current_step) { +#ifdef OLED_DISPLAY for (int i = 0; i < 16; i++) { uint8_t idx = i + offset; @@ -613,9 +646,11 @@ void MCLGUI::draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, x += seq_w + 1; } +#endif } void MCLGUI::draw_panel_toggle(const char *s1, const char *s2, bool s1_active) { +#ifdef OLED_DISPLAY oled_display.setFont(&TomThumb); if (s1_active) { oled_display.fillRect(pane_label_x, pane_label_md_y, pane_label_w, @@ -635,9 +670,11 @@ void MCLGUI::draw_panel_toggle(const char *s1, const char *s2, bool s1_active) { oled_display.setCursor(pane_label_x + 1, pane_label_ex_y + 6); oled_display.print(s2); oled_display.setTextColor(WHITE); +#endif } void MCLGUI::draw_panel_labels(const char *info1, const char *info2) { +#ifdef OLED_DISPLAY oled_display.setFont(&TomThumb); oled_display.fillRect(0, pane_info1_y, pane_w, pane_info_h, WHITE); oled_display.setTextColor(BLACK); @@ -647,9 +684,11 @@ void MCLGUI::draw_panel_labels(const char *info1, const char *info2) { oled_display.setTextColor(WHITE); oled_display.setCursor(1, pane_info2_y + 6); oled_display.print(info2); +#endif } void MCLGUI::draw_panel_status(bool recording, bool playing) { +#ifdef OLED_DISPLAY if (recording) { oled_display.fillRect(pane_cir_x1, pane_tri_y, 4, 5, WHITE); oled_display.drawPixel(pane_cir_x1, pane_tri_y, BLACK); @@ -665,17 +704,23 @@ void MCLGUI::draw_panel_status(bool recording, bool playing) { } else { oled_display.fillRect(pane_tri_x, pane_tri_y, 4, 5, WHITE); } +#endif } void MCLGUI::clear_leftpane() { +#ifdef OLED_DISPLAY oled_display.fillRect(0, 0, pane_w, 32, BLACK); +#endif } void MCLGUI::clear_rightpane() { +#ifdef OLED_DISPLAY oled_display.fillRect(pane_w, 0, 128 - pane_w, 32, BLACK); +#endif } void MCLGUI::draw_panel_number(uint8_t number) { +#ifdef OLED_DISPLAY oled_display.setTextColor(WHITE); oled_display.setFont(&Elektrothic); oled_display.setCursor(pane_trackid_x, pane_trackid_y); @@ -683,6 +728,7 @@ void MCLGUI::draw_panel_number(uint8_t number) { oled_display.print('0'); } oled_display.print(number); +#endif } // ================ SPRITES ================ diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.cpp b/avr/cores/megacommand/MCL/MidiActivePeering.cpp index a3392a27d..727fb7798 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.cpp +++ b/avr/cores/megacommand/MCL/MidiActivePeering.cpp @@ -17,11 +17,12 @@ uint8_t MidiActivePeering::get_device(uint8_t port) { } void MidiActivePeering::prepare_display() { +#ifdef OLED_DISPLAY oled_display.clearDisplay(); oled_display.setFont(); oled_display.setCursor(60, 10); oled_display.println("Peering..."); - +#endif } void MidiActivePeering::delay_progress(uint16_t clock_) { @@ -97,7 +98,9 @@ void MidiActivePeering::md_setup() { } MidiIDSysexListener.cleanup(); +#ifdef OLED_DISPLAY oled_display.setFont(oldfont); +#endif } void MidiActivePeering::a4_setup() { @@ -136,7 +139,9 @@ void MidiActivePeering::a4_setup() { GUI.flash_strings_fill("MIDI DEVICE", "CONNECTED"); #endif } +#ifdef OLED_DISPLAY oled_display.setFont(oldfont); +#endif } void MidiActivePeering::run() { char str[16]; diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp index b15def198..94315a7b0 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -36,14 +36,18 @@ bool QuestionDialogPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { +#ifdef OLED_DISPLAY oled_display.fillRect(MCLGUI::dlg_info_x2 - 82, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); +#endif return true; } if (EVENT_PRESSED(event, Buttons.BUTTON4)) { +#ifdef OLED_DISPLAY oled_display.fillRect(MCLGUI::dlg_info_x2 - 51, MCLGUI::dlg_info_y1 + 18, 12, 6, INVERT); oled_display.display(); +#endif return true; } diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index d7527199d..5e7ae58fe 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -1,7 +1,7 @@ #include "MCL.h" #include "SeqPage.h" - uint8_t SeqPage::page_select = 0; +uint8_t SeqPage::page_select = 0; uint8_t SeqPage::midi_device = DEVICE_MD; @@ -204,6 +204,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { } } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { +#ifdef OLED_DISPLAY encoders[0] = opt_param1_capture; encoders[1] = opt_param2_capture; oled_display.clearDisplay(); @@ -233,6 +234,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { show_step_menu = false; mcl_gui.init_encoders_used_clock(); return true; +#endif } #else if (EVENT_PRESSED(event, Buttons.BUTTON3)) { @@ -782,6 +784,7 @@ void SeqPage::loop() { } void SeqPage::draw_page_index(bool show_page_index, uint8_t _playing_idx) { +#ifdef OLED_DISPLAY // draw page index uint8_t pidx_x = pidx_x0; bool blink = MidiClock.getBlinkHint(true); @@ -827,6 +830,7 @@ void SeqPage::draw_page_index(bool show_page_index, uint8_t _playing_idx) { pidx_x += w + 1; } +#endif } #ifndef OLED_DISPLAY diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 8b63fa818..8e4b3f59c 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -94,10 +94,9 @@ static void calc_charpane_coord(uint8_t &x, uint8_t &y) { // E0 -> x axis [0..17] // E1 -> y axis [0..3] void TextInputPage::config_charpane() { -#ifndef OLED_DISPLAY +#ifdef OLED_DISPLAY // char pane not supported on 1602 displays - return; -#endif + ((MCLEncoder *)encoders[0])->max = charpane_w - 1; ((MCLEncoder *)encoders[1])->max = charpane_h - 1; normal_mode = false; @@ -128,6 +127,7 @@ void TextInputPage::config_charpane() { calc_charpane_coord(sx, sy); oled_display.fillRect(sx, sy, 7, 7, INVERT); oled_display.display(); +#endif } void TextInputPage::display_normal() { @@ -149,11 +149,11 @@ void TextInputPage::display_normal() { encoders[1]->old = encoders[1]->cur; text[cursor_position] = _getchar(encoders[1]->getValue()); } - auto oldfont = oled_display.getFont(); auto time = clock_diff(last_clock, slowclock); #ifdef OLED_DISPLAY // mcl_gui.clear_popup(); <-- E_TOOSLOW + auto oldfont = oled_display.getFont(); oled_display.fillRect(s_text_x, s_text_y, 6 * length, 8, BLACK); oled_display.setFont(); oled_display.setCursor(s_text_x, s_text_y); @@ -190,7 +190,7 @@ void TextInputPage::display_normal() { } void TextInputPage::display_charpane() { - +#ifdef OLED_DISPLAY if (encoders[0]->hasChanged() || encoders[1]->hasChanged()) { // clear old highlight uint8_t sx = encoders[0]->old, sy = encoders[1]->old; @@ -211,6 +211,7 @@ void TextInputPage::display_charpane() { last_clock = slowclock; oled_display.display(); +#endif } void TextInputPage::display() { @@ -224,7 +225,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { return true; } - + #ifdef OLED_DIPLAY // in char-pane mode, do not handle any events // except shift-release event. if (!normal_mode) { @@ -241,7 +242,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { } return false; } - + #endif if (EVENT_RELEASED(event, Buttons.BUTTON1)) { if (!no_escape) { return_state = false; From 9b1d88d09b189453b3c873066e696e80b1a31ede Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 22:29:23 +1100 Subject: [PATCH 383/469] Fixes for compiling in MiniCommand mode --- avr/cores/megacommand/MCL/MCLClipBoard.cpp | 9 ++- avr/cores/megacommand/MCL/PageSelectPage.cpp | 8 ++- avr/cores/megacommand/MCL/SeqExtStepPage.cpp | 3 +- avr/cores/megacommand/MCL/SeqPage.cpp | 72 +++++++++++++++----- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 6 +- 5 files changed, 74 insertions(+), 24 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index 548e29f64..4ae0a00cd 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -82,13 +82,13 @@ bool MCLClipBoard::copy_sequencer_track(uint8_t track) { md_track->get_machine_from_kit(track, track); ret = mcl_sd.write_data(&temp_track, sizeof(MDTrackLight), &file); } - +#ifdef EXT_TRACKS else { memcpy(&temp_track, &mcl_seq.ext_tracks[track - NUM_MD_TRACKS], sizeof(ExtSeqTrackData)); ret = mcl_sd.write_data(&temp_track, sizeof(ExtSeqTrack), &file); } - +#endif close(); if (!ret) { DEBUG_PRINTLN("failed write"); @@ -157,10 +157,13 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { DEBUG_PRINTLN("sending seq track"); md_track->place_track_in_kit(track, track, &(MD.kit), true); MD.setMachine(track, &(md_track->machine)); - } else if (track >= NUM_MD_TRACKS) { + } +#ifdef EXT_TRACKS + else if (track >= NUM_MD_TRACKS) { memcpy(&mcl_seq.ext_tracks[track - NUM_MD_TRACKS], &(temp_track), sizeof(ExtSeqTrackData)); } +#endif close(); return true; } diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 84244ddd5..a9be6c194 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -35,11 +35,15 @@ const PageSelectEntry Entries[] PROGMEM = { {"RECORD", &seq_rtrk_page, 5, 1, 24, 15, (uint8_t *)icon_rec}, {"LOCKS", &seq_param_page[0], 6, 1, 24, 19, (uint8_t *)icon_para}, {"CHROMA", &seq_ptc_page, 7, 1, 24, 25, (uint8_t *)icon_chroma}, - +#ifdef SOUND_PAGE {"SOUND MANAGER", &sound_browser, 8, 2, 24, 19, (uint8_t *)icon_sound}, +#endif +#ifdef WAV_DESIGNER {"WAV DESIGNER", &wd.pages[0], 9, 2, 24, 19, (uint8_t *)icon_wavd}, +#endif +#ifdef LOUDNESS_PAGE {"LOUDNESS", &loudness_page, 10, 2, 24, 16, (uint8_t *)icon_loudness}, - +#endif {"DELAY", &fx_page_a, 12, 3, 24, 25, (uint8_t *)icon_rhytmecho}, {"REVERB", &fx_page_b, 13, 3, 24, 25, (uint8_t *)icon_gatebox}, {"RAM-1", &ram_page_a, 14, 3, 19, 19, (uint8_t *)wheel_top}, diff --git a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp index a434d0aef..cd8a9bee3 100644 --- a/avr/cores/megacommand/MCL/SeqExtStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqExtStepPage.cpp @@ -260,8 +260,9 @@ bool SeqExtStepPage::handleEvent(gui_event_t *event) { if (SeqPage::handleEvent(event)) { return true; } - auto &active_track = mcl_seq.ext_tracks[last_ext_track]; + #ifdef EXT_TRACKS + auto &active_track = mcl_seq.ext_tracks[last_ext_track]; if (note_interface.is_event(event)) { uint8_t mask = event->mask; uint8_t port = event->port; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 5e7ae58fe..b3b29b6ea 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -572,7 +572,9 @@ void opt_clear_track_handler() { } else if (opt_clear == 1) { mcl_seq.md_tracks[last_md_track].clear_track(); } - } else { + } +#ifdef EXT_TRACKS + else { if (opt_clear == 2) { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { mcl_seq.ext_tracks[n].clear_track(); @@ -581,6 +583,7 @@ void opt_clear_track_handler() { mcl_seq.ext_tracks[last_ext_track].clear_track(); } } +#endif opt_clear = 0; } @@ -601,34 +604,46 @@ void opt_clear_locks_handler() { void opt_clear_all_tracks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - } else { + } +#ifdef EXT_TRACKS + else { mcl_seq.ext_tracks[last_ext_track].clear_track(); } +#endif } void opt_clear_all_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { - } else { + } +#ifdef EXT_TRACKS + else { // TODO ext locks } +#endif } void opt_copy_track_handler() { if (opt_copy == 2) { if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.copy_sequencer(); - } else { + } +#ifdef EXT_TRACKS + else { mcl_clipboard.copy_sequencer(NUM_MD_TRACKS); } +#endif } if (opt_copy == 1) { if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.copy_track = last_md_track; mcl_clipboard.copy_sequencer_track(last_md_track); - } else { + } +#ifdef EXT_TRACKS + else { mcl_clipboard.copy_track = last_ext_track + NUM_MD_TRACKS; mcl_clipboard.copy_sequencer_track(last_ext_track + NUM_MD_TRACKS); } +#endif } opt_copy = 0; } @@ -637,18 +652,24 @@ void opt_paste_track_handler() { if (opt_paste == 2) { if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.paste_sequencer(); - } else { + } +#ifdef EXT_TRACKS + else { mcl_clipboard.paste_sequencer(NUM_MD_TRACKS); } +#endif } if (opt_paste == 1) { if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_md_track); - } else { + } +#ifdef EXT_TRACKS + else { mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_ext_track + NUM_MD_TRACKS); } +#endif } opt_paste = 0; } @@ -695,40 +716,50 @@ void opt_shift_track_handler() { case 1: if (opt_midi_device_capture == DEVICE_MD) { mcl_seq.md_tracks[last_md_track].rotate_left(); - } else { + } +#ifdef EXT_TRACKS + else { mcl_seq.ext_tracks[last_ext_track].rotate_left(); } - +#endif break; case 2: if (opt_midi_device_capture == DEVICE_MD) { mcl_seq.md_tracks[last_md_track].rotate_right(); - } else { + } +#ifdef EXT_TRACKS + else { mcl_seq.ext_tracks[last_ext_track].rotate_right(); } +#endif break; case 3: if (opt_midi_device_capture == DEVICE_MD) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { mcl_seq.md_tracks[n].rotate_left(); } - } else { + } +#ifdef EXT_TRACKS + else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { mcl_seq.ext_tracks[n].rotate_left(); } } +#endif break; case 4: if (opt_midi_device_capture == DEVICE_MD) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { mcl_seq.md_tracks[n].rotate_right(); } - } else { + } +#ifdef EXT_TRACKS +else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { mcl_seq.ext_tracks[n].rotate_right(); } } - +#endif break; } } @@ -740,19 +771,25 @@ void opt_reverse_track_handler() { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { mcl_seq.md_tracks[n].reverse(); } - } else { + } +#ifdef EXT_TRACKS + else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { mcl_seq.ext_tracks[n].reverse(); } } +#endif } if (opt_reverse == 1) { if (opt_midi_device_capture == DEVICE_MD) { mcl_seq.md_tracks[last_md_track].reverse(); - } else { + } +#ifdef EXT_TRACKS + else { mcl_seq.ext_tracks[last_ext_track].reverse(); } +#endif } } @@ -796,12 +833,15 @@ void SeqPage::draw_page_index(bool show_page_index, uint8_t _playing_idx) { (mcl_seq.md_tracks[last_md_track].step_count - ((mcl_seq.md_tracks[last_md_track].step_count) / 16) * 16) / 4; - } else { + } +#ifdef EXT_TRACKS + else { playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count - ((mcl_seq.ext_tracks[last_ext_track].step_count) / 16) * 16) / 4; } +#endif } else { playing_idx = _playing_idx; } diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 73f5a100f..771475f22 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -121,7 +121,9 @@ void SeqPtcPage::config() { if (midi_device == DEVICE_MD) { str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); - } else { + } + #ifdef EXT_TRACKS + else { char str_first[3] = "--"; if (Analog4.connected) { strcpy(str_first, "A4"); @@ -133,7 +135,7 @@ void SeqPtcPage::config() { str1 = &str_first[0]; str2 = &str_second[0]; } - + #endif m_strncpy_p(buf, str1, len1); strncpy(info1, buf, len1); strncat(info1, ">", len1); From a8af9b9986e04b5bc04f4b734f6ffcf2d686b97e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 22:47:15 +1100 Subject: [PATCH 384/469] Stop rounding tempo. I find the rounding distracting --- avr/cores/megacommand/MCL/GridPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index d9ac8430e..54355188a 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -316,7 +316,7 @@ void GridPage::display_grid_info() { oled_display.setFont(&Elektrothic); oled_display.setCursor(0, 10); - oled_display.print(round(MidiClock.get_tempo())); + oled_display.print((int)floor(MidiClock.get_tempo())); display_counters(); oled_display.setFont(&TomThumb); From 8045051102547a8cc97aff4b3d7f495b5116536c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 26 Nov 2019 22:48:17 +1100 Subject: [PATCH 385/469] Revert "Stop rounding tempo. I find the rounding distracting" Does not work. This reverts commit a8af9b9986e04b5bc04f4b734f6ffcf2d686b97e. --- avr/cores/megacommand/MCL/GridPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 54355188a..d9ac8430e 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -316,7 +316,7 @@ void GridPage::display_grid_info() { oled_display.setFont(&Elektrothic); oled_display.setCursor(0, 10); - oled_display.print((int)floor(MidiClock.get_tempo())); + oled_display.print(round(MidiClock.get_tempo())); display_counters(); oled_display.setFont(&TomThumb); From 32ffb5ad4aa0cb34251649f016938ac5255bc223 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Wed, 27 Nov 2019 00:37:22 +0800 Subject: [PATCH 386/469] add MC_Legend --- design/MC_Legend.png | Bin 0 -> 152780 bytes design/MC_Legend.xcf | Bin 0 -> 422209 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 design/MC_Legend.png create mode 100644 design/MC_Legend.xcf diff --git a/design/MC_Legend.png b/design/MC_Legend.png new file mode 100644 index 0000000000000000000000000000000000000000..7b4af6d91fdc49cca2abdb64e7e56fcab79367fd GIT binary patch literal 152780 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

t82a$X0DRJ$r*SL__Ly~x28FKN)D+`9F- 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

Y;bHl|)3LWckXIQ%YN5mLwCt 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 jHpwZ8`T4qHDR=73?Q{+#oH$J8 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 G9y!tZf=uWQY%+g|dVBt$29Yup09XlWG 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 Date: Thu, 28 Nov 2019 22:01:54 +1100 Subject: [PATCH 387/469] Initialise track length if loaded track is EMPTY_TRACK type --- avr/cores/megacommand/MCL/A4Track.cpp | 4 ++++ avr/cores/megacommand/MCL/ExtTrack.cpp | 3 +++ avr/cores/megacommand/MCL/MDTrack.cpp | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/avr/cores/megacommand/MCL/A4Track.cpp b/avr/cores/megacommand/MCL/A4Track.cpp index 814fb2136..94762921b 100644 --- a/avr/cores/megacommand/MCL/A4Track.cpp +++ b/avr/cores/megacommand/MCL/A4Track.cpp @@ -49,6 +49,10 @@ bool A4Track::load_track_from_grid(int32_t column, int32_t row, int m) { DEBUG_PRINTLN("Write failed"); return false; } + if (active == EMPTY_TRACK_TYPE) { + seq_data.length = 16; + } + return true; } bool A4Track::store_track_in_grid(int32_t column, int32_t row, int track, diff --git a/avr/cores/megacommand/MCL/ExtTrack.cpp b/avr/cores/megacommand/MCL/ExtTrack.cpp index 2189d43ea..30c8b3013 100644 --- a/avr/cores/megacommand/MCL/ExtTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtTrack.cpp @@ -38,6 +38,9 @@ bool ExtTrack::load_track_from_grid(int32_t column, int32_t row, int m) { DEBUG_PRINTLN("Read failed"); return false; } + if (active == EMPTY_TRACK_TYPE) { + seq_data.length = 16; + } return true; } bool ExtTrack::store_track_in_grid(int track, int32_t column, int32_t row, bool online) { diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index ec5d0e9ce..30f45fb36 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -210,6 +210,11 @@ bool MDTrack::load_track_from_grid(int32_t column, int32_t row, int32_t len) { DEBUG_PRINTLN("read failed"); return false; } + + if (active == EMPTY_TRACK_TYPE) { + seq_data.length = 16; + } + return true; } From 5bf1b232c9eca357f6fe94b5501b50f7f4f27399 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 28 Nov 2019 23:40:50 +1100 Subject: [PATCH 388/469] Refactor NewProject creation, remove NewProjectPage --- avr/cores/megacommand/MCL/MCLMenus.cpp | 7 ++- avr/cores/megacommand/MCL/MCLMenus.h | 2 + avr/cores/megacommand/MCL/MCLSd.cpp | 10 ++-- avr/cores/megacommand/MCL/NewProjectPage.cpp | 55 -------------------- avr/cores/megacommand/MCL/NewProjectPage.h | 24 --------- avr/cores/megacommand/MCL/Project.cpp | 45 ++++++++++++++++ avr/cores/megacommand/MCL/Project.h | 1 + avr/cores/megacommand/MCL/ProjectPages.cpp | 5 -- avr/cores/megacommand/MCL/ProjectPages.h | 8 --- 9 files changed, 59 insertions(+), 98 deletions(-) delete mode 100644 avr/cores/megacommand/MCL/NewProjectPage.cpp delete mode 100644 avr/cores/megacommand/MCL/NewProjectPage.h diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index c3ea46710..3cdc1f969 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -1,4 +1,5 @@ #include "MCLMenus.h" +#include "Project.h" MCLEncoder options_param1(0, 11, ENCODER_RES_SYS); MCLEncoder options_param2(0, 17, ENCODER_RES_SYS); @@ -12,11 +13,15 @@ MCLEncoder config_param5(0, 17, ENCODER_RES_SYS); MCLEncoder config_param6(0, 17, ENCODER_RES_SYS); MCLEncoder config_param7(0, 17, ENCODER_RES_SYS); +void new_proj_handler() { + proj.new_project(); +} + const menu_t<7> system_menu_layout PROGMEM = { "GLOBAL", { {"LOAD PROJECT" ,0, 0, 0, (uint8_t *) NULL, (Page*) &load_proj_page, NULL, {}}, - {"NEW PROJECT",0, 0, 0, (uint8_t *) NULL, (Page*) &new_proj_page, NULL, {}}, + {"NEW PROJECT",0, 0, 0, (uint8_t *) NULL, (Page*) NULL, &new_proj_handler, {}}, {"MIDI",0, 0, 0, (uint8_t *) NULL, (Page*) &midi_config_page, NULL, {}}, {"MACHINEDRUM", 0, 0, 0, (uint8_t *) NULL, (Page*) &md_config_page, NULL, {}}, {"CHAIN MODE", 0, 0, 0, (uint8_t *) NULL, (Page*) &chain_config_page, NULL, {}}, diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 2462ffb30..74483c247 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -25,6 +25,8 @@ extern MCLEncoder config_param5; extern MCLEncoder config_param6; extern MCLEncoder config_param7; +extern void new_proj_handler(); + extern MenuPage<7> system_page; extern MenuPage<5> midi_config_page; extern MenuPage<5> md_config_page; diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index 88f3ef335..0676cff14 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -46,7 +46,7 @@ bool MCLSd::load_init() { DEBUG_PRINTLN("Could not init cfg"); return false; } - GUI.setPage(&new_proj_page); + proj.new_project(); return true; } @@ -55,7 +55,7 @@ bool MCLSd::load_init() { DEBUG_PRINTLN("Project count greater than 0, try to load existing"); if (!proj.load_project(mcl_cfg.project)) { DEBUG_PRINTLN("error loading project"); - GUI.setPage(&new_proj_page); + proj.new_project(); return true; } else { @@ -64,7 +64,7 @@ bool MCLSd::load_init() { } return true; } else { - GUI.setPage(&new_proj_page); + proj.new_project(); return true; } } else { @@ -73,7 +73,7 @@ bool MCLSd::load_init() { if (!mcl_cfg.cfg_init()) { return false; } - GUI.setPage(&new_proj_page); + proj.new_project(); return true; } } else { @@ -81,7 +81,7 @@ bool MCLSd::load_init() { if (!mcl_cfg.cfg_init()) { return false; } - GUI.setPage(&new_proj_page); + proj.new_project(); return true; } return true; diff --git a/avr/cores/megacommand/MCL/NewProjectPage.cpp b/avr/cores/megacommand/MCL/NewProjectPage.cpp deleted file mode 100644 index 2dd84bd86..000000000 --- a/avr/cores/megacommand/MCL/NewProjectPage.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "NewProjectPage.h" -#include "MCL.h" - -void NewProjectPage::setup() { -} - -void NewProjectPage::init() { - DEBUG_PRINTLN("New project page"); - char my_string[sizeof(newprj)] = "project___"; - - my_string[7] = (mcl_cfg.number_projects % 1000) / 100 + '0'; - my_string[7 + 1] = (mcl_cfg.number_projects % 100) / 10 + '0'; - my_string[7 + 2] = (mcl_cfg.number_projects % 10) + '0'; - m_strncpy(newprj, my_string, sizeof(newprj)); - gui_event_t event; - handleEvent(&event); -} - -void NewProjectPage::display() { -} - -bool NewProjectPage::handleEvent(gui_event_t *event) { - // don't handle any events - again: - if (mcl_gui.wait_for_input(newprj, "New Project:", sizeof(newprj))) { - - char full_path[sizeof(newprj) + 5] = {'\0'}; - strcat(full_path, "/"); - strcat(full_path, newprj); - strcat(full_path, ".mcl"); - - gfx.alert("PLEASE WAIT", "CREATING PROJECT"); - - DEBUG_PRINTLN(full_path); - if (SD.exists(full_path)) { - gfx.alert("ERROR", "PROJECT EXISTS"); - goto again; - } - - bool ret = proj.new_project(full_path); - if (ret) { - if (proj.load_project(full_path)) { - grid_page.reload_slot_models = false; - GUI.setPage(&grid_page); - } else { - gfx.alert("ERROR", "SD ERROR"); - goto again; - } - return false; - } - } else if (proj.project_loaded) { - GUI.setPage(&grid_page); - return true; - } -} diff --git a/avr/cores/megacommand/MCL/NewProjectPage.h b/avr/cores/megacommand/MCL/NewProjectPage.h deleted file mode 100644 index 8aa133aec..000000000 --- a/avr/cores/megacommand/MCL/NewProjectPage.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Justin Mammarella jmamma@gmail.com 2018 */ - -#ifndef NEWPROJECTPAGE_H__ -#define NEWPROJECTPAGE_H__ - -#include "GUI.h" - -#define FLASH_SPEED 400 - -class NewProjectPage : public LightPage { -public: - char newprj[14]; - uint16_t last_clock; - - NewProjectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, - Encoder *e4 = NULL) - : LightPage(e1, e2, e3, e4) {} - virtual void display(); - virtual bool handleEvent(gui_event_t *event); - void init(); - void setup(); -}; - -#endif /* NEWPROJECTPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/Project.cpp b/avr/cores/megacommand/MCL/Project.cpp index c734068dc..d1350e7b6 100644 --- a/avr/cores/megacommand/MCL/Project.cpp +++ b/avr/cores/megacommand/MCL/Project.cpp @@ -3,6 +3,51 @@ void Project::setup() {} +bool Project::new_project() { + char newprj[14]; + + char my_string[sizeof(newprj)] = "project___"; + + my_string[7] = (mcl_cfg.number_projects % 1000) / 100 + '0'; + my_string[7 + 1] = (mcl_cfg.number_projects % 100) / 10 + '0'; + my_string[7 + 2] = (mcl_cfg.number_projects % 10) + '0'; + m_strncpy(newprj, my_string, sizeof(newprj)); + again: + + if (mcl_gui.wait_for_input(newprj, "New Project:", sizeof(newprj))) { + + char full_path[sizeof(newprj) + 5] = {'\0'}; + strcat(full_path, "/"); + strcat(full_path, newprj); + strcat(full_path, ".mcl"); + + gfx.alert("PLEASE WAIT", "CREATING PROJECT"); + + DEBUG_PRINTLN(full_path); + if (SD.exists(full_path)) { + gfx.alert("ERROR", "PROJECT EXISTS"); + goto again; + } + + bool ret = proj.new_project(full_path); + if (ret) { + if (proj.load_project(full_path)) { + grid_page.reload_slot_models = false; + DEBUG_PRINTLN("project loaded, setting page to grid"); + GUI.setPage(&grid_page); + return true; + } else { + gfx.alert("ERROR", "SD ERROR"); + goto again; + } + return false; + } + } else if (proj.project_loaded) { + GUI.setPage(&grid_page); + return true; + } +} + bool Project::load_project(const char *projectname) { bool ret; diff --git a/avr/cores/megacommand/MCL/Project.h b/avr/cores/megacommand/MCL/Project.h index d51e0a0e3..9476def0b 100644 --- a/avr/cores/megacommand/MCL/Project.h +++ b/avr/cores/megacommand/MCL/Project.h @@ -19,6 +19,7 @@ class Project : public ProjectHeader { File file; bool project_loaded = false; void setup(); + bool new_project(); bool load_project(const char *projectname); bool check_project_version(); bool new_project(const char *projectname); diff --git a/avr/cores/megacommand/MCL/ProjectPages.cpp b/avr/cores/megacommand/MCL/ProjectPages.cpp index 16c12cf76..bb79719b0 100644 --- a/avr/cores/megacommand/MCL/ProjectPages.cpp +++ b/avr/cores/megacommand/MCL/ProjectPages.cpp @@ -3,10 +3,5 @@ MCLEncoder loadproj_param1(0, 64, ENCODER_RES_SYS); -MCLEncoder newproj_param1(1, 10, ENCODER_RES_SYS); -MCLEncoder newproj_param2(0, 36, ENCODER_RES_SYS); - - -NewProjectPage new_proj_page(&newproj_param1, &newproj_param2); LoadProjectPage load_proj_page(&loadproj_param1,&loadproj_param1); diff --git a/avr/cores/megacommand/MCL/ProjectPages.h b/avr/cores/megacommand/MCL/ProjectPages.h index 8a09b970b..8466ac2cb 100644 --- a/avr/cores/megacommand/MCL/ProjectPages.h +++ b/avr/cores/megacommand/MCL/ProjectPages.h @@ -4,18 +4,10 @@ #define PROJECTPAGES_H__ #include "MCLEncoder.h" -#include "NewProjectPage.h" #include "LoadProjectPage.h" extern MCLEncoder loadproj_param1; -extern MCLEncoder newproj_param1; -extern MCLEncoder newproj_param2; -extern MCLEncoder newproj_param4; - - -extern NewProjectPage new_proj_page; - extern LoadProjectPage load_proj_page; #endif /* PROJECTPAGES_H__ */ From b1eab6a696dc926adc40358800fbae63e9078c9c Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 29 Nov 2019 09:58:18 +0800 Subject: [PATCH 389/469] update build script --- build.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.ps1 b/build.ps1 index b91c0500d..3eba4ed64 100644 --- a/build.ps1 +++ b/build.ps1 @@ -33,7 +33,7 @@ $OBJ_PATH = [System.IO.Path]::GetFullPath("$PSScriptRoot\obj") $SKETCH_PATH = [System.IO.Path]::GetFullPath("$PSScriptRoot\sketch") $buildOutput = & { - arduino compile ` + arduino-cli compile ` --warnings default ` --build-path $BIN_PATH ` --build-cache-path $OBJ_PATH ` @@ -84,6 +84,6 @@ if ($ShowStats) { if ($Upload) { Write-Host "==============> Uploading..." -ForegroundColor Yellow - arduino upload -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ -pCOM4 + arduino-cli upload -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ -pCOM4 Write-Host "==============> Finished." -ForegroundColor Green } From 1f41f7e02fe1eb867a7844a0c2bcaa189c19d0a2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 29 Nov 2019 13:04:29 +1100 Subject: [PATCH 390/469] Set track length to equal MD pattern length on merge --- avr/cores/megacommand/MCL/MDTrack.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index 30f45fb36..9ad292918 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -341,6 +341,8 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s if (merge) { DEBUG_PRINTLN("auto merge"); + //Set track length to equal MD pattern length on merge + seq_data.length = length; MDSeqTrack md_seq_track; memcpy(&(md_seq_track), &(this->seq_data), sizeof(MDSeqTrackData)); md_seq_track.merge_from_md(this); From 8adeb43c37965c78c5a89acffe1d12e20a533382 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 29 Nov 2019 13:59:47 +0800 Subject: [PATCH 391/469] fix new project crash on windows build. --- avr/cores/megacommand/MCL/Grid.cpp | 5 ++--- avr/cores/megacommand/MCL/GridRowHeader.h | 2 +- upload.ps1 | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/Grid.cpp b/avr/cores/megacommand/MCL/Grid.cpp index 9323a3168..ad8ee515e 100644 --- a/avr/cores/megacommand/MCL/Grid.cpp +++ b/avr/cores/megacommand/MCL/Grid.cpp @@ -191,12 +191,11 @@ bool Grid::clear_slot(int16_t column, int16_t row, bool update_header) { return true; } -bool Grid::clear_row(int16_t row) { - bool update_header = false; +__attribute__((noinline)) bool Grid::clear_row(int16_t row) { GridRowHeader row_header; row_header.init(); for (int x = 0; x < GRID_WIDTH; x++) { - clear_slot(x, row, update_header); + clear_slot(x, row, false); } return row_header.write(row); } diff --git a/avr/cores/megacommand/MCL/GridRowHeader.h b/avr/cores/megacommand/MCL/GridRowHeader.h index a090ab218..0d55b90a6 100644 --- a/avr/cores/megacommand/MCL/GridRowHeader.h +++ b/avr/cores/megacommand/MCL/GridRowHeader.h @@ -3,7 +3,7 @@ #ifndef GRIDROW_H__ #define GRIDROW_H__ -#define GRID_WIDTH 22 +#define GRID_WIDTH 20 class GridRowHeader { public: diff --git a/upload.ps1 b/upload.ps1 index c55dfc6fa..75af9164c 100644 --- a/upload.ps1 +++ b/upload.ps1 @@ -1 +1 @@ -arduino upload -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ -pCOM4 +arduino-cli upload -b MIDICtrl20_MegaCommand:avr:mega .\sketch\ -pCOM4 From b88e0ed4e61491721bf368673e5b92c2bf940b56 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 29 Nov 2019 14:06:51 +0800 Subject: [PATCH 392/469] revert GridRowHeader GRID_WIDTH to 22 --- avr/cores/megacommand/MCL/GridRowHeader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/GridRowHeader.h b/avr/cores/megacommand/MCL/GridRowHeader.h index 0d55b90a6..a090ab218 100644 --- a/avr/cores/megacommand/MCL/GridRowHeader.h +++ b/avr/cores/megacommand/MCL/GridRowHeader.h @@ -3,7 +3,7 @@ #ifndef GRIDROW_H__ #define GRIDROW_H__ -#define GRID_WIDTH 20 +#define GRID_WIDTH 22 class GridRowHeader { public: From 73e87cda6f9f5e446b6a9e1323b1665b6b8fc64c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 29 Nov 2019 17:38:00 +1100 Subject: [PATCH 393/469] fix memory layout typo in compiling/linking options --- avr/cores/megacommand/Makefile | 2 +- avr/cores/megacommand/minicommand.mk | 2 +- avr/platform.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/Makefile b/avr/cores/megacommand/Makefile index 675649702..be5d2f875 100644 --- a/avr/cores/megacommand/Makefile +++ b/avr/cores/megacommand/Makefile @@ -21,7 +21,7 @@ OPTIM=-Os CFLAGS = -c -g -Os -Wextra -std=gnu11 -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects -Wl,--gc-sections,--defsym=__stack=0x8021ff,--section-start,.data=0x802200,--defsym=__heap_end=0x80ffff -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -CXXFLAGS = -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -Wl,--gc-sections,--defsym=__stack=0x8021ff,--section-start,.data=0x82200,--defsym=__heap_end=0x80ffff -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR +CXXFLAGS = -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -Wl,--gc-sections,--defsym=__stack=0x8021ff,--section-start,.data=0x802200,--defsym=__heap_end=0x80ffff -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR CLDFLAGS = -Wall -Wextra -Os -flto -fuse-linker-plugin -Wl,--gc-sections,--defsym=__stack=0x8021ff,--section-start,.data=0x802200,--defsym=__heap_end=0x80ffff,--relax -mmcu=atmega2560 DIRS = Sequencer SDCard Elektron A4 Wire Wire/utility Midi MidiTools Adafruit-GFX-Library Adafruit-GFX-Library/Fonts SdFat SdFat/SdCard SdFat/FatLib SdFat/SpiDriver SdFat/SpiDriver/boards MCL SPI Adafruit_SSD1305_Library CommonTools GUI MD MNM diff --git a/avr/cores/megacommand/minicommand.mk b/avr/cores/megacommand/minicommand.mk index c177f8679..f89521349 100644 --- a/avr/cores/megacommand/minicommand.mk +++ b/avr/cores/megacommand/minicommand.mk @@ -30,7 +30,7 @@ OPTIM=-Os #CLDFLAGS = $(OPTIM) -Wl,---gc-sections -mmcu=atmega64 -o /Users/manuel/mididuino/minicommand-sketches/profileTest/applet/profileTest.elf -Wl,--section-start=.bss=0x802000,--defsym=__heap_end=0x80ffff CFLAGS = -c -g -Os -Wextra -std=gnu11 -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects -Wl,--gc-sections,--defsym=__stack=0x8010ff,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff -mmcu=atmega64 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA64 -DARDUINO_ARCH_AVR -CXXFLAGS = -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -Wl,--gc-sections,--defsym=__stack=0x8010ff,--section-start,.data=0x81100,--defsym=__heap_end=0x80ffff -mmcu=atmega64 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA64 -DARDUINO_ARCH_AVR +CXXFLAGS = -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -Wl,--gc-sections,--defsym=__stack=0x8010ff,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff -mmcu=atmega64 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA64 -DARDUINO_ARCH_AVR CLDFLAGS = -Wall -Wextra -Os -flto -fuse-linker-plugin -Wl,--gc-sections,--defsym=__stack=0x8010ff,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff,--relax -mmcu=atmega64 diff --git a/avr/platform.txt b/avr/platform.txt index 31f13212e..c96cc40d6 100644 --- a/avr/platform.txt +++ b/avr/platform.txt @@ -27,7 +27,7 @@ compiler.c.elf.cmd=avr-gcc compiler.S.flags=-c -g -x assembler-with-cpp -flto compiler.cpp.cmd=avr-g++ -compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -Wl,--gc-section,--defsym=__stack=0x8021ff,--section-start,.data=0x82100,--defsym=__heap_end=0x80ffff -I{build.core.path}/Midi -I{build.core.path}/GUI -I{build.core.path}/CommonTools -I{build.core.path}/Elektron -I{build.core.path}/MD -I{build.core.path}/MidiTools -I{build.core.path}/Sequencer -I{build.core.path}/MNM -I{build.core.path}/PageTools -I{build.core.path}/MDSketches -I{build.core.path}/MDPages -I{build.core.path}/A4 -I{build.core.path}/SPI -I{build.core.path}/Wire -I{build.core.path}/Adafruit-GFX-Library -I{build.core.path}/Adafruit_SSD1305_Library -I{build.core.path}/SdFat -I{build.core.path}/SDCard -I{build.core.path}/MCL +compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -Wl,--gc-section,--defsym=__stack=0x8021ff,--section-start,.data=0x802100,--defsym=__heap_end=0x80ffff -I{build.core.path}/Midi -I{build.core.path}/GUI -I{build.core.path}/CommonTools -I{build.core.path}/Elektron -I{build.core.path}/MD -I{build.core.path}/MidiTools -I{build.core.path}/Sequencer -I{build.core.path}/MNM -I{build.core.path}/PageTools -I{build.core.path}/MDSketches -I{build.core.path}/MDPages -I{build.core.path}/A4 -I{build.core.path}/SPI -I{build.core.path}/Wire -I{build.core.path}/Adafruit-GFX-Library -I{build.core.path}/Adafruit_SSD1305_Library -I{build.core.path}/SdFat -I{build.core.path}/SDCard -I{build.core.path}/MCL compiler.ar.cmd=avr-gcc-ar compiler.ar.flags=rcs From 017a0d4425d3d9eabfb3c597ec4a32dfc69194e3 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 29 Nov 2019 22:09:01 +0800 Subject: [PATCH 394/469] fix #83 --- avr/cores/megacommand/MCL/RoutePage.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index 87dda5ed7..c6221b4a9 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -241,13 +241,13 @@ bool RoutePage::handleEvent(gui_event_t *event) { } return true; } - if (EVENT_PRESSED(event, Buttons.BUTTON1)) { - update_globals(); - md_exploit.off(); - md_exploit.on(); - GUI.setPage(&mixer_page); - return true; - } + //if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + //update_globals(); + //md_exploit.off(); + //md_exploit.on(); + //GUI.setPage(&mixer_page); + //return true; + //} if (EVENT_PRESSED(event, Buttons.BUTTON2)) { update_globals(); md_exploit.off(); From 78d9d076b521de7307c31930f82dd2075924f755 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 30 Nov 2019 23:11:30 +1100 Subject: [PATCH 395/469] Fix PageSelectPage for HD44780 display --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index a9be6c194..df0ae5ed1 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -133,6 +133,7 @@ void PageSelectPage::init() { oled_display.setCursor(label_pos[i], 31); oled_display.print(str); } + classic_display = false; #endif bool switch_tracks = false; if (!md_exploit.state) { @@ -142,7 +143,7 @@ void PageSelectPage::init() { md_prepare(); md_exploit.on(switch_tracks); note_interface.state = true; - classic_display = false; + } void PageSelectPage::md_prepare() { From 71a13b02144170d35f286b3233c7cd1fd672bd1f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 30 Nov 2019 23:25:25 +1100 Subject: [PATCH 396/469] Fix FX pages for HD44780 --- avr/cores/megacommand/MCL/FXPage.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 50137ba83..5ea350747 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -96,6 +96,8 @@ void FXPage::loop() { } void FXPage::display() { + char str[4]; + PGM_P param_name = NULL; if (!classic_display) { #ifdef OLED_DISPLAY oled_display.clearDisplay(); @@ -109,19 +111,27 @@ void FXPage::display() { GUI.put_string_at(0, "FX"); GUI.put_value_at1(4, page_mode ? 1 : 0); GUI.setLine(GUI.LINE2); - /* - if (mcl_cfg.ram_page_mode == 0) { - GUI.put_string_at(0, "MON"); - } else { - GUI.put_string_at(0, "LNK"); - } - */ + + for (uint8_t i = 0; i < GUI_NUM_ENCODERS; i++) { + uint8_t n = i + ((page_mode ? 1 : 0) * GUI_NUM_ENCODERS); + + uint8_t fx_param = params[n].param; + uint8_t fx_type = params[n].type; + GUI.setLine(GUI.LINE1); + param_name = fx_param_name(fx_type, fx_param); + m_strncpy_p(str, param_name, 4); + + GUI.put_string_at(i * 4, str); + + GUI.setLine(GUI.LINE2); + GUI.put_value_at(i * 4, encoders[i]->cur); + // mcl_gui.draw_light_encoder(30 + 20 * i, 18, encoders[i], str); + } + #endif #ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); - PGM_P param_name = NULL; - char str[4]; if (page_id == 0) { oled_display.drawBitmap(0, 0, icon_rhytmecho, 24, 18, WHITE); From 6cd496fc63ff52760bd0520676e41ee70c177c00 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 30 Nov 2019 20:54:11 +0800 Subject: [PATCH 397/469] fix #90 --- avr/cores/megacommand/MCL/TextInputPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/TextInputPage.cpp b/avr/cores/megacommand/MCL/TextInputPage.cpp index 8e4b3f59c..9ed5c06eb 100644 --- a/avr/cores/megacommand/MCL/TextInputPage.cpp +++ b/avr/cores/megacommand/MCL/TextInputPage.cpp @@ -225,7 +225,7 @@ bool TextInputPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { return true; } - #ifdef OLED_DIPLAY + #ifdef OLED_DISPLAY // in char-pane mode, do not handle any events // except shift-release event. if (!normal_mode) { From f532cd31e991c2ab844269325385fc45e525dc23 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 00:14:44 +1100 Subject: [PATCH 398/469] Show Step Menu on HD44780 --- avr/cores/megacommand/MCL/SeqPage.cpp | 50 ++++++++++++++++++++++---- avr/cores/megacommand/MCL/SeqPage.h | 3 ++ avr/cores/megacommand/MCL/SeqPages.cpp | 4 +-- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index b3b29b6ea..f5081065e 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -203,8 +203,8 @@ bool SeqPage::handleEvent(gui_event_t *event) { return true; } } + if (EVENT_RELEASED(event, Buttons.BUTTON3)) { -#ifdef OLED_DISPLAY encoders[0] = opt_param1_capture; encoders[1] = opt_param2_capture; oled_display.clearDisplay(); @@ -234,17 +234,42 @@ bool SeqPage::handleEvent(gui_event_t *event) { show_step_menu = false; mcl_gui.init_encoders_used_clock(); return true; -#endif } #else - if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + if (EVENT_PRESSED(event, Buttons.BUTTON3) && !BUTTON_DOWN(Buttons.BUTTON4)) { + // If MD trig is held and BUTTON3 is pressed, launch note menu + if ((note_interface.notes_count_on() != 0) && + (GUI.currentPage() != &seq_ptc_page)) { + uint8_t note = 255; + for (uint8_t n = 0; n < NUM_MD_TRACKS && note == 255; n++) { + if (note_interface.notes[n] == 1) { + note = n; + } + } + if (note == 255) { + return false; + } + step_select = note; + show_step_menu = true; + GUI.pushPage(&step_menu_page); + } else { + if (opt_midi_device_capture == DEVICE_MD) { + DEBUG_PRINTLN("okay using MD for length update"); + opt_trackid = last_md_track + 1; + opt_resolution = (mcl_seq.md_tracks[last_md_track].resolution); + } else { + opt_trackid = last_ext_track + 1; + opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); + } // capture current midi_device value opt_midi_device_capture = midi_device; // capture current page. opt_seqpage_capture = this; GUI.pushPage(&seq_menu_page); + show_seq_menu = true; return true; } +} #endif // legacy enc push page switching code @@ -740,7 +765,7 @@ void opt_shift_track_handler() { } } #ifdef EXT_TRACKS - else { + else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { mcl_seq.ext_tracks[n].rotate_left(); } @@ -753,8 +778,8 @@ void opt_shift_track_handler() { mcl_seq.md_tracks[n].rotate_right(); } } -#ifdef EXT_TRACKS -else { +#ifdef EXT_TRACKS + else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { mcl_seq.ext_tracks[n].rotate_right(); } @@ -793,6 +818,17 @@ void opt_reverse_track_handler() { } } +void seq_menu_handler() { +#ifndef OLED_DISPLAY +SeqPage::show_seq_menu = false; +#endif +} +void step_menu_handler() { +#ifndef OLED_DISPLAY +SeqPage::show_step_menu = false; +#endif +} + void SeqPage::config_as_trackedit() { seq_menu_page.menu.enable_entry(2, true); @@ -835,7 +871,7 @@ void SeqPage::draw_page_index(bool show_page_index, uint8_t _playing_idx) { 4; } #ifdef EXT_TRACKS - else { + else { playing_idx = (mcl_seq.ext_tracks[last_ext_track].step_count - ((mcl_seq.ext_tracks[last_ext_track].step_count) / 16) * 16) / diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index f3776acc6..017a6403d 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -39,6 +39,9 @@ extern void opt_copy_step_handler(); extern void opt_mute_step_handler(); extern void opt_clear_step_locks_handler(); +extern void seq_menu_handler(); +extern void step_menu_handler(); + class SeqPage : public LightPage { public: // Static variables shared amongst derived objects diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index ca54e0573..96b9b101b 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -38,7 +38,7 @@ const menu_t<8> seq_menu_layout PROGMEM = { {"SHIFT:", 0, 5, 5, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "--",}, {1, "L"}, {2, "R"}, {3,"L>ALL"}, {4, "R>ALL"}}}, {"REVERSE:", 0, 3, 3, (uint8_t *)&opt_reverse, (Page *)NULL, opt_reverse_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"} }}, }, - NULL, + seq_menu_handler, }; MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); @@ -53,7 +53,7 @@ const menu_t<4> step_menu_layout PROGMEM = { {"PASTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_paste_step_handler, {}}, {"MUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_mute_step_handler, {}}, }, - NULL, + step_menu_handler, }; MCLEncoder step_menu_value_encoder(0, 16, ENCODER_RES_PAT); From e3670363c6da39bb218b27dd427b5e9a05bca2f3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 00:59:31 +1100 Subject: [PATCH 399/469] LFO Page support for HD44780 --- avr/cores/megacommand/MCL/LFOPage.cpp | 85 ++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index f25458c96..0181aa7d9 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -109,8 +109,8 @@ void LFOPage::loop() { SET_LOCK(); lfo_track->params[0].reset_param_offset(); lfo_track->params[0].param = encoders[1]->cur; - // lfo_track->params[0].offset = lfo_track->params[0].get_param_offset( - // encoders[0]->cur, encoders[1]->cur); + // lfo_track->params[0].offset = lfo_track->params[0].get_param_offset( + // encoders[0]->cur, encoders[1]->cur); lfo_track->params[0].update_offset(); CLEAR_LOCK(); } @@ -133,8 +133,8 @@ void LFOPage::loop() { SET_LOCK(); lfo_track->params[1].reset_param_offset(); lfo_track->params[1].param = encoders[3]->cur; - // lfo_track->params[1].offset = lfo_track->params[1].get_param_offset( - // encoders[2]->cur, encoders[3]->cur); + // lfo_track->params[1].offset = lfo_track->params[1].get_param_offset( + // encoders[2]->cur, encoders[3]->cur); lfo_track->params[1].update_offset(); CLEAR_LOCK(); } @@ -183,7 +183,15 @@ void LFOPage::draw_param(uint8_t knob, uint8_t dest, uint8_t param) { m_strncpy_p(myName, modelname, 4); } } +#ifdef OLED_DISPLAY draw_knob(knob, "PAR", myName); +#else + GUI.setLine(GUI.LINE1); + + GUI.put_string_at(knob * 4, myName); + ; + +#endif } void LFOPage::draw_dest(uint8_t knob, uint8_t value) { @@ -209,7 +217,12 @@ void LFOPage::draw_dest(uint8_t knob, uint8_t value) { itoa(value, K, 10); break; } +#ifdef OLED_DISPLAY draw_knob(knob, "DEST", K); +#else + GUI.setLine(GUI.LINE1); + GUI.put_string_at(knob * 4, K); +#endif } void LFOPage::display() { @@ -233,7 +246,63 @@ void LFOPage::display() { GUI.put_string_at(0, "LNK"); } */ - + if (page_mode == LFO_DESTINATION) { + draw_dest(0, encoders[0]->cur); + draw_param(1, encoders[0]->cur, encoders[1]->cur); + draw_dest(2, encoders[2]->cur); + draw_param(3, encoders[2]->cur, encoders[3]->cur); + } + if (page_mode == LFO_SETTINGS) { + char K[4]; + switch (lfo_track->wav_type) { + case SIN_WAV: + strcpy(K, "SIN"); + break; + case TRI_WAV: + strcpy(K, "TRI"); + break; + case IRAMP_WAV: + strcpy(K, "IR"); + break; + case RAMP_WAV: + strcpy(K, "R"); + break; + case EXP_WAV: + strcpy(K, "EX"); + break; + case IEXP_WAV: + strcpy(K, "IEX"); + break; + } + GUI.setLine(GUI.LINE1); + GUI.put_string_at(0, K); + GUI.put_value_at(4, encoders[1]->cur); + GUI.put_value_at(8, encoders[2]->cur); + GUI.put_value_at(12, encoders[3]->cur); + GUI.setLine(GUI.LINE2); + if (lfo_track->enable) { + switch (lfo_track->mode) { + case LFO_MODE_FREE: + GUI.put_string_at(0, "FREE"); + break; + case LFO_MODE_TRIG: + case LFO_MODE_ONE: + char str[17] = "----------------"; + + for (int i = 0; i < 16; i++) { + if (IS_BIT_SET64(lfo_track->pattern_mask, i)) { + + str[i] = (char)219; + } + } + GUI.put_string_at(0, str); + break; + } + } + else { + GUI.put_string_at(0, "LFO: OFF"); + } +} #endif #ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); @@ -380,12 +449,12 @@ bool LFOPage::handleEvent(gui_event_t *event) { GUI.setPage(&grid_page); } */ - if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + if (EVENT_PRESSED(event, Buttons.BUTTON4)) { page_mode = !(page_mode); update_encoders(); } - if (EVENT_PRESSED(event, Buttons.BUTTON1)) { + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { if (lfo_track->mode >= LFO_MODE_ONE) { lfo_track->mode = 0; } else { @@ -398,7 +467,7 @@ bool LFOPage::handleEvent(gui_event_t *event) { } } - if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + if (EVENT_PRESSED(event, Buttons.BUTTON1)) { lfo_track->enable = !(lfo_track->enable); if (!lfo_track->enable) { lfo_track->reset_params_offset(); From be672fbf19aadaa93c4789c229490fc003d95b20 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 01:01:10 +1100 Subject: [PATCH 400/469] Remove disclamer, as HD44780 is supported in 2.50 --- Changelog | 2 -- 1 file changed, 2 deletions(-) diff --git a/Changelog b/Changelog index 8501daa74..0c200a682 100644 --- a/Changelog +++ b/Changelog @@ -1,7 +1,5 @@ MCL 2.50 01/12/2019 -Note: HD44780 16x2 LCD display is not supported in this release. - Complete GUI overhaul: Button assignment has been improved to provide consistency across all pages. From 65b896ac6558116739bc6a495354dda082945e73 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 01:11:41 +1100 Subject: [PATCH 401/469] Fix bug where note interface would not be enabled when md_exploit on --- avr/cores/megacommand/MCL/LFOPage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index 0181aa7d9..dbbaa5084 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -298,11 +298,10 @@ void LFOPage::display() { GUI.put_string_at(0, str); break; } + } else { + GUI.put_string_at(0, "LFO: OFF"); } - else { - GUI.put_string_at(0, "LFO: OFF"); - } -} + } #endif #ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); @@ -463,6 +462,7 @@ bool LFOPage::handleEvent(gui_event_t *event) { if (lfo_track->mode == LFO_MODE_FREE) { md_exploit.off(); } else { + note_interface.state = true; md_exploit.on(); } } From 55864dbee0aa25e98168f7144690c39291ac2300 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 01:42:23 +1100 Subject: [PATCH 402/469] Fix mixer page for HD44780 --- avr/cores/megacommand/MCL/MixerPage.cpp | 55 ++++++++++++++++++------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index a9f471940..c2b4162bf 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -1,5 +1,5 @@ -#include "MixerPage.h" #include "MCL.h" +#include "MixerPage.h" #define FADER_LEN 16 #define FADE_RATE 16 @@ -118,7 +118,40 @@ void MixerPage::set_level(int curtrack, int value) { void MixerPage::loop() {} -void MixerPage::draw_levels() {} +void MixerPage::draw_levels() { + GUI.setLine(GUI.LINE2); + uint8_t scaled_level; + uint8_t scaled_level2; + char str[17] = " "; + uint8_t fader_level; + for (int i = 0; i < 16; i++) { + + if (display_mode == MODEL_LEVEL) { + fader_level = MD.kit.levels[i]; + } else { + fader_level = MD.kit.params[i][display_mode]; + } + // if (note_interface.notes[i] == 1) { + // scaled_level = (int)(disp_levels[i] / 127.0f) * 7; + // } else { + scaled_level = (int)(((float)fader_level / (float)127) * 7); + // } + if (scaled_level == 7) { + str[i] = (char)(255); + } else if (scaled_level > 0) { + str[i] = (char)(scaled_level + 2); + } + } + GUI.put_string_at(0, str); + uint8_t dec = MidiClock.get_tempo() / FADE_RATE; + for (uint8_t n = 0; n < 16; n++) { + if (disp_levels[n] < dec) { + disp_levels[n] = 0; + } else { + disp_levels[n] -= dec; + } + } +} void encoder_level_handle(Encoder *enc) { @@ -257,15 +290,7 @@ void MixerPage::display() { } } GUI.put_string_at(0, str); - - uint8_t dec = MidiClock.get_tempo() / FADE_RATE; - for (uint8_t n = 0; n < 16; n++) { - if (disp_levels[n] < dec) { - disp_levels[n] = 0; - } else { - disp_levels[n] -= dec; - } - } + draw_levels(); } #else @@ -414,10 +439,10 @@ bool MixerPage::handleEvent(gui_event_t *event) { EVENT_PRESSED(event, Buttons.ENCODER2) || EVENT_PRESSED(event, Buttons.ENCODER3) || EVENT_PRESSED(event, Buttons.ENCODER4)) { -// if (note_interface.notes_count() == 0) { -// route_page.update_globals(); -// GUI.setPage(&grid_page); -// } + // if (note_interface.notes_count() == 0) { + // route_page.update_globals(); + // GUI.setPage(&grid_page); + // } return true; } From 5d292b18fa32f6e68a34b65782b8e03fc9e9190b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 01:54:10 +1100 Subject: [PATCH 403/469] Fix question page and SDDrivePage for HD44780 --- avr/cores/megacommand/MCL/QuestionDialogPage.cpp | 10 ++++++++++ avr/cores/megacommand/MCL/SDDrivePage.cpp | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp index 94315a7b0..c40dfef42 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -26,6 +26,16 @@ void QuestionDialogPage::init(const char* title, const char* text) { oled_display.setFont(oldfont); oled_display.display(); #else + GUI.clearLines(); + GUI.setLine(GUI.LINE1); + uint8_t x; + + GUI.put_string_at(13, "YES"); + GUI.put_string_at(0, "NO"); + + GUI.setLine(GUI.LINE2); + GUI.put_string_at(0, text); + GUI.display(); // TODO #endif } diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 106f2d359..82d1947da 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -74,7 +74,9 @@ void SDDrivePage::save_snapshot() { return; } } - + #ifdef OLED_DISPLAY + mcl_gfx.alert("Please Wait", "Saving Snap"); + #endif DEBUG_PRINTLN("creating new snapshot:"); DEBUG_PRINTLN(temp_entry); if (!file.open(temp_entry, O_WRITE | O_CREAT)) { @@ -173,6 +175,9 @@ void SDDrivePage::load_snapshot() { gfx.alert("Error", "Cannot open file for read"); return; } + #ifdef OLED_DISPLAY + mcl_gfx.alert("Please Wait", "Restoring Snap"); + #endif MidiUart.sendRaw(MIDI_STOP); MidiClock.handleImmediateMidiStop(); From e667da9f8f2d9a9e72f05e021b66c4a8fae79a94 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 11:54:40 +1100 Subject: [PATCH 404/469] Fix question/dialog for HD44780 --- avr/cores/megacommand/GUI/GUI.cpp | 20 +++++++++++++------ avr/cores/megacommand/GUI/GUI.h | 4 ++++ avr/cores/megacommand/MCL/MCLGfx.cpp | 2 +- .../megacommand/MCL/QuestionDialogPage.cpp | 18 +++++++++++------ .../megacommand/MCL/QuestionDialogPage.h | 7 ++++--- 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/avr/cores/megacommand/GUI/GUI.cpp b/avr/cores/megacommand/GUI/GUI.cpp index 02ed504a2..8eadca546 100644 --- a/avr/cores/megacommand/GUI/GUI.cpp +++ b/avr/cores/megacommand/GUI/GUI.cpp @@ -132,15 +132,10 @@ void GuiClass::loop() { } } } - -void GuiClass::display() { +void GuiClass::display_lcd() { PageParent *page = NULL; if (sketch != NULL) { page = sketch->currentPage(); - if (page != NULL) { - page->display(); - page->redisplay = false; - } } for (uint8_t i = 0; i < 2; i++) { @@ -181,6 +176,19 @@ void GuiClass::display() { GUI.setLine(GUI.LINE2); GUI.clearFlashLine(); + +} + +void GuiClass::display() { + PageParent *page = NULL; + if (sketch != NULL) { + page = sketch->currentPage(); + if (page != NULL) { + page->display(); + page->redisplay = false; + } + } + display_lcd(); #ifdef UART_USB #ifdef OLED_DISPLAY #ifndef DEBUGMODE diff --git a/avr/cores/megacommand/GUI/GUI.h b/avr/cores/megacommand/GUI/GUI.h index 498ec32a7..5475761b8 100644 --- a/avr/cores/megacommand/GUI/GUI.h +++ b/avr/cores/megacommand/GUI/GUI.h @@ -240,6 +240,10 @@ class GuiClass { * It then handles the displaying of "flash" messages, by checking * how long the active flash message has been running. **/ + + /** update hd44780 **/ + void display_lcd(); + void display(); /** diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index c05ed06b5..c68303b4c 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -101,7 +101,7 @@ void MCLGfx::alert(const char *str1, const char *str2) { oled_display.clearDisplay(); #else GUI.flash_strings_fill(str1, str2); - GUI.display(); + GUI.display_lcd(); #endif DEBUG_PRINTLN(str1); DEBUG_PRINTLN(str2); diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp index c40dfef42..fa78b34bd 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.cpp +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.cpp @@ -1,9 +1,9 @@ #include "MCL.h" #include "QuestionDialogPage.h" -void QuestionDialogPage::init(const char* title, const char* text) { +void QuestionDialogPage::init(const char* title_, const char* text_) { #ifdef OLED_DISPLAY - mcl_gui.draw_infobox(title, text, -1); + mcl_gui.draw_infobox(title_, text_, -1); oled_display.drawFastHLine(MCLGUI::dlg_info_x1 + 1, MCLGUI::dlg_info_y2, MCLGUI::dlg_info_w - 2, BLACK); auto oldfont = oled_display.getFont(); @@ -26,17 +26,23 @@ void QuestionDialogPage::init(const char* title, const char* text) { oled_display.setFont(oldfont); oled_display.display(); #else + title = title_; + text = text_; +#endif +} + +void QuestionDialogPage::display() { +#ifndef OLED_DISPLAY GUI.clearLines(); GUI.setLine(GUI.LINE1); uint8_t x; - GUI.put_string_at(13, "YES"); - GUI.put_string_at(0, "NO"); + GUI.put_string_at(0, "[NO]"); + GUI.put_string_at(11, "[YES]"); GUI.setLine(GUI.LINE2); GUI.put_string_at(0, text); - GUI.display(); - // TODO + GUI.display_lcd(); #endif } diff --git a/avr/cores/megacommand/MCL/QuestionDialogPage.h b/avr/cores/megacommand/MCL/QuestionDialogPage.h index 9629f49c7..50fd65383 100644 --- a/avr/cores/megacommand/MCL/QuestionDialogPage.h +++ b/avr/cores/megacommand/MCL/QuestionDialogPage.h @@ -8,12 +8,13 @@ class QuestionDialogPage : public LightPage { public: - + char *title; + char *text; QuestionDialogPage() : LightPage() { } bool handleEvent(gui_event_t *event); - void init(const char* title, const char* text); - void display() {} + void init(const char* title_, const char* text_); + void display(); void setup() {} bool return_state = false; From 4603251f50c5b30caf53cf9cb7ff3e26b82bde98 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 13:11:27 +1100 Subject: [PATCH 405/469] FIx filemenu for HD44780 --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 24 ++++++++++++++----- avr/cores/megacommand/MCL/FileBrowserPage.h | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 8e7d0421d..0eccce002 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -24,8 +24,9 @@ void FileBrowserPage::add_entry(const char *entry) { void FileBrowserPage::init() { DEBUG_PRINT_FN(); - char temp_entry[16]; + char temp_entry[16]; + call_handle_filemenu = false; // config menu file_menu_page.visible_rows = 3; file_menu_page.menu.enable_entry(0, show_new_folder); @@ -52,7 +53,6 @@ void FileBrowserPage::init() { if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { add_entry(".."); } - encoders[1]->cur = 1; // iterate through the files @@ -179,6 +179,12 @@ void FileBrowserPage::draw_scrollbar(uint8_t x_offset) { } void FileBrowserPage::loop() { +#ifndef OLED_DISPLAY + if (call_handle_filemenu) { + call_handle_filemenu = false; + _handle_filemenu(); + } +#endif if (filemenu_active) { file_menu_page.loop(); @@ -278,10 +284,11 @@ void FileBrowserPage::_handle_filemenu() { volatile uint8_t *ptr = (uint8_t *)BANK1_FILE_ENTRIES_START + encoders[1]->getValue() * 16; memcpy_bank1(&buf1[0], ptr, sizeof(buf1)); + char *suffix_pos = strchr(buf1, '.'); char buf2[32] = {'\0'}; for (uint8_t n = 1; n < 32; n++) { - buf2[n] = ' '; + buf2[n] = ' '; } uint8_t name_length = 8; @@ -362,7 +369,7 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { if (note_interface.is_event(event)) { return false; } - +#ifdef OLED_DISPLAY if (EVENT_PRESSED(event, Buttons.BUTTON3)) { filemenu_active = true; file_menu_encoder.cur = file_menu_encoder.old = 0; @@ -372,15 +379,20 @@ bool FileBrowserPage::handleEvent(gui_event_t *event) { file_menu_page.init(); return false; } - if (EVENT_RELEASED(event, Buttons.BUTTON3)) { encoders[0] = param1; encoders[1] = param2; + _handle_filemenu(); init(); return false; } - +#else + if (EVENT_PRESSED(event, Buttons.BUTTON3)) { + call_handle_filemenu = true; + GUI.pushPage(&file_menu_page); + } +#endif if (EVENT_PRESSED(event, Buttons.BUTTON4)) { int i_save; diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.h b/avr/cores/megacommand/MCL/FileBrowserPage.h index e668bad81..e0930369d 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.h +++ b/avr/cores/megacommand/MCL/FileBrowserPage.h @@ -45,6 +45,8 @@ class FileBrowserPage : public LightPage { bool filemenu_active = false; + bool call_handle_filemenu = false; + Encoder* param1; Encoder* param2; From d6957ba0b2929b2449b422f88ed9b7ee9e393a9c Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 13:11:57 +1100 Subject: [PATCH 406/469] Remove callbacks for file and step menus --- avr/cores/megacommand/MCL/SeqPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index f5081065e..66fdcb84f 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -820,12 +820,12 @@ void opt_reverse_track_handler() { void seq_menu_handler() { #ifndef OLED_DISPLAY -SeqPage::show_seq_menu = false; +//SeqPage::show_seq_menu = false; #endif } void step_menu_handler() { #ifndef OLED_DISPLAY -SeqPage::show_step_menu = false; +//SeqPage::show_step_menu = false; #endif } From 5ffdf2d63b4540e3c36f8b3923bedbbf90b7a9e5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 13:27:00 +1100 Subject: [PATCH 407/469] Support SDDrive on hd44780 --- avr/cores/megacommand/MCL/MCLGfx.cpp | 13 +++++++------ avr/cores/megacommand/MCL/MCLGfx.h | 2 ++ avr/cores/megacommand/MCL/SDDrivePage.cpp | 9 +++++---- avr/cores/megacommand/MCL/SeqPages.cpp | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index c68303b4c..10cc8fb8d 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -81,18 +81,19 @@ void MCLGfx::splashscreen() { char str1[17] = "MEGACOMMAND LIVE"; char str2[17] = VERSION_STR; str1[16] = '\0'; - LCD.goLine(0); - LCD.puts(str1); - LCD.goLine(1); - LCD.puts(str2); - + display_text(&str1[0], &str2[0]); delay(200); #endif // while (rec_global == 0) { // GUI.setPage(&grid_page); } - +void MCLGfx::display_text(const char *str1, const char *str2) { + LCD.goLine(0); + LCD.puts(str1); + LCD.goLine(1); + LCD.puts(str2); +} void MCLGfx::alert(const char *str1, const char *str2) { #ifdef OLED_DISPLAY mcl_gui.draw_infobox(str1, str2); diff --git a/avr/cores/megacommand/MCL/MCLGfx.h b/avr/cores/megacommand/MCL/MCLGfx.h index aad3ca23e..72839ae20 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.h +++ b/avr/cores/megacommand/MCL/MCLGfx.h @@ -8,7 +8,9 @@ class MCLGfx { public: void splashscreen(); void init_oled(); + void display_text(const char *str1, const char *str2); void alert(const char *str1, const char *str2); + }; extern MCLGfx gfx; diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 82d1947da..d2b924f61 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -74,9 +74,10 @@ void SDDrivePage::save_snapshot() { return; } } - #ifdef OLED_DISPLAY - mcl_gfx.alert("Please Wait", "Saving Snap"); + #ifndef OLED_DISPLAY + gfx.display_text("Please Wait", "Saving Snap"); #endif + DEBUG_PRINTLN("creating new snapshot:"); DEBUG_PRINTLN(temp_entry); if (!file.open(temp_entry, O_WRITE | O_CREAT)) { @@ -175,8 +176,8 @@ void SDDrivePage::load_snapshot() { gfx.alert("Error", "Cannot open file for read"); return; } - #ifdef OLED_DISPLAY - mcl_gfx.alert("Please Wait", "Restoring Snap"); + #ifndef OLED_DISPLAY + gfx.display_text("Please Wait", "Restoring Snap"); #endif MidiUart.sendRaw(MIDI_STOP); diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 96b9b101b..ca54e0573 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -38,7 +38,7 @@ const menu_t<8> seq_menu_layout PROGMEM = { {"SHIFT:", 0, 5, 5, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "--",}, {1, "L"}, {2, "R"}, {3,"L>ALL"}, {4, "R>ALL"}}}, {"REVERSE:", 0, 3, 3, (uint8_t *)&opt_reverse, (Page *)NULL, opt_reverse_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"} }}, }, - seq_menu_handler, + NULL, }; MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); @@ -53,7 +53,7 @@ const menu_t<4> step_menu_layout PROGMEM = { {"PASTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_paste_step_handler, {}}, {"MUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_mute_step_handler, {}}, }, - step_menu_handler, + NULL, }; MCLEncoder step_menu_value_encoder(0, 16, ENCODER_RES_PAT); From cb1d3f66b8223c501813c4f47207f4cd81426d13 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 13:47:11 +1100 Subject: [PATCH 408/469] Add delays for HD44780 when sending snap data, as MD can't keep up --- avr/cores/megacommand/MCL/SDDrivePage.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index d2b924f61..63c0b1599 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -197,6 +197,9 @@ void SDDrivePage::load_snapshot() { { ElektronDataToSysexEncoder encoder(&MidiUart); MD.global.toSysex(encoder); + #ifndef OLED_DISPLAY + delay(20); + #endif } } // Patterns @@ -209,6 +212,9 @@ void SDDrivePage::load_snapshot() { } mcl_actions.md_setsysex_recpos(8, i); MD.pattern.toSysex(); + #ifndef OLED_DISPLAY + delay(20); + #endif } // Kits progress_max = 64; @@ -220,6 +226,10 @@ void SDDrivePage::load_snapshot() { } mcl_actions.md_setsysex_recpos(4, i); MD.kit.toSysex(); + #ifndef OLED_DISPLAY + delay(20); + #endif + } // Load complete progress_max = 0; From 3f64b3ef096dec2060af21c841b9681bf6a7d030 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 14:14:02 +1100 Subject: [PATCH 409/469] regression. trig interface would stop working on hd44780 when exiting step/track menu --- avr/cores/megacommand/MCL/SeqPage.cpp | 4 ++-- avr/cores/megacommand/MCL/SeqPages.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 66fdcb84f..f5081065e 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -820,12 +820,12 @@ void opt_reverse_track_handler() { void seq_menu_handler() { #ifndef OLED_DISPLAY -//SeqPage::show_seq_menu = false; +SeqPage::show_seq_menu = false; #endif } void step_menu_handler() { #ifndef OLED_DISPLAY -//SeqPage::show_step_menu = false; +SeqPage::show_step_menu = false; #endif } diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index ca54e0573..96b9b101b 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -38,7 +38,7 @@ const menu_t<8> seq_menu_layout PROGMEM = { {"SHIFT:", 0, 5, 5, (uint8_t *)&opt_shift, (Page *)NULL, opt_shift_track_handler, { {0, "--",}, {1, "L"}, {2, "R"}, {3,"L>ALL"}, {4, "R>ALL"}}}, {"REVERSE:", 0, 3, 3, (uint8_t *)&opt_reverse, (Page *)NULL, opt_reverse_track_handler, { {0, "--",}, {1, "TRK"}, {2, "ALL"} }}, }, - NULL, + seq_menu_handler, }; MCLEncoder seq_menu_value_encoder(0, 16, ENCODER_RES_PAT); @@ -53,7 +53,7 @@ const menu_t<4> step_menu_layout PROGMEM = { {"PASTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_paste_step_handler, {}}, {"MUTE STEP", 0, 0, 0, (uint8_t *) NULL, (Page *)NULL, opt_mute_step_handler, {}}, }, - NULL, + step_menu_handler, }; MCLEncoder step_menu_value_encoder(0, 16, ENCODER_RES_PAT); From 509f103dad9df925078e4e50d9e4a6f918dfe7f3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 14:43:45 +1100 Subject: [PATCH 410/469] remove diagnostics page --- avr/cores/megacommand/MCL/MCLMenus.cpp | 6 +++--- avr/cores/megacommand/MCL/MCLMenus.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLMenus.cpp b/avr/cores/megacommand/MCL/MCLMenus.cpp index 3cdc1f969..f43ad032c 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.cpp +++ b/avr/cores/megacommand/MCL/MCLMenus.cpp @@ -85,12 +85,12 @@ const menu_t<3> chain_menu_layout PROGMEM = { }; -const menu_t<3> mclconfig_menu_layout PROGMEM = { +const menu_t<2> mclconfig_menu_layout PROGMEM = { "SYSTEM", { {"DISPLAY:", 0, 2, 2, (uint8_t *) &mcl_cfg.display_mirror, (Page*) NULL, NULL, {{0, "INT"}, {1, "INT+EXT"}}}, {"SCREENSAVER:", 0, 2, 2, (uint8_t *) &mcl_cfg.screen_saver, (Page*) NULL, NULL, {{0, "OFF"}, {1, "ON"}}}, - {"DIAGNOSTIC:", 0, 0, 0, (uint8_t *) NULL, (Page*) &diag_page, NULL, {}}, + //{"DIAGNOSTIC:", 0, 0, 0, (uint8_t *) NULL, (Page*) &diag_page, NULL, {}}, }, (&mclsys_apply_config), }; @@ -113,7 +113,7 @@ MenuPage<5> midi_config_page(&midiconfig_menu_layout, &config_param1, &config_param3); MenuPage<5> md_config_page(&mdconfig_menu_layout, &config_param1, &config_param4); MenuPage<3> chain_config_page(&chain_menu_layout, &config_param1, &config_param6); -MenuPage<3> mcl_config_page(&mclconfig_menu_layout, &config_param1, +MenuPage<2> mcl_config_page(&mclconfig_menu_layout, &config_param1, &config_param5); MenuPage<1> ram_config_page(&rampage1_menu_layout, &config_param1, &config_param7); diff --git a/avr/cores/megacommand/MCL/MCLMenus.h b/avr/cores/megacommand/MCL/MCLMenus.h index 74483c247..d958c86d6 100644 --- a/avr/cores/megacommand/MCL/MCLMenus.h +++ b/avr/cores/megacommand/MCL/MCLMenus.h @@ -30,7 +30,7 @@ extern void new_proj_handler(); extern MenuPage<7> system_page; extern MenuPage<5> midi_config_page; extern MenuPage<5> md_config_page; -extern MenuPage<3> mcl_config_page; +extern MenuPage<2> mcl_config_page; extern MenuPage<3> chain_config_page; extern MenuPage<1> aux_config_page; extern MenuPage<1> ram_config_page; From 995d0546db316fdea717d2b848f92dff208a59cf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 14:57:35 +1100 Subject: [PATCH 411/469] RoutePage regression, encoder[2] no longer declared --- avr/cores/megacommand/MCL/RoutePage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/RoutePage.cpp b/avr/cores/megacommand/MCL/RoutePage.cpp index c6221b4a9..53be17f41 100644 --- a/avr/cores/megacommand/MCL/RoutePage.cpp +++ b/avr/cores/megacommand/MCL/RoutePage.cpp @@ -95,7 +95,7 @@ void RoutePage::toggle_routes_batch(bool solo) { quantize_mute = 1 << encoders[1]->getValue(); uint8_t i; hasChanged = true; - if ((encoders[2]->getValue() < 7) && (encoders[2]->getValue() > 0)) { + if ((encoders[1]->getValue() < 7) && (encoders[1]->getValue() > 0)) { while (((((MidiClock.div32th_counter - mcl_actions.start_clock32th) + 3) % (quantize_mute * 2)) != 0) && (MidiClock.state == 2)) { From 28867282129fdd8641e06b8ea47735556b0ab61a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 1 Dec 2019 15:45:29 +1100 Subject: [PATCH 412/469] Don't highlight led when selecting patternmask steps --- avr/cores/megacommand/MCL/MCLGUI.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 810f6ae5f..88344aadb 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -631,10 +631,10 @@ void MCLGUI::draw_leds(uint8_t x, uint8_t y, uint8_t offset, uint64_t lock_mask, show_current_step && step_count == idx && MidiClock.state == 2; bool locked = in_range && IS_BIT_SET64(lock_mask, i + offset); - if (note_interface.notes[i] == 1) { +// if (note_interface.notes[i] == 1) { // TI feedback - oled_display.fillRect(x, y, seq_w, led_h, WHITE); - } else if (!in_range) { + // oled_display.drawRect(x, y, seq_w, led_h, WHITE); + if (!in_range) { // don't draw } else if (current ^ locked) { // highlight From 0722986d88e3ef7c609540cda9de141e0b49af88 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 3 Dec 2019 10:35:22 +1100 Subject: [PATCH 413/469] Add Evil Knievel --- avr/cores/megacommand/MCL/MCLGfx.cpp | 49 +++++++++++++++++++++++++--- avr/cores/megacommand/MCL/MCLGfx.h | 2 +- avr/cores/megacommand/MCL/MCLSd.cpp | 4 +++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index 10cc8fb8d..f505c514a 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -33,7 +33,47 @@ const unsigned char mcl_logo_bitmap[] PROGMEM = { 0x00, 0x1f, 0xf3, 0xff, 0xf3, 0xfe, 0x7e, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0xf3, 0xfe, 0x7c, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0xf3, 0xfe, 0x78, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0xf3, 0xfe, 0x70, 0x00}; + +// 'evilknievel', 45x32px +const unsigned char evilknievel_bitmap[] PROGMEM = { + 0x00, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xd5, 0xff, 0x00, + 0x00, 0x6f, 0xe8, 0x2a, 0xff, 0x80, 0x00, 0x77, 0x80, 0x05, 0x7f, 0x80, + 0x00, 0x62, 0x00, 0x02, 0xff, 0x80, 0x00, 0x70, 0x00, 0x05, 0x7f, 0x00, + 0x00, 0x60, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x20, 0x00, 0x01, 0x7f, 0x00, + 0x00, 0x60, 0x00, 0x02, 0xff, 0x00, 0x00, 0x28, 0x00, 0x01, 0x7f, 0x00, + 0x00, 0x3f, 0x87, 0xf2, 0xfe, 0x00, 0x00, 0x7f, 0xd7, 0xfd, 0x7e, 0x00, + 0x00, 0x7f, 0xef, 0xfe, 0xfe, 0x00, 0x00, 0x7f, 0xc7, 0xff, 0xfc, 0x00, + 0x00, 0x7f, 0xc7, 0xfe, 0xec, 0x00, 0x00, 0x3f, 0xc3, 0xfd, 0xd4, 0x00, + 0x00, 0x1e, 0x80, 0xfa, 0xec, 0x00, 0x00, 0x15, 0x00, 0x05, 0x54, 0x00, + 0x00, 0x1a, 0x80, 0x02, 0xa8, 0x00, 0x00, 0x1d, 0xc0, 0x05, 0x78, 0x00, + 0x00, 0x0a, 0xe2, 0x0a, 0xfc, 0x00, 0x00, 0x1d, 0x00, 0x05, 0x5c, 0x00, + 0x00, 0x0b, 0xc0, 0x0a, 0xbe, 0x00, 0x00, 0x3d, 0xc0, 0x15, 0x5f, 0x80, + 0x07, 0xfe, 0xe8, 0x0a, 0xbf, 0xf0, 0x1f, 0xff, 0xd0, 0x17, 0x5f, 0xf8, + 0x3f, 0xff, 0xe8, 0x0f, 0xbf, 0xf8, 0x3f, 0xff, 0x00, 0x1f, 0x7f, 0xf8, + 0x7f, 0xff, 0x80, 0x3e, 0xbf, 0xf8, 0x7f, 0xff, 0xc0, 0x7d, 0x7f, 0xf8, + 0xff, 0xff, 0xfa, 0xfa, 0xff, 0xf8, 0xff, 0xff, 0x7f, 0xf5, 0x7f, 0xf8}; +// oled_display.drawBitmap(128-45, 0, evilknievel_bitmap, 45, 32); + #endif +void MCLGfx::draw_evil() { +#ifdef OLED_DISPLAY + auto oldfont = oled_display.getFont(); + oled_display.setFont(&TomThumb); + for (int8_t x = -45; x < 128 - 45; x += 2) { + oled_display.fillScreen(WHITE); + oled_display.setCursor(x - 60, 10); + oled_display.setTextColor(BLACK, WHITE); + oled_display.println("THIS PIECE OF"); + oled_display.setCursor(x - 60, 20); + oled_display.println("MACHINE IS O.K."); + + oled_display.drawBitmap(x, 0, evilknievel_bitmap, 45, 32, BLACK); + oled_display.display(); +} + delay(1800); + oled_display.setFont(oldfont); +#endif +} void MCLGfx::splashscreen() { #ifdef OLED_DISPLAY @@ -70,9 +110,10 @@ void MCLGfx::splashscreen() { delay(750); for (uint8_t a = 0; a < 32; a++) { - oled_display.drawLine(35, a, BITMAP_MCL_LOGO_W + 35 + 30, a, BLACK); - oled_display.drawLine(35, 32 - a, BITMAP_MCL_LOGO_W + 35 + 30, 32 - a, BLACK); - oled_display.display(); + oled_display.drawLine(35, a, BITMAP_MCL_LOGO_W + 35 + 30, a, BLACK); + oled_display.drawLine(35, 32 - a, BITMAP_MCL_LOGO_W + 35 + 30, 32 - a, + BLACK); + oled_display.display(); } #else @@ -82,7 +123,7 @@ void MCLGfx::splashscreen() { char str2[17] = VERSION_STR; str1[16] = '\0'; display_text(&str1[0], &str2[0]); - delay(200); + delay(400); #endif // while (rec_global == 0) { diff --git a/avr/cores/megacommand/MCL/MCLGfx.h b/avr/cores/megacommand/MCL/MCLGfx.h index 72839ae20..42cc0c249 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.h +++ b/avr/cores/megacommand/MCL/MCLGfx.h @@ -6,13 +6,13 @@ class MCLGfx { public: + void draw_evil(); void splashscreen(); void init_oled(); void display_text(const char *str1, const char *str2); void alert(const char *str1, const char *str2); }; - extern MCLGfx gfx; #endif /* MCLGFX_H__ */ diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index 0676cff14..dd32ceae6 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -1,5 +1,6 @@ #include "MCL.h" #include "MCLSd.h" +#include "MCLGfx.h" /* Function for initialising the SD Card */ @@ -46,6 +47,9 @@ bool MCLSd::load_init() { DEBUG_PRINTLN("Could not init cfg"); return false; } + #ifdef OLED_DISPLAY + gfx.draw_evil(); + #endif proj.new_project(); return true; From e13e9948a019b47fd38e2396e32536851dd9d356 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 3 Dec 2019 10:41:32 +1100 Subject: [PATCH 414/469] revert splash time to 200ms for HD44780 --- avr/cores/megacommand/MCL/MCLGfx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index f505c514a..67e8aabc4 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -123,7 +123,7 @@ void MCLGfx::splashscreen() { char str2[17] = VERSION_STR; str1[16] = '\0'; display_text(&str1[0], &str2[0]); - delay(400); + delay(200); #endif // while (rec_global == 0) { From 81e66215a6aa71da1cca7f0501e15c7364df46be Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 3 Dec 2019 10:45:50 +1100 Subject: [PATCH 415/469] clear background after drawing evil --- avr/cores/megacommand/MCL/MCLSd.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index dd32ceae6..c1b20e2d0 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -49,6 +49,7 @@ bool MCLSd::load_init() { } #ifdef OLED_DISPLAY gfx.draw_evil(); + oled_display.clearDisplay(); #endif proj.new_project(); return true; From ebac3d761af4b2c57143f3b5999293205389dd06 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 5 Dec 2019 16:48:26 +1100 Subject: [PATCH 416/469] Rename merge to Mode. Implement SEQ, MD, Merge save modes --- avr/cores/megacommand/MCL/GridPages.cpp | 2 +- avr/cores/megacommand/MCL/GridSavePage.cpp | 37 +++++++++++++++------- avr/cores/megacommand/MCL/MCLActions.cpp | 6 ++-- avr/cores/megacommand/MCL/MCLActions.h | 2 +- avr/cores/megacommand/MCL/MDSeqTrackData.h | 11 ++++++- avr/cores/megacommand/MCL/MDTrack.cpp | 13 +++++--- avr/cores/megacommand/MCL/MDTrack.h | 5 ++- 7 files changed, 53 insertions(+), 23 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPages.cpp b/avr/cores/megacommand/MCL/GridPages.cpp index d255e7365..3c3186ca3 100644 --- a/avr/cores/megacommand/MCL/GridPages.cpp +++ b/avr/cores/megacommand/MCL/GridPages.cpp @@ -41,7 +41,7 @@ MCLEncoder param4(GRID_LENGTH, 1, ENCODER_RES_GRID); GridPage grid_page(¶m1, ¶m2, ¶m3, ¶m4); MCLEncoder gridio_param1(0, 1, ENCODER_RES_PAT); -MCLEncoder gridio_param2(0, 15, ENCODER_RES_PAT); +MCLEncoder gridio_param2(0, 2, ENCODER_RES_PAT); MCLEncoder gridio_param3(0, 64, ENCODER_RES_PAT); MCLEncoder gridio_param4(1, 11, ENCODER_RES_PAT); diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index a87438314..cff86f827 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -1,5 +1,6 @@ #include "GridSavePage.h" #include "MCL.h" +#include "MDTrack.h" #define S_PAGE 3 void GridSavePage::setup() { @@ -12,7 +13,7 @@ void GridSavePage::setup() { grid_page.reload_slot_models = false; } -void GridSavePage::init() { +void GridSavePage::init() { #ifdef OLED_DISPLAY mcl_gui.draw_popup("SAVE TO GRID", true, 28); #endif @@ -38,12 +39,20 @@ void GridSavePage::display() { GUI.setLine(GUI.LINE2); GUI.put_string_at_fill(0, "S"); - if ((MidiClock.state != 2) && (encoders[0]->cur == 1)) { - GUI.put_string_at(3, "MERGE"); + const char *merge = "SEQ"; + if (MidiClock.state == 2) { + merge = "---"; } else { - GUI.put_string_at(3, "--"); + if (encoders[0]->cur == SAVE_MD) { + merge = "MD"; + } + if (encoders[0]->cur == SAVE_MERGE) { + merge = "MERGE"; + } } + GUI.put_string_at(3, merge); + uint8_t step_count = (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - (64 * @@ -57,15 +66,19 @@ void GridSavePage::display() { mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); - const char* merge = "NO"; - if (encoders[0]->cur == 1) { - merge = "YES"; - } + const char *merge = "SEQ"; if (MidiClock.state == 2) { merge = "---"; + } else { + if (encoders[0]->cur == SAVE_MD) { + merge = "MD"; + } + if (encoders[0]->cur == SAVE_MERGE) { + merge = "MERGE"; + } } - - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "MERGE", merge); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "MODE", + merge); char step[4] = {'\0'}; uint8_t step_count = @@ -74,7 +87,8 @@ void GridSavePage::display() { ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, step, 10); - // mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "STEP", step); + // mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, + // MCLGUI::s_menu_y + 4, "STEP", step); oled_display.setFont(&TomThumb); // draw data flow in the center @@ -97,7 +111,6 @@ void GridSavePage::display() { oled_display.setCursor(74, MCLGUI::s_menu_y + 15); oled_display.print("GRID"); - oled_display.display(); } #endif diff --git a/avr/cores/megacommand/MCL/MCLActions.cpp b/avr/cores/megacommand/MCL/MCLActions.cpp index 84474f129..9afc7274c 100644 --- a/avr/cores/megacommand/MCL/MCLActions.cpp +++ b/avr/cores/megacommand/MCL/MCLActions.cpp @@ -72,7 +72,7 @@ void MCLActions::md_setsysex_recpos(uint8_t rec_type, uint8_t position) { // MD.sendRequest(0x6b,00000011); } -void MCLActions::store_tracks_in_mem(int column, int row, bool merge) { +void MCLActions::store_tracks_in_mem(int column, int row, uint8_t merge) { DEBUG_PRINT_FN(); EmptyTrack empty_track; @@ -106,9 +106,9 @@ void MCLActions::store_tracks_in_mem(int column, int row, bool merge) { #endif bool storepattern = false; - if (MidiClock.state == 2) { merge = false; } + if (MidiClock.state == 2) { merge = 0; } if (save_md_tracks) { - if ((merge)) { + if (merge > 0) { DEBUG_PRINTLN("fetching pattern"); if (!MD.getBlockingPattern(readpattern)) { DEBUG_PRINTLN("could not receive pattern"); diff --git a/avr/cores/megacommand/MCL/MCLActions.h b/avr/cores/megacommand/MCL/MCLActions.h index f3aeaab09..f3c7ee003 100644 --- a/avr/cores/megacommand/MCL/MCLActions.h +++ b/avr/cores/megacommand/MCL/MCLActions.h @@ -69,7 +69,7 @@ class MCLActions : public ChainModeData { void md_setsysex_recpos(uint8_t rec_type, uint8_t position); - void store_tracks_in_mem(int column, int row, bool merge); + void store_tracks_in_mem(int column, int row, uint8_t merge); void write_tracks(int column, int row); void send_tracks_to_devices(); diff --git a/avr/cores/megacommand/MCL/MDSeqTrackData.h b/avr/cores/megacommand/MCL/MDSeqTrackData.h index 19b15764a..7ba03561e 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrackData.h +++ b/avr/cores/megacommand/MCL/MDSeqTrackData.h @@ -26,7 +26,16 @@ class MDSeqTrackData { uint64_t lock_mask; uint8_t conditional[64]; uint8_t timing[64]; - + void init() { + length = 16; + resolution = 1; + memset(&locks, NUM_MD_LOCKS * 64, 0); + memset(&locks_params, NUM_MD_LOCKS, 0); + pattern_mask = 0; + lock_mask = 0; + memset(&conditional, 64, 0); + memset(&timing, 64, 0); + } }; #endif /* MDSEQTRACKDATA_H__ */ diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index 9ad292918..0e9535d85 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -310,7 +310,7 @@ void MDTrack::normalize() { } bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool storepattern, - bool merge, bool online) { + uint8_t merge, bool online) { /*Assign a track to Grid i*/ /*Extraact track data from received pattern and kit and store in track * object*/ @@ -330,7 +330,7 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s } if (track != 255) { - if ((storepattern) || (merge)) { get_track_from_pattern(track, column); } + if ((storepattern) || (merge > 0)) { get_track_from_pattern(track, column); } get_track_from_kit(track, column); } @@ -339,12 +339,17 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s } // Normalise level and vol locks - if (merge) { + if (merge > 0) { DEBUG_PRINTLN("auto merge"); //Set track length to equal MD pattern length on merge - seq_data.length = length; MDSeqTrack md_seq_track; + if (merge == SAVE_MERGE) { memcpy(&(md_seq_track), &(this->seq_data), sizeof(MDSeqTrackData)); + } + if (merge == SAVE_MD) { + md_seq_track.init(); + seq_data.length = length; + } md_seq_track.merge_from_md(this); memcpy(&(this->seq_data), &(md_seq_track), sizeof(MDSeqTrackData)); } diff --git a/avr/cores/megacommand/MCL/MDTrack.h b/avr/cores/megacommand/MCL/MDTrack.h index 47b921ed0..32c2c7b43 100644 --- a/avr/cores/megacommand/MCL/MDTrack.h +++ b/avr/cores/megacommand/MCL/MDTrack.h @@ -13,6 +13,9 @@ #define LOCK_AMOUNT 256 #define MD_TRACK_TYPE 1 +#define SAVE_MD 1 +#define SAVE_MERGE 2 + class ParameterLock { public: uint8_t param_number; @@ -86,7 +89,7 @@ class MDTrack : public MDTrackLight { bool load_track_from_grid(int32_t column, int32_t row); bool store_track_in_grid(int32_t column, int32_t row, int track = 255, - bool storepattern = false, bool merge = false, + bool storepattern = false, uint8_t merge = 0, bool online = false); // scale machine track vol by percentage From 42f736ed002aa8967074b94447db1e98b0618918 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 5 Dec 2019 16:50:50 +1100 Subject: [PATCH 417/469] wrong encoder max --- avr/cores/megacommand/MCL/GridPages.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPages.cpp b/avr/cores/megacommand/MCL/GridPages.cpp index 3c3186ca3..df4b75c45 100644 --- a/avr/cores/megacommand/MCL/GridPages.cpp +++ b/avr/cores/megacommand/MCL/GridPages.cpp @@ -40,8 +40,8 @@ MCLEncoder param4(GRID_LENGTH, 1, ENCODER_RES_GRID); GridPage grid_page(¶m1, ¶m2, ¶m3, ¶m4); -MCLEncoder gridio_param1(0, 1, ENCODER_RES_PAT); -MCLEncoder gridio_param2(0, 2, ENCODER_RES_PAT); +MCLEncoder gridio_param1(0, 2, ENCODER_RES_PAT); +MCLEncoder gridio_param2(0, 15, ENCODER_RES_PAT); MCLEncoder gridio_param3(0, 64, ENCODER_RES_PAT); MCLEncoder gridio_param4(1, 11, ENCODER_RES_PAT); From 391f0668bb17e2416bff47fc65f065013a677e93 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Thu, 5 Dec 2019 16:55:31 +1100 Subject: [PATCH 418/469] initialise utiming to centre --- avr/cores/megacommand/MCL/MDSeqTrackData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MDSeqTrackData.h b/avr/cores/megacommand/MCL/MDSeqTrackData.h index 7ba03561e..7c2e4006f 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrackData.h +++ b/avr/cores/megacommand/MCL/MDSeqTrackData.h @@ -34,7 +34,7 @@ class MDSeqTrackData { pattern_mask = 0; lock_mask = 0; memset(&conditional, 64, 0); - memset(&timing, 64, 0); + memset(&timing, 64, 12); } }; From 60c04bdd582ae29406dbdb5d9edab4801cf34fa7 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 5 Dec 2019 22:46:07 +0800 Subject: [PATCH 419/469] update save page dataflow visualization --- avr/cores/megacommand/MCL/GridSavePage.cpp | 37 +++++++++++----------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index cff86f827..6985d9394 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -66,19 +66,17 @@ void GridSavePage::display() { mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); - const char *merge = "SEQ"; - if (MidiClock.state == 2) { - merge = "---"; - } else { + const char *modestr = "SEQ"; + if (MidiClock.state != 2) { if (encoders[0]->cur == SAVE_MD) { - merge = "MD"; + modestr = "MD"; } if (encoders[0]->cur == SAVE_MERGE) { - merge = "MERGE"; + modestr = "MERGE"; } - } + } mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "MODE", - merge); + modestr); char step[4] = {'\0'}; uint8_t step_count = @@ -92,23 +90,24 @@ void GridSavePage::display() { oled_display.setFont(&TomThumb); // draw data flow in the center - if (MidiClock.state != 2 && encoders[0]->cur == 1) { - oled_display.setCursor(52, MCLGUI::s_menu_y + 12); + constexpr uint8_t data_x = 56; + if (MidiClock.state != 2 && encoders[0]->cur == SAVE_MERGE) { + oled_display.setCursor(data_x + 2, MCLGUI::s_menu_y + 12); oled_display.print("MD"); - oled_display.setCursor(50, MCLGUI::s_menu_y + 19); + oled_display.setCursor(data_x, MCLGUI::s_menu_y + 19); oled_display.print("SEQ"); - oled_display.drawFastHLine(63, MCLGUI::s_menu_y + 8, 2, WHITE); - oled_display.drawFastHLine(63, MCLGUI::s_menu_y + 15, 2, WHITE); - oled_display.drawFastVLine(65, MCLGUI::s_menu_y + 8, 8, WHITE); - mcl_gui.draw_horizontal_arrow(66, MCLGUI::s_menu_y + 12, 5); + oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 8, 2, WHITE); + oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 15, 2, WHITE); + oled_display.drawFastVLine(data_x + 15, MCLGUI::s_menu_y + 8, 8, WHITE); + mcl_gui.draw_horizontal_arrow(data_x + 16, MCLGUI::s_menu_y + 12, 5); } else { - oled_display.setCursor(50, MCLGUI::s_menu_y + 15); - oled_display.print("SEQ"); - mcl_gui.draw_horizontal_arrow(63, MCLGUI::s_menu_y + 12, 8); + oled_display.setCursor(data_x, MCLGUI::s_menu_y + 15); + oled_display.print(modestr); + mcl_gui.draw_horizontal_arrow(data_x + 13, MCLGUI::s_menu_y + 12, 8); } - oled_display.setCursor(74, MCLGUI::s_menu_y + 15); + oled_display.setCursor(data_x + 24, MCLGUI::s_menu_y + 15); oled_display.print("GRID"); oled_display.display(); From 4d922390a937c28477ec9d336f574ac7c94f4b95 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 6 Dec 2019 19:13:38 +1100 Subject: [PATCH 420/469] work in progress --- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 9 --------- avr/cores/megacommand/MCL/MDTrack.cpp | 9 ++------- avr/cores/megacommand/MCL/MDTrack.h | 3 +++ 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index 8e2adce88..f9e91bf1b 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -454,15 +454,6 @@ void MDSeqTrack::clear_track(bool locks, bool reset_params) { void MDSeqTrack::merge_from_md(MDTrack *md_track) { DEBUG_PRINT_FN(); - if (md_track->trigPattern == 0 && ((pattern_mask | lock_mask) != 0)) { - // If the MD sequencer data is empty, abort merge. - // This will prevent unnecessary length change of internal seq pattern - return; - } - if ((pattern_mask | lock_mask) == 0) { - set_length(md_track->length); - } - for (int n = 0; n < md_track->arraysize; n++) { set_track_locks(md_track->locks[n].step, md_track->locks[n].param_number, md_track->locks[n].value); diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index 0e9535d85..ebb552ac2 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -334,17 +334,12 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s get_track_from_kit(track, column); } - if (online) { - memcpy(&seq_data, &mcl_seq.md_tracks[track], sizeof(seq_data)); - } - // Normalise level and vol locks - - if (merge > 0) { + if (merge > 0 && online) { DEBUG_PRINTLN("auto merge"); //Set track length to equal MD pattern length on merge MDSeqTrack md_seq_track; if (merge == SAVE_MERGE) { - memcpy(&(md_seq_track), &(this->seq_data), sizeof(MDSeqTrackData)); + memcpy(&(md_seq_track), &(mcl_seq.md_tracks[track]), sizeof(MDSeqTrackData)); } if (merge == SAVE_MD) { md_seq_track.init(); diff --git a/avr/cores/megacommand/MCL/MDTrack.h b/avr/cores/megacommand/MCL/MDTrack.h index 32c2c7b43..f0a2fe8bb 100644 --- a/avr/cores/megacommand/MCL/MDTrack.h +++ b/avr/cores/megacommand/MCL/MDTrack.h @@ -71,6 +71,9 @@ class MDTrack : public MDTrackLight { int arraysize; ParameterLock locks[LOCK_AMOUNT]; + MDTrack() { + arraysize = 0; + } void init(); void clear_track(); From 5dd854e426c40f71c9307812a18f5ffdf3f8c3a3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 6 Dec 2019 19:25:34 +1100 Subject: [PATCH 421/469] Fixes --- avr/cores/megacommand/MCL/GridSavePage.cpp | 2 +- avr/cores/megacommand/MCL/MDTrack.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 6985d9394..5c86b349d 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -122,7 +122,7 @@ bool GridSavePage::handleEvent(gui_event_t *event) { return true; } else { md_exploit.off(); - bool merge = (encoders[0]->cur == 1); + uint8_t merge = encoders[0]->cur; mcl_actions.store_tracks_in_mem(0, grid_page.encoders[1]->getValue(), merge); } diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index ebb552ac2..dcca0223f 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -311,9 +311,6 @@ void MDTrack::normalize() { bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool storepattern, uint8_t merge, bool online) { - /*Assign a track to Grid i*/ - /*Extraact track data from received pattern and kit and store in track - * object*/ active = MD_TRACK_TYPE; bool ret; @@ -336,24 +333,30 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s if (merge > 0 && online) { DEBUG_PRINTLN("auto merge"); - //Set track length to equal MD pattern length on merge MDSeqTrack md_seq_track; if (merge == SAVE_MERGE) { - memcpy(&(md_seq_track), &(mcl_seq.md_tracks[track]), sizeof(MDSeqTrackData)); + //Load up internal sequencer data + memcpy(&(md_seq_track), &(mcl_seq.md_tracks[track]), sizeof(MDSeqTrackData)); } if (merge == SAVE_MD) { md_seq_track.init(); seq_data.length = length; } + //merge md pattern data with seq_data md_seq_track.merge_from_md(this); + //copy merged data in to this track object's seq data for writing to SD memcpy(&(this->seq_data), &(md_seq_track), sizeof(MDSeqTrackData)); } + + //Legacy, we no longer store the MD data. if (!storepattern) { clear_track(); } + //Normalise track levels if (mcl_cfg.auto_normalize == 1) { normalize(); } + //Write data to sd len = sizeof(MDTrack) - (LOCK_AMOUNT * 3); DEBUG_PRINTLN(len); ret = mcl_sd.write_data((uint8_t *)(this), len, &proj.file); From e64d2a1f125e9cb59f5206057aad8d74291a66fd Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 6 Dec 2019 19:29:52 +1100 Subject: [PATCH 422/469] more fix --- avr/cores/megacommand/MCL/GridSavePage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 5c86b349d..8b8492ec6 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -146,7 +146,7 @@ bool GridSavePage::handleEvent(gui_event_t *event) { note_interface.notes[i] = 3; } - bool merge = (encoders[0]->cur == 1); + uint8_t merge = encoders[0]->cur; mcl_actions.store_tracks_in_mem(grid_page.encoders[0]->getValue(), grid_page.encoders[1]->getValue(), merge); GUI.setPage(&grid_page); From 48113d00759830a192e2c29fa995d36ac3d32641 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 6 Dec 2019 19:46:20 +1100 Subject: [PATCH 423/469] Rename row on merge if the row was originally not active --- avr/cores/megacommand/MCL/MCLActions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLActions.cpp b/avr/cores/megacommand/MCL/MCLActions.cpp index 9afc7274c..94381d950 100644 --- a/avr/cores/megacommand/MCL/MCLActions.cpp +++ b/avr/cores/megacommand/MCL/MCLActions.cpp @@ -169,8 +169,8 @@ void MCLActions::store_tracks_in_mem(int column, int row, uint8_t merge) { } } - // Only update row name if, the current name is empty - if (strlen(grid_page.row_headers[grid_page.cur_row].name) == 0) { + // Only update row name if, the current row is not active. + if (!grid_page.row_headers[grid_page.cur_row].active) { for (uint8_t c = 0; c < 17; c++) { grid_page.row_headers[grid_page.cur_row].name[c] = MD.kit.name[c]; } From 5a917586ea9b8a0a94d780020e0fe842c3c2faed Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 6 Dec 2019 19:50:50 +1100 Subject: [PATCH 424/469] Throw alert when trying to rename inactive row --- avr/cores/megacommand/MCL/GridPage.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index d9ac8430e..10dec0b1e 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -600,10 +600,15 @@ void GridPage::prepare() { void rename_row() { char *my_title = "Row Name:"; - if (mcl_gui.wait_for_input(grid_page.row_headers[grid_page.cur_row].name, + if (grid_page.row_headers[grid_page.cur_row].active) { + if (mcl_gui.wait_for_input(grid_page.row_headers[grid_page.cur_row].name, my_title, 8)) { - grid_page.row_headers[grid_page.cur_row].write(grid_page.encoders[1]->cur); - proj.file.sync(); + grid_page.row_headers[grid_page.cur_row].write(grid_page.encoders[1]->cur); + proj.file.sync(); + } + } + else { + gfx.alert("Error","Row not active"); } } From d75e593c2e5159f10c45269c7da32e3efa7186cc Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 6 Dec 2019 21:10:33 +1100 Subject: [PATCH 425/469] MIDI start would still reset lfo parameters if LFO was disabled. --- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index 232f88c29..0da1ad356 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -102,8 +102,7 @@ void LFOSeqTrack::seq() { if (speed == 0) { sample_count += 2; - } - else { + } else { sample_hold += 1; if (sample_hold >= speed - 1) { sample_hold = 0; @@ -128,7 +127,8 @@ void LFOSeqTrack::seq() { } } -void LFOSeqTrack::check_and_update_params_offset(uint8_t track, uint8_t dest, uint8_t value) { +void LFOSeqTrack::check_and_update_params_offset(uint8_t track, uint8_t dest, + uint8_t value) { for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { if ((params[n].dest - 1 == track) && (params[n].param == dest)) { wav_table_state[n] = false; @@ -138,8 +138,10 @@ void LFOSeqTrack::check_and_update_params_offset(uint8_t track, uint8_t dest, ui } void LFOSeqTrack::reset_params_offset() { - for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { - params[n].reset_param_offset(); + if (enable) { + for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { + params[n].reset_param_offset(); + } } } @@ -151,13 +153,13 @@ void LFOSeqTrack::update_params_offset() { void LFOSeqTrack::update_kit_params() { for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { - params[n].update_kit(); + params[n].update_kit(); } } void LFOSeqParam::update_kit() { - if (dest <= NUM_MD_TRACKS) { - MD.kit.params[dest - 1][param] = offset; + if (dest <= NUM_MD_TRACKS) { + MD.kit.params[dest - 1][param] = offset; } else { switch (dest - NUM_MD_TRACKS - 1) { case MD_FX_ECHO - MD_FX_ECHO: From 2a87dd055648e27bbc449f40de2bf3b5212f173b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Fri, 6 Dec 2019 21:14:55 +1100 Subject: [PATCH 426/469] Allow for clearing locks of multiple steps --- avr/cores/megacommand/MCL/SeqPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index f5081065e..bda0ed9e4 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -725,7 +725,7 @@ void opt_clear_step_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { mcl_seq.md_tracks[last_md_track].clear_step_locks( - SeqPage::step_select + SeqPage::page_select * 16); + n + SeqPage::page_select * 16); } else { // mcl_seq.ext_tracks[last_ext_track].clear_step_locks( // SeqPage::step_select + SeqPage::page_select * 16); From b4ed327d5a47ef73ed96dc69036d4a249951da13 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 13:32:08 +1100 Subject: [PATCH 427/469] Typo in MDSeqTrack set_track_locks --- avr/cores/megacommand/MCL/MDSeqTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index f9e91bf1b..0e4e193da 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -289,7 +289,7 @@ void MDSeqTrack::set_track_locks(uint8_t step, uint8_t track_param, match = c; } } - if (match != 254) { + if (match != 255) { locks[match][step] = value + 1; } if (MidiClock.state == 2) { From adc42f4e6a586ce99b94296be2284b8dc5ae7e3d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 13:50:52 +1100 Subject: [PATCH 428/469] Memset parameter order reversed :'-( --- avr/cores/megacommand/MCL/MDSeqTrackData.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MDSeqTrackData.h b/avr/cores/megacommand/MCL/MDSeqTrackData.h index 7c2e4006f..1c13534c0 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrackData.h +++ b/avr/cores/megacommand/MCL/MDSeqTrackData.h @@ -29,12 +29,12 @@ class MDSeqTrackData { void init() { length = 16; resolution = 1; - memset(&locks, NUM_MD_LOCKS * 64, 0); - memset(&locks_params, NUM_MD_LOCKS, 0); + memset(&locks, 0, NUM_MD_LOCKS * 64); + memset(&locks_params, 0, NUM_MD_LOCKS); pattern_mask = 0; lock_mask = 0; - memset(&conditional, 64, 0); - memset(&timing, 64, 12); + memset(&conditional, 0, 64); + memset(&timing, 12, 64); } }; From 99b6fe3f5de71899242285da9f19b26dbe32ae6e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 15:07:02 +1100 Subject: [PATCH 429/469] SeqParamPage encoder value would be offset by 1 --- avr/cores/megacommand/MCL/SeqParamPage.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 53c5c538b..1cf120b6f 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -88,7 +88,7 @@ void SeqParamPage::display() { if (seq_lock1.getValue() == 0) { GUI.put_string_at(4, "--"); } else { - GUI.put_value_at2(4, seq_lock1.getValue() - 1); + GUI.put_value_at2(4, seq_lock1.getValue()); } if (seq_param3.getValue() == 0) { GUI.put_string_at(7, "--"); @@ -104,7 +104,7 @@ void SeqParamPage::display() { if (seq_lock2.getValue() == 0) { GUI.put_string_at(11, "--"); } else { - GUI.put_value_at2(11, seq_lock2.getValue() - 1); + GUI.put_value_at2(11, seq_lock2.getValue()); } if (page_id == 0) { GUI.put_string_at(14, "A"); @@ -178,8 +178,8 @@ void SeqParamPage::loop() { } SET_BIT64(mcl_seq.md_tracks[last_md_track].lock_mask, step); - mcl_seq.md_tracks[last_md_track].locks[p1][step] = seq_lock1.cur; - mcl_seq.md_tracks[last_md_track].locks[p2][step] = seq_lock2.cur; + mcl_seq.md_tracks[last_md_track].locks[p1][step] = seq_lock1.cur + 1; + mcl_seq.md_tracks[last_md_track].locks[p2][step] = seq_lock2.cur + 1; } } if (seq_param1.hasChanged() || seq_param3.hasChanged()) { @@ -216,8 +216,8 @@ bool SeqParamPage::handleEvent(gui_event_t *event) { seq_param1.cur = mcl_seq.md_tracks[last_md_track].locks_params[p1]; seq_param3.cur = mcl_seq.md_tracks[last_md_track].locks_params[p2]; - seq_lock1.cur = mcl_seq.md_tracks[last_md_track].locks[p1][step]; - seq_lock2.cur = mcl_seq.md_tracks[last_md_track].locks[p2][step]; + seq_lock1.cur = mcl_seq.md_tracks[last_md_track].locks[p1][step] - 1; + seq_lock2.cur = mcl_seq.md_tracks[last_md_track].locks[p2][step] - 1; } if (event->mask == EVENT_BUTTON_RELEASED) { if (device == DEVICE_A4) { From 5475d23c9c9a8fd5532609aae3962a00440ea0ee Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 21:23:12 +1100 Subject: [PATCH 430/469] Fix Chromatic Page info labels --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 46 +++++++++++++++--------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 771475f22..9cb660090 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -116,31 +116,33 @@ void SeqPtcPage::config() { // config info labels constexpr uint8_t len1 = sizeof(info1); char buf[len1] = {'\0'}; - char *str1; - char *str2; + + char str_first[3] = "--"; + char str_second[3] = "--"; if (midi_device == DEVICE_MD) { + char *str1; + char *str2; str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); + + m_strncpy_p(str_first, str1, len1); + + m_strncpy_p(str_second, str2, len1); } - #ifdef EXT_TRACKS +#ifdef EXT_TRACKS else { - char str_first[3] = "--"; if (Analog4.connected) { strcpy(str_first, "A4"); } else { strcpy(str_first, "MI"); } - char str_second[3] = "T "; - str_second[1] = '0' + last_ext_track; - str1 = &str_first[0]; - str2 = &str_second[0]; - } - #endif - m_strncpy_p(buf, str1, len1); - strncpy(info1, buf, len1); + str_second[0] = 'T'; + str_second[1] = last_ext_track + '1'; + } +#endif + strncpy(info1, str_first, len1); strncat(info1, ">", len1); - m_strncpy_p(buf, str2, len1); - strncat(info1, buf, len1); + strncat(info1, str_second, len1); strcpy(info2, "CHROMAT"); display_page_index = false; @@ -449,7 +451,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { redisplay = true; return true; } - queue_redraw(); + queue_redraw(); } if (note_interface.is_event(event)) { @@ -469,8 +471,13 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { if (mask == EVENT_BUTTON_PRESSED) { SET_BIT64(note_mask, pitch); + if (midi_device != DEVICE_MD) { + midi_device = device; + config(); + } else { + config_encoders(); + } midi_device = device; - config_encoders(); trig_md(note, pitch); } else if (mask == EVENT_BUTTON_RELEASED) { CLEAR_BIT(note_mask, pitch); @@ -572,6 +579,13 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { #ifdef EXT_TRACKS // otherwise, translate the message and send it back to MIDI2. + if (SeqPage::midi_device != midi_active_peering.get_device(UART2_PORT) || + (last_ext_track != channel)) { + + SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); + last_ext_track = channel; + seq_ptc_page.config(); + } SeqPage::midi_device = midi_active_peering.get_device(UART2_PORT); if (channel >= mcl_seq.num_ext_tracks) { return; From a3d183cbfd8210daac1ceec534946bcf499de4a2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 21:35:03 +1100 Subject: [PATCH 431/469] WavDesigner: confirmation box before sending sample --- avr/cores/megacommand/MCL/OscMixerPage.cpp | 48 ++++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/avr/cores/megacommand/MCL/OscMixerPage.cpp b/avr/cores/megacommand/MCL/OscMixerPage.cpp index 614be7003..b03aa7c53 100644 --- a/avr/cores/megacommand/MCL/OscMixerPage.cpp +++ b/avr/cores/megacommand/MCL/OscMixerPage.cpp @@ -19,34 +19,38 @@ void OscMixerPage::init() { void OscMixerPage::cleanup() {} bool OscMixerPage::handleEvent(gui_event_t *event) { if (EVENT_PRESSED(event, Buttons.BUTTON4)) { + GUI.ignoreNextEvent(event->source); + if (mcl_gui.wait_for_confirm("Send Sample", "Overwrite sample slot?")) { + #ifdef OLED_DISPLAY - oled_display.clearDisplay(); + oled_display.clearDisplay(); #endif - GUI.setLine(GUI.LINE1); - GUI.put_string_at(0, "Render.."); - LCD.goLine(0); - LCD.puts(GUI.lines[0].data); + GUI.setLine(GUI.LINE1); + GUI.put_string_at(0, "Render.."); + LCD.goLine(0); + LCD.puts(GUI.lines[0].data); #ifdef OLED_DISPLAY - oled_display.display(); - oled_display.clearDisplay(); + oled_display.display(); + oled_display.clearDisplay(); #endif - wd.render(); - GUI.put_string_at(0, "Sending.."); - LCD.goLine(0); - LCD.puts(GUI.lines[0].data); + wd.render(); + GUI.put_string_at(0, "Sending.."); + LCD.goLine(0); + LCD.puts(GUI.lines[0].data); #ifdef OLED_DISPLAY - oled_display.display(); + oled_display.display(); #endif - // if (MD.connected) { - // MD.rec_sample(); - //} - // delay(250); - // in_sysex = 1; - wd.send(); - // in_sysex = 0; - // delay(100); - // MD.press_no_button(); - // MD.clear_all_windows_quick(); + // if (MD.connected) { + // MD.rec_sample(); + //} + // delay(250); + // in_sysex = 1; + wd.send(); + // in_sysex = 0; + // delay(100); + // MD.press_no_button(); + // MD.clear_all_windows_quick(); + } return true; } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { From 77f707705a68bc908841dc96a17a114c4ee9447d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 21:44:08 +1100 Subject: [PATCH 432/469] Fixes for Evil. --- avr/cores/megacommand/MCL/MCLGfx.cpp | 1 + avr/cores/megacommand/MCL/MCLSd.cpp | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLGfx.cpp b/avr/cores/megacommand/MCL/MCLGfx.cpp index 67e8aabc4..3fdda6169 100644 --- a/avr/cores/megacommand/MCL/MCLGfx.cpp +++ b/avr/cores/megacommand/MCL/MCLGfx.cpp @@ -71,6 +71,7 @@ void MCLGfx::draw_evil() { oled_display.display(); } delay(1800); + oled_display.setTextColor(WHITE, BLACK); oled_display.setFont(oldfont); #endif } diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index c1b20e2d0..d6658bc79 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -1,6 +1,6 @@ #include "MCL.h" -#include "MCLSd.h" #include "MCLGfx.h" +#include "MCLSd.h" /* Function for initialising the SD Card */ @@ -32,6 +32,13 @@ bool MCLSd::load_init() { bool ret = false; int b; + if (BUTTON_DOWN(Buttons.BUTTON3)) { +#ifdef OLED_DISPLAY + gfx.draw_evil(); + oled_display.clearDisplay(); +#endif + } + if (sd_state) { if (mcl_cfg.cfgfile.open("/config.mcls", O_RDWR)) { @@ -47,10 +54,10 @@ bool MCLSd::load_init() { DEBUG_PRINTLN("Could not init cfg"); return false; } - #ifdef OLED_DISPLAY +#ifdef OLED_DISPLAY gfx.draw_evil(); oled_display.clearDisplay(); - #endif +#endif proj.new_project(); return true; @@ -60,7 +67,7 @@ bool MCLSd::load_init() { DEBUG_PRINTLN("Project count greater than 0, try to load existing"); if (!proj.load_project(mcl_cfg.project)) { DEBUG_PRINTLN("error loading project"); - proj.new_project(); + proj.new_project(); return true; } else { @@ -69,7 +76,7 @@ bool MCLSd::load_init() { } return true; } else { - proj.new_project(); + proj.new_project(); return true; } } else { @@ -78,7 +85,7 @@ bool MCLSd::load_init() { if (!mcl_cfg.cfg_init()) { return false; } - proj.new_project(); + proj.new_project(); return true; } } else { @@ -86,7 +93,7 @@ bool MCLSd::load_init() { if (!mcl_cfg.cfg_init()) { return false; } - proj.new_project(); + proj.new_project(); return true; } return true; From 9bdc77a824d0df786f2d76b42e5f374ef15aca4b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 22:10:04 +1100 Subject: [PATCH 433/469] Hide A4/Ext device on MD only sequencer pages. --- avr/cores/megacommand/MCL/SeqPage.cpp | 50 +++++++++++++--------- avr/cores/megacommand/MCL/SeqPage.h | 1 + avr/cores/megacommand/MCL/SeqParamPage.cpp | 1 + avr/cores/megacommand/MCL/SeqRlckPage.cpp | 4 +- avr/cores/megacommand/MCL/SeqRtrkPage.cpp | 5 ++- 5 files changed, 38 insertions(+), 23 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index bda0ed9e4..76f745e1c 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -8,6 +8,8 @@ uint8_t SeqPage::midi_device = DEVICE_MD; uint8_t SeqPage::page_count = 4; bool SeqPage::show_seq_menu = false; bool SeqPage::show_step_menu = false; +bool SeqPage::toggle_device = true; + uint8_t SeqPage::step_select = 255; uint8_t opt_resolution = 1; @@ -45,6 +47,7 @@ void SeqPage::init() { classic_display = false; oled_display.clearDisplay(); #endif + toggle_device = true; } void SeqPage::cleanup() { @@ -252,24 +255,24 @@ bool SeqPage::handleEvent(gui_event_t *event) { step_select = note; show_step_menu = true; GUI.pushPage(&step_menu_page); - } else { - if (opt_midi_device_capture == DEVICE_MD) { - DEBUG_PRINTLN("okay using MD for length update"); - opt_trackid = last_md_track + 1; - opt_resolution = (mcl_seq.md_tracks[last_md_track].resolution); } else { - opt_trackid = last_ext_track + 1; - opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); - } - // capture current midi_device value - opt_midi_device_capture = midi_device; - // capture current page. - opt_seqpage_capture = this; - GUI.pushPage(&seq_menu_page); - show_seq_menu = true; - return true; + if (opt_midi_device_capture == DEVICE_MD) { + DEBUG_PRINTLN("okay using MD for length update"); + opt_trackid = last_md_track + 1; + opt_resolution = (mcl_seq.md_tracks[last_md_track].resolution); + } else { + opt_trackid = last_ext_track + 1; + opt_resolution = (mcl_seq.ext_tracks[last_ext_track].resolution); + } + // capture current midi_device value + opt_midi_device_capture = midi_device; + // capture current page. + opt_seqpage_capture = this; + GUI.pushPage(&seq_menu_page); + show_seq_menu = true; + return true; + } } -} #endif // legacy enc push page switching code @@ -820,12 +823,12 @@ void opt_reverse_track_handler() { void seq_menu_handler() { #ifndef OLED_DISPLAY -SeqPage::show_seq_menu = false; + SeqPage::show_seq_menu = false; #endif } void step_menu_handler() { #ifndef OLED_DISPLAY -SeqPage::show_step_menu = false; + SeqPage::show_step_menu = false; #endif } @@ -929,6 +932,9 @@ void SeqPage::display() { void SeqPage::display() { bool is_md = (midi_device == DEVICE_MD); + if (!toggle_device) { + is_md = true; + } #ifdef EXT_TRACKS bool ext_is_a4 = Analog4.connected; #else @@ -946,8 +952,12 @@ void SeqPage::display() { // draw MD/EXT label const char *str_ext = "MI"; - if (ext_is_a4) { - str_ext = "A4"; + if (toggle_device) { + if (ext_is_a4) { + str_ext = "A4"; + } + } else { + str_ext = " "; } mcl_gui.draw_panel_toggle("MD", str_ext, is_md); diff --git a/avr/cores/megacommand/MCL/SeqPage.h b/avr/cores/megacommand/MCL/SeqPage.h index 017a6403d..abd12f236 100644 --- a/avr/cores/megacommand/MCL/SeqPage.h +++ b/avr/cores/megacommand/MCL/SeqPage.h @@ -51,6 +51,7 @@ class SeqPage : public LightPage { static uint8_t step_select; static bool show_seq_menu; static bool show_step_menu; + static bool toggle_device; bool recording = false; bool display_page_index = true; diff --git a/avr/cores/megacommand/MCL/SeqParamPage.cpp b/avr/cores/megacommand/MCL/SeqParamPage.cpp index 1cf120b6f..d9b779092 100644 --- a/avr/cores/megacommand/MCL/SeqParamPage.cpp +++ b/avr/cores/megacommand/MCL/SeqParamPage.cpp @@ -30,6 +30,7 @@ void SeqParamPage::config() { void SeqParamPage::init() { config(); md_exploit.on(); + toggle_device = false; note_interface.state = true; seq_param1.max = 24; diff --git a/avr/cores/megacommand/MCL/SeqRlckPage.cpp b/avr/cores/megacommand/MCL/SeqRlckPage.cpp index ee120c916..6bd7dbcc1 100644 --- a/avr/cores/megacommand/MCL/SeqRlckPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRlckPage.cpp @@ -26,6 +26,7 @@ void SeqRlckPage::config() { void SeqRlckPage::init() { SeqPage::init(); + toggle_device = false; if (MidiClock.state == 2) { MD.midi_events.disable_live_kit_update(); } @@ -97,12 +98,13 @@ void SeqRlckPage::display() { draw_knob_frame(); uint8_t len = seq_param3.getValue(); +/* #ifdef EXT_TRACKS if (SeqPage::midi_device != DEVICE_MD) { len = len / (2 / mcl_seq.ext_tracks[last_ext_track].resolution); } #endif - +*/ char K[4]; itoa(len, K, 10); draw_knob(2, "LEN", K); diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 6aab2c993..07cafa91c 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -27,7 +27,7 @@ void SeqRtrkPage::config() { void SeqRtrkPage::init() { SeqPage::init(); - + toggle_device = false; note_interface.state = true; seq_param1.max = 4; @@ -86,12 +86,13 @@ void SeqRtrkPage::display() { draw_knob_frame(); uint8_t len = seq_param3.getValue(); +/* #ifdef EXT_TRACKS if (SeqPage::midi_device != DEVICE_MD) { len = len / (2 / mcl_seq.ext_tracks[last_ext_track].resolution); } #endif - +*/ char K[4]; itoa(len, K, 10); draw_knob(2, "LEN", K); From faa629b2ef3e036cc257a40cef0bf9b5717045c0 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 22:51:20 +1100 Subject: [PATCH 434/469] Bug fix: Wrong kit would be loaded on write original mode --- avr/cores/megacommand/MCL/MCLActions.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLActions.cpp b/avr/cores/megacommand/MCL/MCLActions.cpp index 94381d950..ee7857b4a 100644 --- a/avr/cores/megacommand/MCL/MCLActions.cpp +++ b/avr/cores/megacommand/MCL/MCLActions.cpp @@ -380,10 +380,8 @@ void MCLActions::send_tracks_to_devices() { /*Send the encoded kit to the MD via sysex*/ md_setsysex_recpos(4, MD.kit.origPosition); MD.kit.toSysex(); - /*Instruct the MD to reload the kit, as the kit changes won't update until - * the kit is reloaded*/ - // if (reload == 1) { - MD.loadKit(MD.pattern.kit); + MD.loadKit(MD.kit.origPosition); + // Send Analog4 #ifdef EXT_TRACKS if (Analog4.connected) { From e969b4be387396d45798b19a904e5c104789d5a5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 7 Dec 2019 23:44:33 +1100 Subject: [PATCH 435/469] fix incorrect formula for fx params --- avr/cores/megacommand/MCL/FXPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/FXPage.cpp b/avr/cores/megacommand/MCL/FXPage.cpp index 5ea350747..4847753cd 100644 --- a/avr/cores/megacommand/MCL/FXPage.cpp +++ b/avr/cores/megacommand/MCL/FXPage.cpp @@ -89,7 +89,7 @@ void FXPage::loop() { break; } for (uint8_t n = 0; n < mcl_seq.num_lfo_tracks; n++) { - mcl_seq.lfo_tracks[n].check_and_update_params_offset(MD_FX_ECHO + fx_type, fx_param, encoders[i]->cur); + mcl_seq.lfo_tracks[n].check_and_update_params_offset(NUM_MD_TRACKS + 1 + fx_type - MD_FX_ECHO, fx_param, encoders[i]->cur); } } } From e423f004fb0a2d9642475b571ec5bfacbc9c6752 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 12:03:59 +1100 Subject: [PATCH 436/469] LFO Bug Fixes. Reset params would not work when disabling lfo (regression) Wavtable up-to-date check was faulty. Adjusting FX params from FX pages will now properly adjust lfo offset if lfo dest is an fx param. --- avr/cores/megacommand/MCL/LFOPage.cpp | 4 ++-- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 12 +++++++----- avr/cores/megacommand/MCL/LFOSeqTrack.h | 2 +- avr/cores/megacommand/MCL/MCLSeq.cpp | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index dbbaa5084..cd775c369 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -468,10 +468,10 @@ bool LFOPage::handleEvent(gui_event_t *event) { } if (EVENT_PRESSED(event, Buttons.BUTTON1)) { - lfo_track->enable = !(lfo_track->enable); - if (!lfo_track->enable) { + if (lfo_track->enable) { lfo_track->reset_params_offset(); } + lfo_track->enable = !(lfo_track->enable); } if (EVENT_PRESSED(event, Buttons.BUTTON2)) { GUI.setPage(&page_select_page); diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index 0da1ad356..b42e92886 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -45,13 +45,14 @@ void LFOSeqTrack::load_wav_table(uint8_t table) { } uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { - uint8_t offset = params[param].offset; - uint8_t depth = params[param].depth; + int8_t offset = params[param].offset; + int8_t depth = params[param].depth; + int8_t sample = wav_table[param][sample_count]; int8_t val; switch (offset_behaviour) { case LFO_OFFSET_CENTRE: - val = offset + (wav_table[param][sample_count] - (depth / 2)); + val = offset + (sample - (depth / 2)); if (val > 127) { return 127; } @@ -62,7 +63,8 @@ uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { } break; case LFO_OFFSET_MAX: - val = offset - depth + wav_table[param][sample_count]; + //val = 127 - sample; + val = offset - depth + sample; if (val > 127) { return 127; } @@ -130,7 +132,7 @@ void LFOSeqTrack::seq() { void LFOSeqTrack::check_and_update_params_offset(uint8_t track, uint8_t dest, uint8_t value) { for (uint8_t n = 0; n < NUM_LFO_PARAMS; n++) { - if ((params[n].dest - 1 == track) && (params[n].param == dest)) { + if ((params[n].dest == track) && (params[n].param == dest)) { wav_table_state[n] = false; params[n].offset = value; } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index e3fc73427..ba1f40c93 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -62,7 +62,7 @@ class LFOSeqTrack { void reset_params_offset(); bool wav_table_up_to_date(uint8_t n) { - return wav_table_state[2]; + return wav_table_state[n]; } void check_and_update_params_offset(uint8_t dest, uint8_t param, uint8_t value); diff --git a/avr/cores/megacommand/MCL/MCLSeq.cpp b/avr/cores/megacommand/MCL/MCLSeq.cpp index 4a638a6d1..1ef21cc38 100644 --- a/avr/cores/megacommand/MCL/MCLSeq.cpp +++ b/avr/cores/megacommand/MCL/MCLSeq.cpp @@ -178,7 +178,7 @@ void MCLSeqMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { MD.parseCC(channel, param, &track, &track_param); mcl_seq.md_tracks[track].update_param(track_param, value); for (uint8_t n = 0; n < mcl_seq.num_lfo_tracks; n++) { - mcl_seq.lfo_tracks[n].check_and_update_params_offset(track, track_param, value); + mcl_seq.lfo_tracks[n].check_and_update_params_offset(track + 1, track_param, value); } } } From 1218114b7d1a275bebc78aff5ddd4c96a2e454cc Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 13:01:12 +1100 Subject: [PATCH 437/469] Add text box popup to describe user action --- avr/cores/megacommand/MCL/GridSavePage.cpp | 26 +++++++++++++++++++-- avr/cores/megacommand/MCL/GridWritePage.cpp | 2 ++ avr/cores/megacommand/MCL/MCLGUI.cpp | 18 ++++++++++++++ avr/cores/megacommand/MCL/MCLGUI.h | 1 + 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 8b8492ec6..a7c6ab303 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -6,7 +6,6 @@ void GridSavePage::setup() { MD.getCurrentTrack(CALLBACK_TIMEOUT); MD.getCurrentPattern(CALLBACK_TIMEOUT); - encoders[0]->cur = 1; md_exploit.on(); note_interface.state = true; curpage = S_PAGE; @@ -74,7 +73,7 @@ void GridSavePage::display() { if (encoders[0]->cur == SAVE_MERGE) { modestr = "MERGE"; } - } + } mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "MODE", modestr); @@ -121,6 +120,18 @@ bool GridSavePage::handleEvent(gui_event_t *event) { if (BUTTON_DOWN(Buttons.BUTTON2)) { return true; } else { + + const char *modestr = "SEQ"; + if (MidiClock.state != 2) { + if (encoders[0]->cur == SAVE_MD) { + modestr = "MD"; + } + if (encoders[0]->cur == SAVE_MERGE) { + modestr = "MERGE"; + } + } + + mcl_gui.draw_textbox("SAVE ", modestr); md_exploit.off(); uint8_t merge = encoders[0]->cur; mcl_actions.store_tracks_in_mem(0, grid_page.encoders[1]->getValue(), @@ -137,6 +148,17 @@ bool GridSavePage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { + const char *modestr = "SEQ"; + if (MidiClock.state != 2) { + if (encoders[0]->cur == SAVE_MD) { + modestr = "MD"; + } + if (encoders[0]->cur == SAVE_MERGE) { + modestr = "MERGE"; + } + } + + mcl_gui.draw_textbox("SAVE ", modestr); md_exploit.off(); DEBUG_PRINTLN("notes"); diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index bbd56a332..8a6ceb996 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -144,6 +144,7 @@ bool GridWritePage::handleEvent(gui_event_t *event) { if (BUTTON_DOWN(Buttons.BUTTON2)) { return true; } else { + mcl_gui.draw_textbox("CHAIN",""); md_exploit.off(); mcl_actions.write_tracks(0, grid_page.encoders[1]->getValue()); } @@ -163,6 +164,7 @@ bool GridWritePage::handleEvent(gui_event_t *event) { note_interface.notes[i] = 3; } // write_tracks_to_md(-1); + mcl_gui.draw_textbox("CHAIN + FX",""); mcl_actions.write_original = 1; mcl_actions.write_tracks(0, grid_page.encoders[1]->getValue()); GUI.setPage(&grid_page); diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 88344aadb..44399ab6d 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -1,5 +1,23 @@ #include "MCL.h" +void MCLGUI::draw_textbox(char *text, char *text2) { + auto oldfont = oled_display.getFont(); + oled_display.setFont(); + uint8_t font_width = 6; + uint8_t w = ((strlen(text) + strlen(text2) + 2) * font_width); + uint8_t x = 64 - w / 2; + uint8_t y = 8; + + oled_display.fillRect(x - 1, y - 1, w + 2, 8 * 2 + 2, BLACK); + oled_display.drawRect(x, y, w, 8 * 2, WHITE); + oled_display.setCursor(x + font_width, y + 4); + oled_display.print(text); + oled_display.print(text2); + oled_display.display(); + oled_display.setFont(oldfont); + +} + bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { text_input_page.init(); text_input_page.init_text(dst, title, len); diff --git a/avr/cores/megacommand/MCL/MCLGUI.h b/avr/cores/megacommand/MCL/MCLGUI.h index f303b91ca..ad27cb0ec 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -15,6 +15,7 @@ class MCLGUI { // fills dst buffer with input text. ensures that: // 1. dst is null-terminated // 2. dst has no trailing spaces + void draw_textbox(char *text, char *text2); bool wait_for_input(char *dst, const char *title, uint8_t len); void draw_vertical_dashline(uint8_t x, uint8_t from = 1, uint8_t to = 32); void draw_horizontal_dashline(uint8_t y, uint8_t from, uint8_t to); From 18451487ecb19dc034ea514eb7eafda4f984700a Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 16:27:45 +1100 Subject: [PATCH 438/469] Move draw_textbox in to SSD1305 + GFX libraries --- .../Adafruit-GFX-Library/Adafruit_GFX.cpp | 14 ++++++++++ .../Adafruit-GFX-Library/Adafruit_GFX.h | 1 + .../Adafruit_SSD1305.cpp | 10 ++++++- .../Adafruit_SSD1305.h | 9 +++++- avr/cores/megacommand/MCL/GridSavePage.cpp | 11 +++++--- avr/cores/megacommand/MCL/GridWritePage.cpp | 28 +++++++++++-------- 6 files changed, 56 insertions(+), 17 deletions(-) diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp index bbff41ddc..806c2fd16 100755 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.cpp @@ -970,6 +970,20 @@ void Adafruit_GFX::setRotation(uint8_t x) { } } +void Adafruit_GFX::draw_textbox(char *text, char *text2) { + auto oldfont = getFont(); + setFont(); + uint8_t font_width = 6; + uint8_t w = ((strlen(text) + strlen(text2) + 2) * font_width); + uint8_t x = 64 - w / 2; + uint8_t y = 8; + fillRect(x - 1, y - 1, w + 2, 8 * 2 + 2, 0); + drawRect(x, y, w, 8 * 2, 1); + setCursor(x + font_width, y + 4); + print(text); + print(text2); + setFont(oldfont); +} // Enable (or disable) Code Page 437-compatible charset. // There was an error in glcdfont.c for the longest time -- one character // (#176, the 'light shade' block) was missing -- this threw off the index diff --git a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h index 8e8fe8331..33a57ec42 100755 --- a/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h +++ b/avr/cores/megacommand/Adafruit-GFX-Library/Adafruit_GFX.h @@ -34,6 +34,7 @@ class Adafruit_GFX : public Print { // optimized code. Otherwise 'generic' versions are used. virtual void setRotation(uint8_t r); virtual void invertDisplay(boolean i); + virtual void draw_textbox(char *text, char *text2); // BASIC DRAW API // These MAY be overridden by the subclass to provide device-specific diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index 9793af2c1..d60b2b3d4 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -30,7 +30,6 @@ redistribution #include #include #include - #include "glcdfont.c" #ifdef SPI_HAS_TRANSACTION @@ -536,7 +535,16 @@ void Adafruit_SSD1305::data(uint8_t c) { } } + +void Adafruit_SSD1305::textbox(char *text, char *text2, uint16_t delay) { + textbox_clock = slowclock; + strncpy(textbox_str, text, sizeof(textbox_str)); + strncpy(textbox_str2, text2, sizeof(textbox_str)); + textbox_delay = delay; +} + void Adafruit_SSD1305::display(void) { + if (clock_diff(textbox_clock, slowclock) < textbox_delay) { draw_textbox(textbox_str, textbox_str2); } uint16_t i = 0; uint8_t page; if (SSD1305_LCDHEIGHT == 64) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 67f319234..1620f69b4 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -22,7 +22,7 @@ All text above, and the splash screen must be included in any redistribution #endif #include - +#include "helpers.h" #define adagfx_swap(a, b) { uint8_t t = a; a = b; b = t; } #define BLACK 0 @@ -121,6 +121,9 @@ class Adafruit_SSD1305 : public Adafruit_GFX { void invertDisplay(uint8_t i); void setBrightness(uint8_t i); uint8_t getBuffer(uint16_t i); + bool redisplay = true; + + void textbox(char *text, char *text2, uint16_t delay = 800); void display(); void drawPixel(int16_t x, int16_t y, uint16_t color); @@ -130,6 +133,10 @@ class Adafruit_SSD1305 : public Adafruit_GFX { virtual void fillScreen(uint16_t color); private: + uint16_t textbox_delay; + uint16_t textbox_clock; + char textbox_str[12]; + char textbox_str2[12]; uint8_t _i2caddr; int8_t sid, sclk, dc, rst, cs; void spixfer(uint8_t x); diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index a7c6ab303..8a250a5ea 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -121,7 +121,11 @@ bool GridSavePage::handleEvent(gui_event_t *event) { return true; } else { + uint16_t last_clock = slowclock; const char *modestr = "SEQ"; +#ifdef OLED_DISPLAY + oled_display.textbox("SAVE SLOTS: ", modestr); +#endif if (MidiClock.state != 2) { if (encoders[0]->cur == SAVE_MD) { modestr = "MD"; @@ -131,7 +135,6 @@ bool GridSavePage::handleEvent(gui_event_t *event) { } } - mcl_gui.draw_textbox("SAVE ", modestr); md_exploit.off(); uint8_t merge = encoders[0]->cur; mcl_actions.store_tracks_in_mem(0, grid_page.encoders[1]->getValue(), @@ -157,9 +160,9 @@ bool GridSavePage::handleEvent(gui_event_t *event) { modestr = "MERGE"; } } - - mcl_gui.draw_textbox("SAVE ", modestr); - +#ifdef OLED_DISPLAY + oled_display.textbox("SAVE PAT: ", modestr); +#endif md_exploit.off(); DEBUG_PRINTLN("notes"); DEBUG_DUMP(note_interface.notes_all_off()); diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index 8a6ceb996..a6fe12431 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -10,12 +10,12 @@ void GridWritePage::setup() { MD.currentPattern - 16 * ((int)MD.currentPattern / (int)16); patternswitch = 1; - ((MCLEncoder *)encoders[3])->max = 6; - if (mode == WRITE_PAGE) { - encoders[3]->cur = 4; - mode = CHAIN_PAGE; - } - ((MCLEncoder *)encoders[2])->max = 1; + ((MCLEncoder *)encoders[3])->max = 6; + if (mode == WRITE_PAGE) { + encoders[3]->cur = 4; + mode = CHAIN_PAGE; + } + ((MCLEncoder *)encoders[2])->max = 1; // MD.requestKit(MD.currentKit); md_exploit.on(); @@ -93,7 +93,7 @@ void GridWritePage::display() { mcl_gui.draw_trigs(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 21, 0, 0, 0, 16); - char K[4] = { '\0' }; + char K[4] = {'\0'}; // draw step count uint8_t step_count = @@ -101,7 +101,8 @@ void GridWritePage::display() { (64 * ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) / 64)); itoa(step_count, K, 10); - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "STEP", K); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + 4, MCLGUI::s_menu_y + 4, "STEP", + K); // draw quantize strcpy(K, "---"); @@ -109,7 +110,8 @@ void GridWritePage::display() { uint8_t x = 1 << encoders[3]->getValue(); itoa(x, K, 10); } - mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, MCLGUI::s_menu_y + 4, "QUANT", K); + mcl_gui.draw_text_encoder(MCLGUI::s_menu_x + MCLGUI::s_menu_w - 26, + MCLGUI::s_menu_y + 4, "QUANT", K); oled_display.setFont(&TomThumb); // draw data flow in the center @@ -144,7 +146,9 @@ bool GridWritePage::handleEvent(gui_event_t *event) { if (BUTTON_DOWN(Buttons.BUTTON2)) { return true; } else { - mcl_gui.draw_textbox("CHAIN",""); +#ifdef OLED_DISPLAY + oled_display.textbox("CHAIN SLOTS", ""); +#endif md_exploit.off(); mcl_actions.write_tracks(0, grid_page.encoders[1]->getValue()); } @@ -164,7 +168,9 @@ bool GridWritePage::handleEvent(gui_event_t *event) { note_interface.notes[i] = 3; } // write_tracks_to_md(-1); - mcl_gui.draw_textbox("CHAIN + FX",""); +#ifdef OLED_DISPLAY + oled_display.textbox("CHAIN PAT", " + FX"); +#endif mcl_actions.write_original = 1; mcl_actions.write_tracks(0, grid_page.encoders[1]->getValue()); GUI.setPage(&grid_page); From 6ced81f27c32d4bdd1660ab25cda56108552ec71 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 16:38:24 +1100 Subject: [PATCH 439/469] Add switch to disable textbox after timeout --- .../Adafruit_SSD1305.cpp | 18 ++++++++++++------ .../Adafruit_SSD1305.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index d60b2b3d4..8be31cc2b 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -27,10 +27,10 @@ redistribution #include "Adafruit_GFX.h" #include "Adafruit_SSD1305.h" +#include "glcdfont.c" #include #include #include -#include "glcdfont.c" #ifdef SPI_HAS_TRANSACTION SPISettings oledspi = SPISettings(16000000, MSBFIRST, SPI_MODE0); @@ -241,11 +241,11 @@ void Adafruit_SSD1305::drawFastVLine(int16_t x, int16_t y, int16_t h, void Adafruit_SSD1305::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { - //for (int16_t x2 = x + w; x < x2; ++x) + // for (int16_t x2 = x + w; x < x2; ++x) //{ - //drawFastVLine(x, y, h, color); + // drawFastVLine(x, y, h, color); //} - //return; + // return; if (x < 0) { x = 0; } @@ -535,16 +535,22 @@ void Adafruit_SSD1305::data(uint8_t c) { } } - void Adafruit_SSD1305::textbox(char *text, char *text2, uint16_t delay) { textbox_clock = slowclock; strncpy(textbox_str, text, sizeof(textbox_str)); strncpy(textbox_str2, text2, sizeof(textbox_str)); textbox_delay = delay; + textbox_enabled = true; } void Adafruit_SSD1305::display(void) { - if (clock_diff(textbox_clock, slowclock) < textbox_delay) { draw_textbox(textbox_str, textbox_str2); } + if (textbox_enabled) { + if (clock_diff(textbox_clock, slowclock) < textbox_delay) { + draw_textbox(textbox_str, textbox_str2); + } else { + textbox_enabled = false; + } + } uint16_t i = 0; uint8_t page; if (SSD1305_LCDHEIGHT == 64) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 1620f69b4..4908900ce 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -133,6 +133,7 @@ class Adafruit_SSD1305 : public Adafruit_GFX { virtual void fillScreen(uint16_t color); private: + bool textbox_enabled = false; uint16_t textbox_delay; uint16_t textbox_clock; char textbox_str[12]; From 3a0cbd182aa07a9129837aacc3ee627b1507cbf5 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 16:42:34 +1100 Subject: [PATCH 440/469] improve textbox label for write page --- avr/cores/megacommand/MCL/GridWritePage.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index a6fe12431..35b627dae 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -169,7 +169,12 @@ bool GridWritePage::handleEvent(gui_event_t *event) { } // write_tracks_to_md(-1); #ifdef OLED_DISPLAY + if (MidiClock.state != 2) { oled_display.textbox("CHAIN PAT", " + FX"); + } + else { + oled_display.textbox("CHAIN PAT", ""); + } #endif mcl_actions.write_original = 1; mcl_actions.write_tracks(0, grid_page.encoders[1]->getValue()); From 899547be0e44624c505219e4f7510bae9dd4a85b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 21:36:43 +1100 Subject: [PATCH 441/469] SEQ mode, would not copy sequencer data to track object for storage --- .../Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp | 3 ++- .../Adafruit_SSD1305_Library/Adafruit_SSD1305.h | 2 +- avr/cores/megacommand/MCL/MDTrack.cpp | 9 +++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index 8be31cc2b..b616ccea3 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -535,12 +535,13 @@ void Adafruit_SSD1305::data(uint8_t c) { } } -void Adafruit_SSD1305::textbox(char *text, char *text2, uint16_t delay) { +void Adafruit_SSD1305::textbox(char *text, char *text2, uint16_t delay, bool displaynow) { textbox_clock = slowclock; strncpy(textbox_str, text, sizeof(textbox_str)); strncpy(textbox_str2, text2, sizeof(textbox_str)); textbox_delay = delay; textbox_enabled = true; + if (displaynow) { display(); } } void Adafruit_SSD1305::display(void) { diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 4908900ce..5d7a0014a 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -123,7 +123,7 @@ class Adafruit_SSD1305 : public Adafruit_GFX { uint8_t getBuffer(uint16_t i); bool redisplay = true; - void textbox(char *text, char *text2, uint16_t delay = 800); + void textbox(char *text, char *text2, uint16_t delay = 800, displaynow = false); void display(); void drawPixel(int16_t x, int16_t y, uint16_t color); diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index dcca0223f..d55f9324e 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -66,7 +66,9 @@ bool MDTrack::get_track_from_pattern(int tracknumber, uint8_t column) { if (IS_BIT_SET64(trigPattern, s)) { locks[n].step = s; - locks[n].param_number = i; + DEBUG_PRINTLN("storing lock"); + locks[n].param_number = i; + DEBUG_PRINTLN(locks[n].param_number); locks[n].value = MD.pattern.locks[idx][s]; n++; } @@ -79,6 +81,7 @@ bool MDTrack::get_track_from_pattern(int tracknumber, uint8_t column) { // itoa(n,&str[2],10); arraysize = n; + DEBUG_PRINTLN(arraysize); patternOrigPosition = MD.pattern.origPosition; } @@ -347,7 +350,9 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s //copy merged data in to this track object's seq data for writing to SD memcpy(&(this->seq_data), &(md_seq_track), sizeof(MDSeqTrackData)); } - + else { + memcpy(&(this->seq_data), &(mcl_seq.md_tracks[track]), sizeof(MDSeqTrackData)); + } //Legacy, we no longer store the MD data. if (!storepattern) { clear_track(); } From c2db745e57c44cc8521934b8d117837d96239c35 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 21:53:19 +1100 Subject: [PATCH 442/469] Add action text boxes for slot, track and step menus --- .../Adafruit_SSD1305.cpp | 3 +- .../Adafruit_SSD1305.h | 2 +- avr/cores/megacommand/MCL/GridPage.cpp | 14 +++++- avr/cores/megacommand/MCL/GridWritePage.cpp | 3 ++ avr/cores/megacommand/MCL/SeqPage.cpp | 43 +++++++++++++++++-- 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index b616ccea3..8be31cc2b 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -535,13 +535,12 @@ void Adafruit_SSD1305::data(uint8_t c) { } } -void Adafruit_SSD1305::textbox(char *text, char *text2, uint16_t delay, bool displaynow) { +void Adafruit_SSD1305::textbox(char *text, char *text2, uint16_t delay) { textbox_clock = slowclock; strncpy(textbox_str, text, sizeof(textbox_str)); strncpy(textbox_str2, text2, sizeof(textbox_str)); textbox_delay = delay; textbox_enabled = true; - if (displaynow) { display(); } } void Adafruit_SSD1305::display(void) { diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 5d7a0014a..4908900ce 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -123,7 +123,7 @@ class Adafruit_SSD1305 : public Adafruit_GFX { uint8_t getBuffer(uint16_t i); bool redisplay = true; - void textbox(char *text, char *text2, uint16_t delay = 800, displaynow = false); + void textbox(char *text, char *text2, uint16_t delay = 800); void display(); void drawPixel(int16_t x, int16_t y, uint16_t color); diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 10dec0b1e..145b9499a 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -659,16 +659,26 @@ void GridPage::apply_slot_changes() { } if (slot_copy == 1) { - +#ifdef OLED_DISPLAY + oled_display.textbox("COPY", ""); +#endif mcl_clipboard.copy(getCol(), getRow(), width, height); } else if (slot_paste == 1) { + oled_display.textbox("PASTE", ""); mcl_clipboard.paste(getCol(), getRow()); } else { GridRowHeader header; - +#ifdef OLED_DISPLAY + if (slot_clear == 1) { + oled_display.textbox("CLEAR", ""); + } + else if (slot_update == 1) { + oled_display.textbox("CHAIN ", "UPDATE"); + } +#endif for (uint8_t y = 0; y < height && y + getRow() < GRID_LENGTH; y++) { header.read(y + getRow()); diff --git a/avr/cores/megacommand/MCL/GridWritePage.cpp b/avr/cores/megacommand/MCL/GridWritePage.cpp index 35b627dae..0a962f201 100644 --- a/avr/cores/megacommand/MCL/GridWritePage.cpp +++ b/avr/cores/megacommand/MCL/GridWritePage.cpp @@ -148,6 +148,7 @@ bool GridWritePage::handleEvent(gui_event_t *event) { } else { #ifdef OLED_DISPLAY oled_display.textbox("CHAIN SLOTS", ""); + oled_display.display(); #endif md_exploit.off(); mcl_actions.write_tracks(0, grid_page.encoders[1]->getValue()); @@ -171,9 +172,11 @@ bool GridWritePage::handleEvent(gui_event_t *event) { #ifdef OLED_DISPLAY if (MidiClock.state != 2) { oled_display.textbox("CHAIN PAT", " + FX"); + oled_display.display(); } else { oled_display.textbox("CHAIN PAT", ""); + oled_display.display(); } #endif mcl_actions.write_original = 1; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 76f745e1c..bcf59be08 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -594,21 +594,35 @@ void opt_resolution_handler() { void opt_clear_track_handler() { if (opt_midi_device_capture == DEVICE_MD) { if (opt_clear == 2) { + #ifdef OLED_DISPLAY + oled_display.textbox("CLEAR MD ", "TRACKS"); +#endif + for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_track(); } } else if (opt_clear == 1) { - mcl_seq.md_tracks[last_md_track].clear_track(); + #ifdef OLED_DISPLAY + oled_display.textbox("CLEAR TRACK", ""); +#endif + + mcl_seq.md_tracks[last_md_track].clear_track(); } } #ifdef EXT_TRACKS else { if (opt_clear == 2) { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR TRACK", ""); +#endif mcl_seq.ext_tracks[n].clear_track(); } } else if (opt_clear == 1) { - mcl_seq.ext_tracks[last_ext_track].clear_track(); + #ifdef OLED_DISPLAY + oled_display.textbox("CLEAR EXT ", "TRACKS"); +#endif + mcl_seq.ext_tracks[last_ext_track].clear_track(); } } #endif @@ -619,9 +633,17 @@ void opt_clear_locks_handler() { if (opt_midi_device_capture == DEVICE_MD) { if (opt_clear == 2) { for (uint8_t n = 0; n < 16; ++n) { +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR ALL ", "LOCKS"); +#endif + mcl_seq.md_tracks[n].clear_locks(); } } else if (opt_clear == 1) { +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR TRACK ", "LOCKS"); +#endif + mcl_seq.md_tracks[last_md_track].clear_locks(); } } else { @@ -651,6 +673,9 @@ void opt_clear_all_locks_handler() { } void opt_copy_track_handler() { +#ifdef OLED_DISPLAY + oled_display.textbox("COPY TRACK", ""); +#endif if (opt_copy == 2) { if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.copy_sequencer(); @@ -677,6 +702,9 @@ void opt_copy_track_handler() { } void opt_paste_track_handler() { +#ifdef OLED_DISPLAY + oled_display.textbox("PASTE TRACK", ""); +#endif if (opt_paste == 2) { if (opt_midi_device_capture == DEVICE_MD) { mcl_clipboard.paste_sequencer(); @@ -703,11 +731,17 @@ void opt_paste_track_handler() { } void opt_copy_step_handler() { +#ifdef OLED_DISPLAY + oled_display.textbox("COPY STEP", ""); +#endif mcl_seq.md_tracks[last_md_track].copy_step( SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); } void opt_paste_step_handler() { +#ifdef OLED_DISPLAY + oled_display.textbox("PASTE STEP", ""); +#endif mcl_seq.md_tracks[last_md_track].paste_step( SeqPage::step_select + SeqPage::page_select * 16, &mcl_clipboard.step); } @@ -722,7 +756,10 @@ void opt_mute_step_handler() { } void opt_clear_step_locks_handler() { - if (opt_clear_step == 1) { +#ifdef OLED_DISPLAY +oled_display.textbox("CLEAR STEP: ", "LOCKS"); +#endif +if (opt_clear_step == 1) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { if (note_interface.notes[n] == 1) { From 03811c0d5313324e2454e8ce193c703d575a97d9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 22:13:38 +1100 Subject: [PATCH 443/469] improvements to action textboxess --- avr/cores/megacommand/MCL/GridSavePage.cpp | 2 + avr/cores/megacommand/MCL/SeqPage.cpp | 77 +++++++++++++++------- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 8a250a5ea..3aa7d477f 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -125,6 +125,7 @@ bool GridSavePage::handleEvent(gui_event_t *event) { const char *modestr = "SEQ"; #ifdef OLED_DISPLAY oled_display.textbox("SAVE SLOTS: ", modestr); + oled_display.display(); #endif if (MidiClock.state != 2) { if (encoders[0]->cur == SAVE_MD) { @@ -162,6 +163,7 @@ bool GridSavePage::handleEvent(gui_event_t *event) { } #ifdef OLED_DISPLAY oled_display.textbox("SAVE PAT: ", modestr); + oled_display.display(); #endif md_exploit.off(); DEBUG_PRINTLN("notes"); diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index bcf59be08..21470423a 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -594,19 +594,19 @@ void opt_resolution_handler() { void opt_clear_track_handler() { if (opt_midi_device_capture == DEVICE_MD) { if (opt_clear == 2) { - #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR MD ", "TRACKS"); +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR MD ", "TRACKS"); #endif - + for (uint8_t n = 0; n < 16; ++n) { mcl_seq.md_tracks[n].clear_track(); } } else if (opt_clear == 1) { - #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR TRACK", ""); +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR TRACK", ""); #endif - - mcl_seq.md_tracks[last_md_track].clear_track(); + + mcl_seq.md_tracks[last_md_track].clear_track(); } } #ifdef EXT_TRACKS @@ -614,15 +614,15 @@ void opt_clear_track_handler() { if (opt_clear == 2) { for (uint8_t n = 0; n < mcl_seq.num_ext_tracks; n++) { #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR TRACK", ""); + oled_display.textbox("CLEAR TRACK", ""); #endif mcl_seq.ext_tracks[n].clear_track(); } } else if (opt_clear == 1) { - #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR EXT ", "TRACKS"); +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR EXT ", "TRACKS"); #endif - mcl_seq.ext_tracks[last_ext_track].clear_track(); + mcl_seq.ext_tracks[last_ext_track].clear_track(); } } #endif @@ -634,16 +634,15 @@ void opt_clear_locks_handler() { if (opt_clear == 2) { for (uint8_t n = 0; n < 16; ++n) { #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR ALL ", "LOCKS"); + oled_display.textbox("CLEAR MD ", "LOCKS"); #endif mcl_seq.md_tracks[n].clear_locks(); } } else if (opt_clear == 1) { #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR TRACK ", "LOCKS"); + oled_display.textbox("CLEAR ", "LOCKS"); #endif - mcl_seq.md_tracks[last_md_track].clear_locks(); } } else { @@ -673,26 +672,38 @@ void opt_clear_all_locks_handler() { } void opt_copy_track_handler() { -#ifdef OLED_DISPLAY - oled_display.textbox("COPY TRACK", ""); -#endif if (opt_copy == 2) { + if (opt_midi_device_capture == DEVICE_MD) { +#ifdef OLED_DISPLAY + oled_display.textbox("COPY MD ", "TRACKS"); +#endif + mcl_clipboard.copy_sequencer(); } #ifdef EXT_TRACKS else { +#ifdef OLED_DISPLAY + oled_display.textbox("COPY EXT ", "TRACKS"); +#endif mcl_clipboard.copy_sequencer(NUM_MD_TRACKS); } #endif } if (opt_copy == 1) { if (opt_midi_device_capture == DEVICE_MD) { +#ifdef OLED_DISPLAY + oled_display.textbox("COPY TRACK", ""); +#endif mcl_clipboard.copy_track = last_md_track; mcl_clipboard.copy_sequencer_track(last_md_track); } #ifdef EXT_TRACKS else { +#ifdef OLED_DISPLAY + oled_display.textbox("COPY EXT ", "TRACK"); +#endif + mcl_clipboard.copy_track = last_ext_track + NUM_MD_TRACKS; mcl_clipboard.copy_sequencer_track(last_ext_track + NUM_MD_TRACKS); } @@ -702,26 +713,36 @@ void opt_copy_track_handler() { } void opt_paste_track_handler() { -#ifdef OLED_DISPLAY - oled_display.textbox("PASTE TRACK", ""); -#endif if (opt_paste == 2) { + if (opt_midi_device_capture == DEVICE_MD) { +#ifdef OLED_DISPLAY + oled_display.textbox("PASTE MD ", "TRACKS"); +#endif mcl_clipboard.paste_sequencer(); } #ifdef EXT_TRACKS else { +#ifdef OLED_DISPLAY + oled_display.textbox("PASTE EXT ", "TRACKS"); +#endif mcl_clipboard.paste_sequencer(NUM_MD_TRACKS); } #endif } if (opt_paste == 1) { if (opt_midi_device_capture == DEVICE_MD) { +#ifdef OLED_DISPLAY + oled_display.textbox("PASTE TRACK", ""); +#endif mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_md_track); } #ifdef EXT_TRACKS else { +#ifdef OLED_DISPLAY + oled_display.textbox("PASTE EXT ", "TRACK"); +#endif mcl_clipboard.paste_sequencer_track(mcl_clipboard.copy_track, last_ext_track + NUM_MD_TRACKS); } @@ -757,9 +778,9 @@ void opt_mute_step_handler() { void opt_clear_step_locks_handler() { #ifdef OLED_DISPLAY -oled_display.textbox("CLEAR STEP: ", "LOCKS"); + oled_display.textbox("CLEAR STEP: ", "LOCKS"); #endif -if (opt_clear_step == 1) { + if (opt_clear_step == 1) { for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { if (note_interface.notes[n] == 1) { @@ -833,12 +854,18 @@ void opt_reverse_track_handler() { if (opt_reverse == 2) { if (opt_midi_device_capture == DEVICE_MD) { +#ifdef OLED_DISPLAY + //oled_display.textbox("REVERSE ", "MD TRACKS"); +#endif for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { mcl_seq.md_tracks[n].reverse(); } } #ifdef EXT_TRACKS else { +#ifdef OLED_DISPLAY + //oled_display.textbox("REVERSE ", "EXT TRACKS"); +#endif for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { mcl_seq.ext_tracks[n].reverse(); } @@ -848,10 +875,16 @@ void opt_reverse_track_handler() { if (opt_reverse == 1) { if (opt_midi_device_capture == DEVICE_MD) { +#ifdef OLED_DISPLAY + //oled_display.textbox("REVERSE ", "TRACK"); +#endif mcl_seq.md_tracks[last_md_track].reverse(); } #ifdef EXT_TRACKS else { +#ifdef OLED_DISPLAY + //oled_display.textbox("REVERSE ", "EXT TRACK"); +#endif mcl_seq.ext_tracks[last_ext_track].reverse(); } #endif From fde451ec51f8fdf88a8330227d263f687dbdc76e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 22:19:22 +1100 Subject: [PATCH 444/469] improve textbox text on grid page --- avr/cores/megacommand/MCL/GridPage.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 145b9499a..2a3ab3320 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -660,7 +660,12 @@ void GridPage::apply_slot_changes() { if (slot_copy == 1) { #ifdef OLED_DISPLAY - oled_display.textbox("COPY", ""); + if (width > 0) { + oled_display.textbox("COPY ", "SLOTS"); + } + else { + oled_display.textbox("COPY ", "SLOT"); + } #endif mcl_clipboard.copy(getCol(), getRow(), width, height); @@ -673,7 +678,12 @@ void GridPage::apply_slot_changes() { GridRowHeader header; #ifdef OLED_DISPLAY if (slot_clear == 1) { - oled_display.textbox("CLEAR", ""); + if (width > 0) { + oled_display.textbox("CLEAR ", "SLOTS"); + } + else { + oled_display.textbox("CLEAR ", "SLOT"); + } } else if (slot_update == 1) { oled_display.textbox("CHAIN ", "UPDATE"); From 64cf19d2730e7dddf17f912ce5052d0a989b5948 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 8 Dec 2019 22:37:38 +1100 Subject: [PATCH 445/469] Add action textbox for SeqPtcPage --- avr/cores/megacommand/MCL/SeqPtcPage.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index 9cb660090..ea2c40e02 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -506,18 +506,26 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { if (midi_device == DEVICE_MD) { if (poly_count > 1) { +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR ", "POLY TRACKS"); +#endif for (uint8_t c = 0; c < 16; c++) { if (IS_BIT_SET16(mcl_cfg.poly_mask, c)) { - - mcl_seq.md_tracks[c].clear_track(); + mcl_seq.md_tracks[c].clear_track(); } } } else { - mcl_seq.md_tracks[last_md_track].clear_track(); +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR ", "TRACK"); +#endif + mcl_seq.md_tracks[last_md_track].clear_track(); } } #ifdef EXT_TRACKS else { +#ifdef OLED_DISPLAY + oled_display.textbox("CLEAR ", "EXT TRACK"); +#endif mcl_seq.ext_tracks[last_ext_track].clear_track(); } #endif From 9fcd5c34d93924c08e59814e266262927678f8d8 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 09:34:21 +1100 Subject: [PATCH 446/469] Step page should be called STEP --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 9ca23e22e..533008363 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -22,7 +22,7 @@ void SeqStepPage::config() { strncat(info1, ">", len1); m_strncpy_p(buf, str2, len1); strncat(info1, buf, len1); - strcpy(info2, "NOTE"); + strcpy(info2, "STEP"); // config menu config_as_trackedit(); From c0580565c3d3f5e397e1dd11740a9bdaab161eb2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 09:35:31 +1100 Subject: [PATCH 447/469] ignore button release --- avr/cores/megacommand/MCL/MCLSd.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index d6658bc79..5043efcbd 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -36,6 +36,7 @@ bool MCLSd::load_init() { #ifdef OLED_DISPLAY gfx.draw_evil(); oled_display.clearDisplay(); + GUI.ignoreNextEvent(Buttons.BUTTON3); #endif } From 29cff384423c41fe58f24a22a75377cc394f596e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 10:43:06 +1100 Subject: [PATCH 448/469] Add note_interface ignoreNextEvent --- avr/cores/megacommand/MCL/NoteInterface.cpp | 14 +++++++++++--- avr/cores/megacommand/MCL/NoteInterface.h | 5 ++++- avr/cores/megacommand/MCL/SeqPage.cpp | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/NoteInterface.cpp b/avr/cores/megacommand/MCL/NoteInterface.cpp index 46f8ea38f..e87c5915c 100644 --- a/avr/cores/megacommand/MCL/NoteInterface.cpp +++ b/avr/cores/megacommand/MCL/NoteInterface.cpp @@ -23,7 +23,7 @@ void NoteInterface::note_on_event(uint8_t note_num, uint8_t port) { DEBUG_PRINTLN("note interface disabled"); return; } - if (note_num > 20) { + if (note_num > 20) { return; } if (notes[note_num] != 1) { @@ -32,7 +32,10 @@ void NoteInterface::note_on_event(uint8_t note_num, uint8_t port) { if (note_num < 16) { note_hold = slowclock; } - + if (IS_BIT_SET64(ignore_next_mask, note_num)) { + CLEAR_BIT64(ignore_next_mask, note_num); + return; + } gui_event_t event; event.source = note_num + 128; event.mask = EVENT_BUTTON_PRESSED; @@ -43,8 +46,13 @@ void NoteInterface::note_off_event(uint8_t note_num, uint8_t port) { if (!state) { return; } - DEBUG_PRINTLN(note_num); + DEBUG_PRINTLN(note_num); notes[note_num] = 3; + if (IS_BIT_SET64(ignore_next_mask, note_num)) { + CLEAR_BIT64(ignore_next_mask, note_num); + return; + } + DEBUG_PRINTLN("note off"); gui_event_t event; event.source = note_num + 128; diff --git a/avr/cores/megacommand/MCL/NoteInterface.h b/avr/cores/megacommand/MCL/NoteInterface.h index 9f9466b6e..1d8404916 100644 --- a/avr/cores/megacommand/MCL/NoteInterface.h +++ b/avr/cores/megacommand/MCL/NoteInterface.h @@ -29,6 +29,7 @@ class NoteInterface { uint8_t notes[NI_MAX_NOTES]; uint8_t notecount = 0; uint8_t last_note; + uint64_t ignore_next_mask; uint16_t note_hold = 0; bool note_proceed = false; bool state = true; @@ -44,7 +45,9 @@ class NoteInterface { uint8_t notes_count_off(); uint8_t notes_count(); uint8_t notes_count_on(); - + void ignoreNextEvent(uint8_t i) { + SET_BIT64(ignore_next_mask, i); + } NoteInterfaceMidiEvents ni_midi_events; }; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index 21470423a..c90d6313d 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -86,6 +86,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { // TI + SHIFT2 = select track. if (BUTTON_DOWN(Buttons.BUTTON3)) { opt_trackid = track + 1; + note_interface.ignoreNextEvent(track); select_track(device, track); redisplay = true; seq_menu_page.select_item(0); @@ -124,6 +125,7 @@ bool SeqPage::handleEvent(gui_event_t *event) { } #endif encoders[2]->cur = step; + note_interface.ignoreNextEvent(track); if (event->mask == EVENT_BUTTON_RELEASED) { note_interface.notes[track] = 0; } From cdb2289209aac20bd4a7e6fbabfeb7f184e1e9bf Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 10:54:19 +1100 Subject: [PATCH 449/469] fix LFOPage labeling --- avr/cores/megacommand/MCL/LFOPage.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOPage.cpp b/avr/cores/megacommand/MCL/LFOPage.cpp index cd775c369..3c2e88748 100644 --- a/avr/cores/megacommand/MCL/LFOPage.cpp +++ b/avr/cores/megacommand/MCL/LFOPage.cpp @@ -338,7 +338,7 @@ void LFOPage::display() { } x = mcl_gui.knob_x0 + 2; - oled_display.setCursor(x + 4, 7); + oled_display.setCursor(x + 5, 6); oled_display.print("WAV"); draw_knob(1, encoders[1], "SPD"); @@ -350,26 +350,26 @@ void LFOPage::display() { const char *info2; if (page_mode) { - info1 = "LFO>DST"; + info2 = "LFO>DST"; } else { - info1 = "LFO>MOD"; + info2 = "LFO>MOD"; } switch (lfo_track->mode) { case LFO_MODE_FREE: - info2 = "FREE"; + info1 = "FREE"; break; case LFO_MODE_TRIG: draw_lock_mask(0, 0, lfo_track->step_count, lfo_track->length, true); draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); - info2 = "TRIG"; + info1 = "TRIG"; break; case LFO_MODE_ONE: draw_lock_mask(0, 0, lfo_track->step_count, lfo_track->length, true); draw_pattern_mask(0, lfo_track->pattern_mask, lfo_track->step_count, lfo_track->length, true); - info2 = "ONE"; + info1 = "ONE"; break; } mcl_gui.draw_panel_labels(info1, info2); From 945d49ccd0ce5c65fe6500abb1c73f835c4dbe82 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 11:21:23 +1100 Subject: [PATCH 450/469] Fix: Mixer page encoders would get stuck on first use. hopefully fixed --- avr/cores/megacommand/MCL/MixerPage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index c2b4162bf..31f0ea9af 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -67,10 +67,10 @@ void MixerPage::setup() { void MixerPage::init() { level_pressmode = 0; - encoders[0]->cur = 60; - encoders[1]->cur = 60; - encoders[2]->cur = 60; - encoders[3]->cur = 60; + for (uint8_t i = 0; i < 4; i++) { + encoders[i]->cur = 64; + encoders[i]->old = 64; + } bool switch_tracks = false; note_interface.state = true; midi_events.setup_callbacks(); From 2a725b7986b6447b1386c3229ecdb0b6aacfa41f Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 11:26:25 +1100 Subject: [PATCH 451/469] remove initializing code from mixer page --- avr/cores/megacommand/MCL/MixerPage.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/avr/cores/megacommand/MCL/MixerPage.cpp b/avr/cores/megacommand/MCL/MixerPage.cpp index 31f0ea9af..a0ca5a1a4 100644 --- a/avr/cores/megacommand/MCL/MixerPage.cpp +++ b/avr/cores/megacommand/MCL/MixerPage.cpp @@ -68,8 +68,8 @@ void MixerPage::setup() { void MixerPage::init() { level_pressmode = 0; for (uint8_t i = 0; i < 4; i++) { - encoders[i]->cur = 64; - encoders[i]->old = 64; + encoders[i]->cur = 64; + encoders[i]->old = 64; } bool switch_tracks = false; note_interface.state = true; @@ -84,7 +84,6 @@ void MixerPage::init() { oled_display.clearDisplay(); oled_draw_routing(); set_display_mode(MODEL_LEVEL); - initializing = true; for (uint8_t i = 0; i < 16; i++) { uint8_t scaled_level = (uint8_t)(((float)MD.kit.levels[i] / (float)127) * (float)FADER_LEN); @@ -213,13 +212,7 @@ void encoder_lastparam_handle(Encoder *enc) { void MixerPage::adjust_param(Encoder *enc, uint8_t param) { - if (initializing) { - if (param == MODEL_FLTQ) { - initializing = false; - } - } else { - set_display_mode(param); - } + set_display_mode(param); int dir = enc->getValue() - enc->old; int newval; From a599156fe44d3f4f13dc9d4a7873b3f0b7b3fafd Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 11:39:43 +1100 Subject: [PATCH 452/469] fix compile on HD44780 --- avr/cores/megacommand/MCL/GridPage.cpp | 2 ++ avr/cores/megacommand/MCL/MCLGUI.cpp | 3 ++- avr/cores/megacommand/MCL/MixerPage.h | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 2a3ab3320..f7f7764c1 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -672,7 +672,9 @@ void GridPage::apply_slot_changes() { } else if (slot_paste == 1) { +#ifdef OLED_DISPLAY oled_display.textbox("PASTE", ""); +#endif mcl_clipboard.paste(getCol(), getRow()); } else { GridRowHeader header; diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 44399ab6d..1f23d9e8d 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -1,6 +1,7 @@ #include "MCL.h" void MCLGUI::draw_textbox(char *text, char *text2) { +#ifdef OLED_DISPLAY auto oldfont = oled_display.getFont(); oled_display.setFont(); uint8_t font_width = 6; @@ -15,7 +16,7 @@ void MCLGUI::draw_textbox(char *text, char *text2) { oled_display.print(text2); oled_display.display(); oled_display.setFont(oldfont); - +#endif } bool MCLGUI::wait_for_input(char *dst, const char *title, uint8_t len) { diff --git a/avr/cores/megacommand/MCL/MixerPage.h b/avr/cores/megacommand/MCL/MixerPage.h index 42a200e23..6a260623f 100644 --- a/avr/cores/megacommand/MCL/MixerPage.h +++ b/avr/cores/megacommand/MCL/MixerPage.h @@ -33,7 +33,6 @@ class MixerPage : public LightPage { uint8_t params[16][3]; char info_line2[9]; uint8_t display_mode; - bool initializing = false; MixerPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : LightPage(e1, e2, e3, e4) { From 6f64a8c5aa07f35c653a9bf85612e3056b8d98f7 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 13:14:09 +1100 Subject: [PATCH 453/469] Add textbox to indicate display_mirror is active --- avr/cores/megacommand/MCL/MCL.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avr/cores/megacommand/MCL/MCL.cpp b/avr/cores/megacommand/MCL/MCL.cpp index 680302bc0..a2427f05e 100644 --- a/avr/cores/megacommand/MCL/MCL.cpp +++ b/avr/cores/megacommand/MCL/MCL.cpp @@ -59,6 +59,9 @@ void MCL::setup() { if (mcl_cfg.display_mirror == 1) { #ifndef DEBUGMODE +#ifdef OLED_DISPLAY + oled_display.textbox("DISPLAY ","MIRROR"); +#endif Serial.begin(250000); GUI.display_mirror = true; #endif From 6d36eadee66c567fd85aee09f06d4970c51be8e3 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Mon, 9 Dec 2019 15:11:19 +1100 Subject: [PATCH 454/469] Changelog --- Changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changelog b/Changelog index 0c200a682..18692add5 100644 --- a/Changelog +++ b/Changelog @@ -25,6 +25,7 @@ Enahanced Graphics: GUI popups. Loading screens. Graphical Encoders. + User action feedback textboxes for almost all user actions. Improved Project and File navigation. Abiltiy to rename/delete files/projects @@ -41,6 +42,12 @@ Sequencer Track Menu and Trig Menu. Trig menu accessible by pressing a trig and then holding shift 2. Allows copying/pasting of trigs, adding/remove trig mutes clearing locks. +Save/Chain Pages: + 3 Save modes now: SEQ, MD, MERGE. (MD and MERGE only available when + sequencer is stopped and are used for copying MD pattern data) + + New GUI popup menu. + Grid Page: Slot menu, provides copy + paste options. Encoders 3 and 4 are used to select a rectangular region of tracks/slots to be copied. @@ -72,6 +79,7 @@ Firmware Performance & MIDI Timing : Improved latency coompensation for realtime recording on Chromatic and Record pages. Device peering now works from any page. + Numerous bug and workflow fixes. MCL 2.40 25/06/2019 From f1817aa4d69dbf934d94cd380082e8a8660f5c40 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 10 Dec 2019 22:09:26 +1100 Subject: [PATCH 455/469] Fix GridSavePage seq diagram --- avr/cores/megacommand/MCL/GridSavePage.cpp | 20 +++++++++++++++++--- avr/cores/megacommand/MCL/MDTrack.h | 4 +--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 3aa7d477f..b61c06904 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -101,9 +101,23 @@ void GridSavePage::display() { oled_display.drawFastVLine(data_x + 15, MCLGUI::s_menu_y + 8, 8, WHITE); mcl_gui.draw_horizontal_arrow(data_x + 16, MCLGUI::s_menu_y + 12, 5); } else { - oled_display.setCursor(data_x, MCLGUI::s_menu_y + 15); - oled_display.print(modestr); - mcl_gui.draw_horizontal_arrow(data_x + 13, MCLGUI::s_menu_y + 12, 8); + if (encoders[0]->cur == SAVE_SEQ || MidiClock.state == 2) { + oled_display.setCursor(data_x, MCLGUI::s_menu_y + 12); + oled_display.print("SND"); + oled_display.setCursor(data_x, MCLGUI::s_menu_y + 19); + oled_display.print("SEQ"); + + oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 8, 2, WHITE); + oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 15, 2, + WHITE); + oled_display.drawFastVLine(data_x + 15, MCLGUI::s_menu_y + 8, 8, WHITE); + mcl_gui.draw_horizontal_arrow(data_x + 16, MCLGUI::s_menu_y + 12, 5); + } + else { + oled_display.setCursor(data_x, MCLGUI::s_menu_y + 15); + oled_display.print(modestr); + mcl_gui.draw_horizontal_arrow(data_x + 13, MCLGUI::s_menu_y + 12, 8); + } } oled_display.setCursor(data_x + 24, MCLGUI::s_menu_y + 15); diff --git a/avr/cores/megacommand/MCL/MDTrack.h b/avr/cores/megacommand/MCL/MDTrack.h index f0a2fe8bb..2a58688df 100644 --- a/avr/cores/megacommand/MCL/MDTrack.h +++ b/avr/cores/megacommand/MCL/MDTrack.h @@ -13,6 +13,7 @@ #define LOCK_AMOUNT 256 #define MD_TRACK_TYPE 1 +#define SAVE_SEQ 0 #define SAVE_MD 1 #define SAVE_MERGE 2 @@ -71,9 +72,6 @@ class MDTrack : public MDTrackLight { int arraysize; ParameterLock locks[LOCK_AMOUNT]; - MDTrack() { - arraysize = 0; - } void init(); void clear_track(); From fe300edc0114c512bcc2f3761783a2969052d407 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Wed, 11 Dec 2019 12:58:14 +0800 Subject: [PATCH 456/469] OOBE: clear display before showing new project dialog --- avr/cores/megacommand/MCL/MCLSd.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index 5043efcbd..f1c5b2580 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -94,6 +94,9 @@ bool MCLSd::load_init() { if (!mcl_cfg.cfg_init()) { return false; } +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif proj.new_project(); return true; } From ea62ee8b5ae54a0dd7c755979651d0f4476c35c6 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Wed, 11 Dec 2019 20:54:25 +1100 Subject: [PATCH 457/469] Regression fix: Copy paste was broken. MDTrack::save did not support offline mode after recent changes. --- avr/cores/megacommand/MCL/MCLSd.cpp | 2 +- avr/cores/megacommand/MCL/MDTrack.cpp | 96 +++++++++++++++------------ avr/cores/megacommand/MCL/MDTrack.h | 4 ++ 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index f1c5b2580..a2635b3b9 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -32,7 +32,7 @@ bool MCLSd::load_init() { bool ret = false; int b; - if (BUTTON_DOWN(Buttons.BUTTON3)) { + if (BUTTON_DOWN(Buttons.BUTTON2)) { #ifdef OLED_DISPLAY gfx.draw_evil(); oled_display.clearDisplay(); diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index d55f9324e..8ebcafeb0 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -67,7 +67,7 @@ bool MDTrack::get_track_from_pattern(int tracknumber, uint8_t column) { locks[n].step = s; DEBUG_PRINTLN("storing lock"); - locks[n].param_number = i; + locks[n].param_number = i; DEBUG_PRINTLN(locks[n].param_number); locks[n].value = MD.pattern.locks[idx][s]; n++; @@ -82,11 +82,10 @@ bool MDTrack::get_track_from_pattern(int tracknumber, uint8_t column) { arraysize = n; DEBUG_PRINTLN(arraysize); - + patternOrigPosition = MD.pattern.origPosition; } - bool MDTrack::get_track_from_sysex(int tracknumber, uint8_t column) { active = MD_TRACK_TYPE; @@ -116,11 +115,17 @@ void MDTrack::place_track_in_kit(int tracknumber, uint8_t column, MDKit *kit, memcpy(&(kit->lfos[tracknumber]), &machine.lfo, sizeof(machine.lfo)); - if ((machine.trigGroup < 16) && (machine.trigGroup != column)) { kit->trigGroups[tracknumber] = machine.trigGroup; } - else { kit->trigGroups[tracknumber] = 255; } + if ((machine.trigGroup < 16) && (machine.trigGroup != column)) { + kit->trigGroups[tracknumber] = machine.trigGroup; + } else { + kit->trigGroups[tracknumber] = 255; + } - if ((machine.muteGroup < 16) && (machine.muteGroup != column)) { kit->muteGroups[tracknumber] = machine.muteGroup; } - else { kit->muteGroups[tracknumber] = 255; } + if ((machine.muteGroup < 16) && (machine.muteGroup != column)) { + kit->muteGroups[tracknumber] = machine.muteGroup; + } else { + kit->muteGroups[tracknumber] = 255; + } } void MDTrack::init() { @@ -215,7 +220,7 @@ bool MDTrack::load_track_from_grid(int32_t column, int32_t row, int32_t len) { } if (active == EMPTY_TRACK_TYPE) { - seq_data.length = 16; + seq_data.length = 16; } return true; @@ -312,8 +317,9 @@ void MDTrack::normalize() { scale_seq_vol(scale); } -bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool storepattern, - uint8_t merge, bool online) { +bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, + bool storepattern, uint8_t merge, + bool online) { active = MD_TRACK_TYPE; bool ret; @@ -329,39 +335,43 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s return false; } - if (track != 255) { - if ((storepattern) || (merge > 0)) { get_track_from_pattern(track, column); } + if (track != 255 && online == true) { + if ((storepattern) || (merge > 0)) { + get_track_from_pattern(track, column); + } get_track_from_kit(track, column); - } - if (merge > 0 && online) { - DEBUG_PRINTLN("auto merge"); - MDSeqTrack md_seq_track; - if (merge == SAVE_MERGE) { - //Load up internal sequencer data - memcpy(&(md_seq_track), &(mcl_seq.md_tracks[track]), sizeof(MDSeqTrackData)); + if (merge > 0) { + DEBUG_PRINTLN("auto merge"); + MDSeqTrack md_seq_track; + if (merge == SAVE_MERGE) { + // Load up internal sequencer data + memcpy(&(md_seq_track), &(mcl_seq.md_tracks[track]), + sizeof(MDSeqTrackData)); + } + if (merge == SAVE_MD) { + md_seq_track.init(); + seq_data.length = length; + } + // merge md pattern data with seq_data + md_seq_track.merge_from_md(this); + // copy merged data in to this track object's seq data for writing to SD + memcpy(&(this->seq_data), &(md_seq_track), sizeof(MDSeqTrackData)); + } else { + memcpy(&(this->seq_data), &(mcl_seq.md_tracks[track]), + sizeof(MDSeqTrackData)); } - if (merge == SAVE_MD) { - md_seq_track.init(); - seq_data.length = length; + // Legacy, we no longer store the MD data. + if (!storepattern) { + clear_track(); } - //merge md pattern data with seq_data - md_seq_track.merge_from_md(this); - //copy merged data in to this track object's seq data for writing to SD - memcpy(&(this->seq_data), &(md_seq_track), sizeof(MDSeqTrackData)); - } - else { - memcpy(&(this->seq_data), &(mcl_seq.md_tracks[track]), sizeof(MDSeqTrackData)); - } - //Legacy, we no longer store the MD data. - if (!storepattern) { clear_track(); } - //Normalise track levels - if (mcl_cfg.auto_normalize == 1) { - normalize(); + // Normalise track levels + if (mcl_cfg.auto_normalize == 1) { + normalize(); + } } - - //Write data to sd + // Write data to sd len = sizeof(MDTrack) - (LOCK_AMOUNT * 3); DEBUG_PRINTLN(len); ret = mcl_sd.write_data((uint8_t *)(this), len, &proj.file); @@ -370,13 +380,13 @@ bool MDTrack::store_track_in_grid(int32_t column, int32_t row, int track, bool s return false; } if (storepattern) { - ret = mcl_sd.write_data((uint8_t *)&(this->locks[0]), arraysize * 3, - &proj.file); + ret = mcl_sd.write_data((uint8_t *)&(this->locks[0]), arraysize * 3, + &proj.file); - if (!ret) { - DEBUG_PRINTLN("write failed"); - return false; - } + if (!ret) { + DEBUG_PRINTLN("write failed"); + return false; + } } uint8_t model = machine.model; grid_page.row_headers[grid_page.cur_row].update_model(column, model, diff --git a/avr/cores/megacommand/MCL/MDTrack.h b/avr/cores/megacommand/MCL/MDTrack.h index 2a58688df..95f9cdfdf 100644 --- a/avr/cores/megacommand/MCL/MDTrack.h +++ b/avr/cores/megacommand/MCL/MDTrack.h @@ -72,6 +72,10 @@ class MDTrack : public MDTrackLight { int arraysize; ParameterLock locks[LOCK_AMOUNT]; + MDTrack() { + arraysize = 0; + } + void init(); void clear_track(); From 13c5571846f2c2563a4d0ba2e37cd7ec6fb009f9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 14 Dec 2019 14:45:29 +1100 Subject: [PATCH 458/469] prevent save_mode encoder changing when sequencer is running --- avr/cores/megacommand/MCL/GridSavePage.cpp | 9 +++++++++ avr/cores/megacommand/MCL/GridSavePage.h | 1 + 2 files changed, 10 insertions(+) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index b61c06904..434aac98a 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -18,6 +18,15 @@ void GridSavePage::init() { #endif } +void GridSavePage::loop() { + + //prevent encoder 0 from changing when sequencer is running + if (encoders[0]->hasChanged() && MidiClock.state == 2) { + encoders[0]->cur = encoders[0]->old; + } + +} + void GridSavePage::cleanup() {} #ifndef OLED_DISPLAY diff --git a/avr/cores/megacommand/MCL/GridSavePage.h b/avr/cores/megacommand/MCL/GridSavePage.h index 04c91ba47..af3612b04 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.h +++ b/avr/cores/megacommand/MCL/GridSavePage.h @@ -12,6 +12,7 @@ class GridSavePage : public GridIOPage { Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : GridIOPage(e1, e2, e3, e4) {} bool handleEvent(gui_event_t *event); + void loop(); void display(); void setup(); void init(); From 64d26f06147d6009f9c82845f33d8d7ac7d8cb9d Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 14 Dec 2019 16:00:52 +1100 Subject: [PATCH 459/469] Fix graphics glitch on save, retain save_mode on save --- .../megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp | 2 +- .../megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index 8be31cc2b..4323e1496 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -538,7 +538,7 @@ void Adafruit_SSD1305::data(uint8_t c) { void Adafruit_SSD1305::textbox(char *text, char *text2, uint16_t delay) { textbox_clock = slowclock; strncpy(textbox_str, text, sizeof(textbox_str)); - strncpy(textbox_str2, text2, sizeof(textbox_str)); + strncpy(textbox_str2, text2, sizeof(textbox_str2)); textbox_delay = delay; textbox_enabled = true; } diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 4908900ce..d8e7be9aa 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -136,8 +136,8 @@ class Adafruit_SSD1305 : public Adafruit_GFX { bool textbox_enabled = false; uint16_t textbox_delay; uint16_t textbox_clock; - char textbox_str[12]; - char textbox_str2[12]; + char textbox_str[16]; + char textbox_str2[16]; uint8_t _i2caddr; int8_t sid, sclk, dc, rst, cs; void spixfer(uint8_t x); From dc8f1d607739fb13371ee4b2e50ed1c39dccb6b1 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 14 Dec 2019 20:12:07 +1100 Subject: [PATCH 460/469] Add while loop when exiting page select, to pause to wait for kit receive This should prevent kit not being received if exiting page select too quickly. --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index df0ae5ed1..3d9297a45 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -174,6 +174,8 @@ void PageSelectPage::md_prepare() { void PageSelectPage::cleanup() { #ifndef USE_BLOCKINGKIT + uint16_t myclock = slowclock; + while (!kit_cb.received && (clock_diff(myclock, slowclock) < 400)); if (kit_cb.received) { MD.kit.fromSysex(MD.midi); if (MidiClock.state == 2) { From 5516b2c5612fcf36f74e858c4661506c05933d35 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 14 Dec 2019 20:28:32 +1100 Subject: [PATCH 461/469] Allow for one loop, before requesting kit This will prevent a brief GUI freeze if shift 1 is accidently pressed quickly. --- avr/cores/megacommand/MCL/PageSelectPage.cpp | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 3d9297a45..608d48ad7 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -135,15 +135,10 @@ void PageSelectPage::init() { } classic_display = false; #endif - bool switch_tracks = false; if (!md_exploit.state) { last_md_track = MD.getCurrentTrack(CALLBACK_TIMEOUT); } - md_exploit.off(switch_tracks); - md_prepare(); - md_exploit.on(switch_tracks); - note_interface.state = true; - + loop_init = true; } void PageSelectPage::md_prepare() { @@ -175,11 +170,14 @@ void PageSelectPage::md_prepare() { void PageSelectPage::cleanup() { #ifndef USE_BLOCKINGKIT uint16_t myclock = slowclock; - while (!kit_cb.received && (clock_diff(myclock, slowclock) < 400)); - if (kit_cb.received) { - MD.kit.fromSysex(MD.midi); - if (MidiClock.state == 2) { - mcl_seq.update_kit_params(); + if (!loop_init) { + while (!kit_cb.received && (clock_diff(myclock, slowclock) < 400)) + ; + if (kit_cb.received) { + MD.kit.fromSysex(MD.midi); + if (MidiClock.state == 2) { + mcl_seq.update_kit_params(); + } } } MDSysexListener.removeOnKitMessageCallback(&kit_cb); @@ -238,6 +236,15 @@ uint8_t PageSelectPage::get_category_page(uint8_t offset) { } void PageSelectPage::loop() { + if (loop_init) { + bool switch_tracks = false; + md_exploit.off(switch_tracks); + md_prepare(); + md_exploit.on(switch_tracks); + note_interface.state = true; + loop_init = false; + } + auto enc_ = (MCLEncoder *)encoders[0]; int8_t diff = enc_->cur - enc_->old; if ((diff > 0) && (page_select < 16)) { From 949d672d15b1ab74a07c81d3f3e048a48fda6fd1 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 14 Dec 2019 21:07:36 +1100 Subject: [PATCH 462/469] Don't retransmit lfo value if it is the same as previous --- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 28 +++++++++++++---------- avr/cores/megacommand/MCL/LFOSeqTrack.h | 6 +++-- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index b42e92886..f849cb750 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -63,7 +63,7 @@ uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { } break; case LFO_OFFSET_MAX: - //val = 127 - sample; + // val = 127 - sample; val = offset - depth + sample; if (val > 127) { return 127; @@ -86,17 +86,21 @@ void LFOSeqTrack::seq() { } if (enable) { for (uint8_t i = 0; i < NUM_LFO_PARAMS; i++) { - - if (params[i].dest > 0) { - // MD CC LFO - if (params[i].dest <= NUM_MD_TRACKS) { - MD.setTrackParam_inline(params[i].dest - 1, params[i].param, - get_wav_value(sample_count, i)); - } - // MD FX LFO - else { - MD.sendFXParam(params[i].param, get_wav_value(sample_count, i), - MD_FX_ECHO + params[i].dest - NUM_MD_TRACKS - 1); + uint8_t wav_value = get_wav_value(sample_count, i); + if (last_wav_value[i] != wav_value) { + + if (params[i].dest > 0) { + // MD CC LFO + if (params[i].dest <= NUM_MD_TRACKS) { + MD.setTrackParam_inline(params[i].dest - 1, params[i].param, + wav_value); + } + // MD FX LFO + else { + MD.sendFXParam(params[i].param, wav_value, + MD_FX_ECHO + params[i].dest - NUM_MD_TRACKS - 1); + } + last_wav_value[i] = wav_value; } } } diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.h b/avr/cores/megacommand/MCL/LFOSeqTrack.h index ba1f40c93..ba94ce45b 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.h +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.h @@ -39,8 +39,9 @@ class LFOSeqTrack { public: uint8_t track_number; uint8_t wav_type; - uint8_t wav_table[2][WAV_LENGTH]; - bool wav_table_state[2]; + uint8_t wav_table[NUM_LFO_PARAMS][WAV_LENGTH]; + bool wav_table_state[NUM_LFO_PARAMS]; + uint8_t last_wav_value[NUM_LFO_PARAMS]; uint8_t sample_count; uint8_t sample_hold = 0; @@ -68,6 +69,7 @@ class LFOSeqTrack { void check_and_update_params_offset(uint8_t dest, uint8_t param, uint8_t value); void init() { for (uint8_t a = 0; a < NUM_LFO_PARAMS; a++) { + last_wav_value[a] = 255; params[a].dest = 255; } } From c07d3c4be7de98c630956507df3dec4e11009279 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sat, 14 Dec 2019 21:18:29 +1100 Subject: [PATCH 463/469] Fix overflow for lfo values --- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index f849cb750..a1bc6ad88 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -48,7 +48,7 @@ uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { int8_t offset = params[param].offset; int8_t depth = params[param].depth; int8_t sample = wav_table[param][sample_count]; - int8_t val; + uint16_t val; switch (offset_behaviour) { case LFO_OFFSET_CENTRE: @@ -59,7 +59,7 @@ uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { if (val < 0) { return 0; } else { - return val; + return (uint8_t)val; } break; case LFO_OFFSET_MAX: @@ -71,7 +71,7 @@ uint8_t LFOSeqTrack::get_wav_value(uint8_t sample_count, uint8_t param) { if (val < 0) { return 0; } else { - return val; + return (uint8_t)val; } break; } From ae3fe13eecddd66066b07e1b82c2ba4bec7730f9 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 15 Dec 2019 12:01:00 +1100 Subject: [PATCH 464/469] Fix bug in filebrowser where additional entry would be displayed --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 0eccce002..6d9be5806 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -53,7 +53,6 @@ void FileBrowserPage::init() { if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { add_entry(".."); } - encoders[1]->cur = 1; // iterate through the files while (file.openNext(SD.vwd(), O_READ) && (numEntries < MAX_ENTRIES)) { @@ -96,6 +95,9 @@ void FileBrowserPage::init() { ((MCLEncoder *)encoders[1])->max = 0; } ((MCLEncoder *)encoders[1])->max = numEntries - 1; + encoders[1]->cur = 1; + encoders[1]->old = 1; + cur_row = 1; DEBUG_PRINTLN("finished list files"); } From 117464db13a2c4c55dfa7b6a3344fefefdf5b7b2 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 15 Dec 2019 12:15:11 +1100 Subject: [PATCH 465/469] regression: filebrowser match was being overlooked --- avr/cores/megacommand/MCL/FileBrowserPage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index 6d9be5806..39a083a53 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -53,6 +53,9 @@ void FileBrowserPage::init() { if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { add_entry(".."); } + encoders[1]->cur = 1; + encoders[1]->old = 1; + cur_row = 1; // iterate through the files while (file.openNext(SD.vwd(), O_READ) && (numEntries < MAX_ENTRIES)) { @@ -95,9 +98,6 @@ void FileBrowserPage::init() { ((MCLEncoder *)encoders[1])->max = 0; } ((MCLEncoder *)encoders[1])->max = numEntries - 1; - encoders[1]->cur = 1; - encoders[1]->old = 1; - cur_row = 1; DEBUG_PRINTLN("finished list files"); } @@ -182,7 +182,7 @@ void FileBrowserPage::draw_scrollbar(uint8_t x_offset) { void FileBrowserPage::loop() { #ifndef OLED_DISPLAY - if (call_handle_filemenu) { + if (call_handle_filemenu) { call_handle_filemenu = false; _handle_filemenu(); } From fee56e88942d0fa7f2bc40341ff708205b46357e Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 15 Dec 2019 18:02:38 +1100 Subject: [PATCH 466/469] Fix bug: occasional latency on step edit --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 38 ++++++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 533008363..82b4479fe 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -8,8 +8,14 @@ void SeqStepPage::setup() { SeqPage::setup(); } void SeqStepPage::config() { seq_param3.cur = mcl_seq.md_tracks[last_md_track].length; tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); + seq_param4.cur = 0; + seq_param4.old = 0; + if (tuning) { seq_param4.max = tuning->len - 1; - + } + else { + seq_param4.max = 1; + } // config info labels const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); const char *str2 = getMachineNameShort(MD.kit.models[last_md_track], 2); @@ -205,8 +211,10 @@ void SeqStepPage::loop() { if (seq_param1.hasChanged() || seq_param2.hasChanged() || seq_param4.hasChanged()) { + DEBUG_PRINTLN("has changed"); tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); - auto &active_track = mcl_seq.md_tracks[last_md_track]; + + MDSeqTrack &active_track = mcl_seq.md_tracks[last_md_track]; for (uint8_t n = 0; n < 16; n++) { @@ -235,6 +243,9 @@ void SeqStepPage::loop() { } } } + seq_param1.old = seq_param1.cur; + seq_param2.old = seq_param2.cur; + seq_param4.old = seq_param4.cur; } } @@ -244,7 +255,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { return true; } - auto &active_track = mcl_seq.md_tracks[last_md_track]; + MDSeqTrack &active_track = mcl_seq.md_tracks[last_md_track]; if (note_interface.is_event(event)) { uint8_t mask = event->mask; @@ -257,6 +268,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_PRESSED) { if (device == DEVICE_A4) { // GUI.setPage(&seq_extstep_page); + return true; } show_pitch = true; @@ -301,6 +313,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { if (device == DEVICE_A4) { // GUI.setPage(&seq_extstep_page); + setLed2(); return true; } @@ -308,7 +321,10 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { show_pitch = false; } if (step >= active_track.length) { - return true; + DEBUG_PRINTLN("bad length"); + DEBUG_PRINTLN(active_track.length); + DEBUG_PRINTLN(step); + return true; } /* uint8_t utiming = (seq_param2.cur + 0); @@ -340,13 +356,25 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { uint8_t utiming = (seq_param2.cur + 0); uint8_t condition = seq_param1.cur; + DEBUG_PRINTLN("settting"); + DEBUG_PRINTLN(last_md_track); + active_track.conditional[step] = condition; active_track.timing[step] = utiming; // upper // active_track.clear_step_locks(step); CLEAR_BIT64(active_track.oneshot_mask, step); SET_BIT64(active_track.pattern_mask, step); } else { - DEBUG_PRINTLN("Trying to clear"); + DEBUG_PRINTLN("clear step"); + /* + DEBUG_DUMP(step); + Serial.println(); +# for (uint8_t n = 0; n < 64; n++) { + if (IS_BIT_SET64(active_track.pattern_mask, n)) { Serial.print(1); } + else { Serial.print(0); } + } + Serial.println(); + */ if (clock_diff(note_interface.note_hold, slowclock) < TRIG_HOLD_TIME) { CLEAR_BIT64(active_track.pattern_mask, step); active_track.conditional[step] = 0; From cb3502e3f0bba0025b0bd44c93b4be2cd4da729b Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Sun, 15 Dec 2019 18:05:49 +1100 Subject: [PATCH 467/469] remove some debug --- avr/cores/megacommand/MCL/SeqStepPage.cpp | 25 ++++++++--------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 82b4479fe..049a10b71 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -11,10 +11,9 @@ void SeqStepPage::config() { seq_param4.cur = 0; seq_param4.old = 0; if (tuning) { - seq_param4.max = tuning->len - 1; - } - else { - seq_param4.max = 1; + seq_param4.max = tuning->len - 1; + } else { + seq_param4.max = 1; } // config info labels const char *str1 = getMachineNameShort(MD.kit.models[last_md_track], 1); @@ -211,7 +210,6 @@ void SeqStepPage::loop() { if (seq_param1.hasChanged() || seq_param2.hasChanged() || seq_param4.hasChanged()) { - DEBUG_PRINTLN("has changed"); tuning_t const *tuning = MD.getModelTuning(MD.kit.models[last_md_track]); MDSeqTrack &active_track = mcl_seq.md_tracks[last_md_track]; @@ -243,9 +241,9 @@ void SeqStepPage::loop() { } } } - seq_param1.old = seq_param1.cur; - seq_param2.old = seq_param2.cur; - seq_param4.old = seq_param4.cur; + seq_param1.old = seq_param1.cur; + seq_param2.old = seq_param2.cur; + seq_param4.old = seq_param4.cur; } } @@ -268,7 +266,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_PRESSED) { if (device == DEVICE_A4) { // GUI.setPage(&seq_extstep_page); - + return true; } show_pitch = true; @@ -313,7 +311,6 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { if (device == DEVICE_A4) { // GUI.setPage(&seq_extstep_page); - setLed2(); return true; } @@ -321,10 +318,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { show_pitch = false; } if (step >= active_track.length) { - DEBUG_PRINTLN("bad length"); - DEBUG_PRINTLN(active_track.length); - DEBUG_PRINTLN(step); - return true; + return true; } /* uint8_t utiming = (seq_param2.cur + 0); @@ -357,8 +351,7 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { uint8_t condition = seq_param1.cur; DEBUG_PRINTLN("settting"); - DEBUG_PRINTLN(last_md_track); - + active_track.conditional[step] = condition; active_track.timing[step] = utiming; // upper // active_track.clear_step_locks(step); From 5ee9c14a2a60fb5151d09e85f2b47bc31ba89b75 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 17 Dec 2019 16:32:36 +1100 Subject: [PATCH 468/469] Retain save-mode on save. missing commit --- avr/cores/megacommand/MCL/GridSavePage.cpp | 91 +++++++++++----------- avr/cores/megacommand/MCL/PageSelectPage.h | 1 + 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index 434aac98a..c171cc629 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -20,11 +20,10 @@ void GridSavePage::init() { void GridSavePage::loop() { - //prevent encoder 0 from changing when sequencer is running - if (encoders[0]->hasChanged() && MidiClock.state == 2) { - encoders[0]->cur = encoders[0]->old; - } - + // prevent encoder 0 from changing when sequencer is running + if (encoders[0]->hasChanged() && MidiClock.state == 2) { + encoders[0]->cur = encoders[0]->old; + } } void GridSavePage::cleanup() {} @@ -47,19 +46,19 @@ void GridSavePage::display() { GUI.setLine(GUI.LINE2); GUI.put_string_at_fill(0, "S"); - const char *merge = "SEQ"; + const char *save_mode = "SEQ"; if (MidiClock.state == 2) { - merge = "---"; + save_mode = "---"; } else { if (encoders[0]->cur == SAVE_MD) { - merge = "MD"; + save_mode = "MD"; } if (encoders[0]->cur == SAVE_MERGE) { - merge = "MERGE"; + save_mode = "MERGE"; } } - GUI.put_string_at(3, merge); + GUI.put_string_at(3, save_mode); uint8_t step_count = (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - @@ -111,18 +110,16 @@ void GridSavePage::display() { mcl_gui.draw_horizontal_arrow(data_x + 16, MCLGUI::s_menu_y + 12, 5); } else { if (encoders[0]->cur == SAVE_SEQ || MidiClock.state == 2) { - oled_display.setCursor(data_x, MCLGUI::s_menu_y + 12); - oled_display.print("SND"); - oled_display.setCursor(data_x, MCLGUI::s_menu_y + 19); - oled_display.print("SEQ"); - - oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 8, 2, WHITE); - oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 15, 2, - WHITE); - oled_display.drawFastVLine(data_x + 15, MCLGUI::s_menu_y + 8, 8, WHITE); - mcl_gui.draw_horizontal_arrow(data_x + 16, MCLGUI::s_menu_y + 12, 5); - } - else { + oled_display.setCursor(data_x, MCLGUI::s_menu_y + 12); + oled_display.print("SND"); + oled_display.setCursor(data_x, MCLGUI::s_menu_y + 19); + oled_display.print("SEQ"); + + oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 8, 2, WHITE); + oled_display.drawFastHLine(data_x + 13, MCLGUI::s_menu_y + 15, 2, WHITE); + oled_display.drawFastVLine(data_x + 15, MCLGUI::s_menu_y + 8, 8, WHITE); + mcl_gui.draw_horizontal_arrow(data_x + 16, MCLGUI::s_menu_y + 12, 5); + } else { oled_display.setCursor(data_x, MCLGUI::s_menu_y + 15); oled_display.print(modestr); mcl_gui.draw_horizontal_arrow(data_x + 13, MCLGUI::s_menu_y + 12, 8); @@ -137,6 +134,15 @@ void GridSavePage::display() { #endif bool GridSavePage::handleEvent(gui_event_t *event) { + const char *modestr = "SEQ"; + if (MidiClock.state != 2) { + if (encoders[0]->cur == SAVE_MD) { + modestr = "MD"; + } + if (encoders[0]->cur == SAVE_MERGE) { + modestr = "MERGE"; + } + } if (note_interface.is_event(event)) { if (note_interface.notes_all_off()) { @@ -145,24 +151,22 @@ bool GridSavePage::handleEvent(gui_event_t *event) { } else { uint16_t last_clock = slowclock; - const char *modestr = "SEQ"; + #ifdef OLED_DISPLAY oled_display.textbox("SAVE SLOTS: ", modestr); oled_display.display(); #endif - if (MidiClock.state != 2) { - if (encoders[0]->cur == SAVE_MD) { - modestr = "MD"; - } - if (encoders[0]->cur == SAVE_MERGE) { - modestr = "MERGE"; - } - } md_exploit.off(); - uint8_t merge = encoders[0]->cur; + + uint8_t save_mode = encoders[0]->cur; + if (MidiClock.state == 2) { + encoders[0]->cur = SAVE_SEQ; + encoders[0]->old = SAVE_SEQ; + save_mode = SAVE_SEQ; + } mcl_actions.store_tracks_in_mem(0, grid_page.encoders[1]->getValue(), - merge); + save_mode); } GUI.setPage(&grid_page); @@ -175,15 +179,6 @@ bool GridSavePage::handleEvent(gui_event_t *event) { } if (EVENT_RELEASED(event, Buttons.BUTTON3)) { - const char *modestr = "SEQ"; - if (MidiClock.state != 2) { - if (encoders[0]->cur == SAVE_MD) { - modestr = "MD"; - } - if (encoders[0]->cur == SAVE_MERGE) { - modestr = "MERGE"; - } - } #ifdef OLED_DISPLAY oled_display.textbox("SAVE PAT: ", modestr); oled_display.display(); @@ -196,9 +191,17 @@ bool GridSavePage::handleEvent(gui_event_t *event) { note_interface.notes[i] = 3; } - uint8_t merge = encoders[0]->cur; + + uint8_t save_mode = encoders[0]->cur; + if (MidiClock.state == 2) { + encoders[0]->cur = SAVE_SEQ; + encoders[0]->old = SAVE_SEQ; + save_mode = SAVE_SEQ; + } + mcl_actions.store_tracks_in_mem(grid_page.encoders[0]->getValue(), - grid_page.encoders[1]->getValue(), merge); + grid_page.encoders[1]->getValue(), + save_mode); GUI.setPage(&grid_page); curpage = 0; return true; diff --git a/avr/cores/megacommand/MCL/PageSelectPage.h b/avr/cores/megacommand/MCL/PageSelectPage.h index 5ea06a6b3..2e6c1e462 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.h +++ b/avr/cores/megacommand/MCL/PageSelectPage.h @@ -14,6 +14,7 @@ class PageSelectPage : public LightPage { #ifndef USE_BLOCKINGKIT MDCallback kit_cb; #endif + bool loop_init = false; uint8_t page_select; PageSelectPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) From 4e1c909f93dad371aca48a1c04f3208e135e1d74 Mon Sep 17 00:00:00 2001 From: Justin Mammarella Date: Tue, 17 Dec 2019 21:07:51 +1100 Subject: [PATCH 469/469] LFO one mode would not hold at last value --- avr/cores/megacommand/MCL/LFOSeqTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp index a1bc6ad88..c3d01ed35 100644 --- a/avr/cores/megacommand/MCL/LFOSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/LFOSeqTrack.cpp @@ -118,7 +118,7 @@ void LFOSeqTrack::seq() { if (sample_count > LFO_LENGTH) { // Free running LFO should reset, oneshot should hold at last value. if (mode == LFO_MODE_ONE) { - sample_count = LFO_LENGTH; + sample_count = LFO_LENGTH - 1; } else { sample_count = 0; }