diff --git a/Changelog b/Changelog index 7400057ad..2c2695493 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,20 @@ +MCL 2.61 16/03/2020 + +Changes: + + - Arpeggitaor added to Chromatic Page. Accessible from Track Select Menu. + - MIDI CC can be used to control MD Parms across poly tracks. + CC 16 to 40 controls MD Params 1 to 24. + - Added many more scales to Chromatic Page + - Chromatic Page now indicates when in POLY mode. + - Added support for TRX-S2 + +Bug Fixes: + + - Peering was broken in certain pages. Fixed + - Fixed PolyPage selection bugs. + + MCL 2.60 15/02/2019 Note: MCL update 2.60, requires that you upgrade your Machinedrum to OS version 1.70. diff --git a/art/sprites/ram_1_icon.png b/art/sprites/ram_1_icon.png new file mode 100644 index 000000000..980823fc7 Binary files /dev/null and b/art/sprites/ram_1_icon.png differ diff --git a/art/sprites/ram_2_icon.png b/art/sprites/ram_2_icon.png new file mode 100644 index 000000000..1d5819a7d Binary files /dev/null and b/art/sprites/ram_2_icon.png differ diff --git a/avr/cores/megacommand/MCL/ArpPage.cpp b/avr/cores/megacommand/MCL/ArpPage.cpp new file mode 100644 index 000000000..1c4d92f68 --- /dev/null +++ b/avr/cores/megacommand/MCL/ArpPage.cpp @@ -0,0 +1,156 @@ +#include "ArpPage.h" +#include "MCL.h" + +MCLEncoder arp_oct(0, 3, ENCODER_RES_SEQ); +MCLEncoder arp_mode(0, 17, ENCODER_RES_SEQ); +MCLEncoder arp_speed(0, 3, ENCODER_RES_SEQ); +MCLEncoder arp_und(0, 2, ENCODER_RES_SEQ); + +void ArpPage::setup() {} + +void ArpPage::init() { + + DEBUG_PRINT_FN(); +#ifdef OLED_DISPLAY + classic_display = false; + oled_display.setFont(); + seq_ptc_page.redisplay = true; + seq_ptc_page.display(); +#endif +} + +void ArpPage::cleanup() { + // md_exploit.off(); +#ifdef OLED_DISPLAY + oled_display.clearDisplay(); +#endif +} + +void ArpPage::loop() { + if (encoders[0]->hasChanged()) { + switch (encoders[0]->cur) { + case ARP_ON: + seq_ptc_page.setup_arp(); + break; + case ARP_OFF: + seq_ptc_page.remove_arp(); + break; + } + if (encoders[0]->old > 1) { + seq_ptc_page.note_mask = 0; + seq_ptc_page.render_arp(); + } + } + if (encoders[1]->hasChanged() || encoders[2]->hasChanged() || + encoders[3]->hasChanged()) { + seq_ptc_page.render_arp(); + } +} + +typedef char arp_name_t[4]; + +const arp_name_t arp_names[] PROGMEM = { + "UP", "DWN", "UD", "DU", "UND", "DNU", "CNV", "DIV", "CND", + "PU", "PD", "TU", "TD", "UPP", "DP", "U2", "D2", "RND", +}; + +#ifndef OLED_DISPLAY +void ArpPage::display() { + uint8_t dev_num; + if (!redisplay) { + return true; + } + GUI.setLine(GUI.LINE1); + + GUI.put_string_at(0, "ARP"); + GUI.put_string_at(4, "MOD"); + GUI.put_string_at(8, "SPD"); + GUI.put_string_at(12,"OCT"); + + GUI.setLine(GUI.LINE2); + char str[5]; + + switch (encoders[0]->cur) { + case ARP_ON: + strcpy(str, "ON"); + break; + case ARP_OFF: + strcpy(str, "--"); + break; + case ARP_LATCH: + strcpy(str, "LAT"); + break; + } + GUI.put_string_at(0, str); + m_strncpy_p(str, arp_names[encoders[1]->cur], 4); + GUI.put_string_at(4,str); + GUI.put_value_at2(8, encoders[2]->cur); + GUI.put_value_at2(12, encoders[3]->cur); +} +#else +void ArpPage::display() { + + if (!classic_display) { + } + auto oldfont = oled_display.getFont(); + oled_display.setFont(&TomThumb); + + 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(42, 10); + + oled_display.setTextColor(WHITE); + oled_display.print("ARPEGGIATOR"); + char str[5]; + uint8_t y = 12; + uint8_t x = 16; + + switch (encoders[0]->cur) { + case ARP_ON: + strcpy(str, "ON"); + break; + case ARP_OFF: + strcpy(str, "--"); + break; + case ARP_LATCH: + strcpy(str, "LAT"); + break; + } + mcl_gui.draw_text_encoder(x + 0 * mcl_gui.knob_w, y, "ARP", str); + + m_strncpy_p(str, arp_names[encoders[1]->cur], 4); + + mcl_gui.draw_text_encoder(x + 1 * mcl_gui.knob_w, y, "MODE", str); + + itoa(encoders[2]->cur, str, 10); + mcl_gui.draw_text_encoder(x + 2 * mcl_gui.knob_w, y, "SPD", str); + + itoa(encoders[3]->cur, str, 10); + mcl_gui.draw_text_encoder(x + 3 * mcl_gui.knob_w, y, "OCT", str); + + oled_display.display(); + oled_display.setFont(oldfont); +} + +#endif +bool ArpPage::handleEvent(gui_event_t *event) { + if (EVENT_PRESSED(event, Buttons.BUTTON1) || + EVENT_PRESSED(event, Buttons.BUTTON3) || + EVENT_PRESSED(event, Buttons.BUTTON2) || + EVENT_PRESSED(event, Buttons.BUTTON4)) { + GUI.ignoreNextEvent(event->source); + GUI.popPage(); + return true; + } + + seq_ptc_page.handleEvent(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; + } + } + return false; +} diff --git a/avr/cores/megacommand/MCL/ArpPage.h b/avr/cores/megacommand/MCL/ArpPage.h new file mode 100644 index 000000000..e74110225 --- /dev/null +++ b/avr/cores/megacommand/MCL/ArpPage.h @@ -0,0 +1,32 @@ +/* Justin Mammarella jmamma@gmail.com 2018 */ + +#ifndef ARPPAGE_H__ +#define ARPPAGE_H__ + +//#include "Pages.hh" +#include "GUI.h" +#include "MCLEncoder.h" + +extern MCLEncoder arp_speed; +extern MCLEncoder arp_oct; +extern MCLEncoder arp_mode; +extern MCLEncoder arp_und; + +class ArpPage : public LightPage { +public: + + ArpPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, + Encoder *e4 = NULL) + : LightPage(e1, e2, e3, e4) { + } + + bool handleEvent(gui_event_t *event); + + void loop(); + void display(); + void setup(); + void init(); + void cleanup(); +}; + +#endif /* ARPPAGE_H__ */ diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index d077e5fe0..bf35eeef6 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -91,8 +91,8 @@ #include "Fonts/Elektrothic.h" #endif -#define VERSION 2060 -#define VERSION_STR "2.60" +#define VERSION 2061 +#define VERSION_STR "2.61" #define CALLBACK_TIMEOUT 500 #define GUI_NAME_TIMEOUT 800 diff --git a/avr/cores/megacommand/MCL/MCLActions.cpp b/avr/cores/megacommand/MCL/MCLActions.cpp index 7676b96ea..969ce3cb9 100644 --- a/avr/cores/megacommand/MCL/MCLActions.cpp +++ b/avr/cores/megacommand/MCL/MCLActions.cpp @@ -362,12 +362,12 @@ void MCLActions::send_tracks_to_devices() { memcpy(&MD.kit.dynamics[0], kit_extra.dynamics, sizeof(kit_extra.dynamics)); } - MD.kit.origPosition = 0xF7; + MD.kit.origPosition = 0x7F; /*Send the encoded kit to the MD via sysex*/ uint16_t myclock = slowclock; - md_setsysex_recpos(4, MD.kit.origPosition); + //md_setsysex_recpos(4, MD.kit.origPosition); MD.kit.toSysex(); // mcl_seq.disable(); diff --git a/avr/cores/megacommand/MCL/MCLGUI.cpp b/avr/cores/megacommand/MCL/MCLGUI.cpp index 1f23d9e8d..9991f21fc 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.cpp +++ b/avr/cores/megacommand/MCL/MCLGUI.cpp @@ -439,18 +439,21 @@ void MCLGUI::draw_microtiming(uint8_t resolution, uint8_t timing) { oled_display.setFont(&TomThumb); oled_display.setTextColor(WHITE); - if (resolution == 0) { resolution = 1; } - if (resolution > 2) { resolution = 1; } + 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 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; @@ -459,23 +462,27 @@ void MCLGUI::draw_microtiming(uint8_t resolution, uint8_t timing) { uint8_t x_w = (w / (degrees)); uint8_t x = x_pos; - oled_display.fillRect(8,2,128 - 16, 32 - 2,BLACK); + 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); + + 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); - } + 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; + if (a == degrees / 2) { + a = 0; + } + x += x_w; } oled_display.setFont(oldfont); #endif @@ -650,9 +657,9 @@ 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) { - // TI feedback - // oled_display.drawRect(x, y, seq_w, led_h, WHITE); + // if (note_interface.notes[i] == 1) { + // TI feedback + // oled_display.drawRect(x, y, seq_w, led_h, WHITE); if (!in_range) { // don't draw } else if (current ^ locked) { @@ -913,6 +920,25 @@ const unsigned char icon_sound[] PROGMEM = { 0x01, 0xc7, 0xe0, 0x07, 0xc7, 0xc0, 0x0f, 0xc3, 0x80, 0x0f, 0xc0, 0x00, 0x0f, 0x80, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00}; +// 'ram_2_icon2', 24x25px +const unsigned char icon_ram2[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x20, 0x00, + 0x04, 0x20, 0x00, 0x04, 0x20, 0x00, 0x04, 0x23, 0xff, 0xc4, 0x26, + 0x43, 0x24, 0x25, 0x42, 0xa4, 0x24, 0xc2, 0x64, 0x23, 0x81, 0xc4, + 0x20, 0x00, 0x04, 0x20, 0x00, 0x04, 0x20, 0xff, 0x04, 0x1f, 0xff, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x6c, 0xe0, 0x0f, + 0x6d, 0xf0, 0x06, 0x6d, 0xb0, 0x06, 0x7d, 0xb0, 0x06, 0x7d, 0xb0, + 0x06, 0x7d, 0xf0, 0x06, 0x38, 0xe0, 0x00, 0x00, 0x00}; +// 'ram_1_icon', 24x25px +const unsigned char icon_ram1[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x20, 0x00, + 0x04, 0x20, 0x00, 0x04, 0x20, 0x00, 0x04, 0x23, 0xff, 0xc4, 0x24, + 0xc2, 0x64, 0x25, 0x42, 0xa4, 0x26, 0x43, 0x24, 0x23, 0x81, 0xc4, + 0x20, 0x00, 0x04, 0x20, 0x00, 0x04, 0x20, 0xff, 0x04, 0x1f, 0xff, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x38, 0x70, 0x0f, + 0xbc, 0xf0, 0x0d, 0xb6, 0xc0, 0x0d, 0xb6, 0xf0, 0x0d, 0xb6, 0xc0, + 0x0f, 0xb6, 0xf0, 0x07, 0x36, 0x70, 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 ad27cb0ec..ecbf5258a 100644 --- a/avr/cores/megacommand/MCL/MCLGUI.h +++ b/avr/cores/megacommand/MCL/MCLGUI.h @@ -231,6 +231,10 @@ extern const unsigned char icon_para[]; extern const unsigned char icon_step[]; // 'gatebox', 24x25px extern const unsigned char icon_gatebox[]; +// 'ram1', 24x25px +extern const unsigned char icon_ram1[]; +// 'ram2', 24x25px +extern const unsigned char icon_ram2[]; // 'rythmecho', 24x25px extern const unsigned char icon_rhytmecho []; // 'route', 24x16px diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index d1e260d62..41f9564f3 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -337,7 +337,7 @@ void MDSeqTrack::record_track_pitch(uint8_t pitch) { } set_track_pitch(step_count, pitch); } -void MDSeqTrack::record_track(uint8_t note_num, uint8_t velocity) { +void MDSeqTrack::record_track(uint8_t velocity) { /*uint8_t step_count = (MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) - (length * ((MidiClock.div16th_counter - mcl_actions.start_clock32th / 2) @@ -347,10 +347,10 @@ void MDSeqTrack::record_track(uint8_t note_num, uint8_t velocity) { return; } uint8_t utiming = MidiClock.mod12_counter + 12; - set_track_step(step_count, utiming, note_num, velocity); + set_track_step(step_count, utiming, velocity); } -void MDSeqTrack::set_track_step(uint8_t step, uint8_t utiming, uint8_t note_num, +void MDSeqTrack::set_track_step(uint8_t step, uint8_t utiming, uint8_t velocity) { uint8_t condition = 0; diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.h b/avr/cores/megacommand/MCL/MDSeqTrack.h index 450610c20..30b89a046 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.h +++ b/avr/cores/megacommand/MCL/MDSeqTrack.h @@ -28,7 +28,7 @@ class MDSeqTrack : public MDSeqTrackData { uint8_t locks_params_orig[4]; bool load = false; -// uint8_t params[24]; + // uint8_t params[24]; uint8_t trigGroup; uint32_t start_step; @@ -45,12 +45,11 @@ class MDSeqTrack : public MDSeqTrackData { 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, - uint8_t velocity); + void set_track_step(uint8_t step, uint8_t utiming, uint8_t velocity); void set_track_locks(uint8_t step, uint8_t track_param, uint8_t velocity); uint8_t get_track_lock(uint8_t step, uint8_t track_param); - void record_track(uint8_t note_num, uint8_t velocity); + void record_track(uint8_t velocity); void record_track_locks(uint8_t track_param, uint8_t value); void record_track_pitch(uint8_t pitch); void clear_step_locks(uint8_t step); diff --git a/avr/cores/megacommand/MCL/MDTrackSelect.cpp b/avr/cores/megacommand/MCL/MDTrackSelect.cpp index 83932842a..5d362fa63 100644 --- a/avr/cores/megacommand/MCL/MDTrackSelect.cpp +++ b/avr/cores/megacommand/MCL/MDTrackSelect.cpp @@ -7,27 +7,28 @@ void MDTrackSelect::start() { } bool MDTrackSelect::on() { - MD.activate_track_select(); - sysex->addSysexListener(this); if (state) { return false; } if (!MD.connected) { return false; } + MD.activate_track_select(); + sysex->addSysexListener(this); + state = true; return true; } bool MDTrackSelect::off() { - MD.deactivate_track_select(); - sysex->removeSysexListener(this); if (!state) { return false; } if (!MD.connected) { return false; } + MD.deactivate_track_select(); + sysex->removeSysexListener(this); state = false; return true; } diff --git a/avr/cores/megacommand/MCL/MidiActivePeering.cpp b/avr/cores/megacommand/MCL/MidiActivePeering.cpp index 02d4da947..bbee4f049 100644 --- a/avr/cores/megacommand/MCL/MidiActivePeering.cpp +++ b/avr/cores/megacommand/MCL/MidiActivePeering.cpp @@ -35,6 +35,13 @@ void MidiActivePeering::delay_progress(uint16_t clock_) { void MidiActivePeering::md_setup() { DEBUG_PRINT_FN(); + + bool ts = md_track_select.state; + bool ti = trig_interface.state; + + if (ts) { md_track_select.off(); } + if (ti) { trig_interface.off(); } + MidiIDSysexListener.setup(&Midi); MidiUart.set_speed((uint32_t)31250, 1); #ifdef OLED_DISPLAY @@ -100,7 +107,11 @@ void MidiActivePeering::md_setup() { delay(250); } } + MidiIDSysexListener.cleanup(); + if (ts) { md_track_select.on(); } + if (ti) { trig_interface.on(); } + GUI.currentPage()->redisplay = true; #ifdef OLED_DISPLAY oled_display.setFont(oldfont); #endif diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index 776cdb5e7..d2279b8fe 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -46,8 +46,8 @@ const PageSelectEntry Entries[] PROGMEM = { #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}, - {"RAM-2", &ram_page_b, 15, 3, 19, 19, (uint8_t *)wheel_angle}, + {"RAM-1", &ram_page_a, 14, 3, 24, 25, (uint8_t *)icon_ram1}, + {"RAM-2", &ram_page_b, 15, 3, 24, 25, (uint8_t *)icon_ram2}, }; constexpr uint8_t n_category = sizeof(Categories) / sizeof(PageCategory); diff --git a/avr/cores/megacommand/MCL/PolyPage.cpp b/avr/cores/megacommand/MCL/PolyPage.cpp index 2c2bd5e88..5b70ddb5d 100644 --- a/avr/cores/megacommand/MCL/PolyPage.cpp +++ b/avr/cores/megacommand/MCL/PolyPage.cpp @@ -8,7 +8,7 @@ void PolyPage::init() { poly_mask = &mcl_cfg.poly_mask; DEBUG_PRINT_FN(); trig_interface.on(); - note_interface.state = true; + note_interface.init_notes(); #ifdef OLED_DISPLAY classic_display = false; oled_display.clearDisplay(); @@ -17,9 +17,7 @@ void PolyPage::init() { } void PolyPage::cleanup() { - // md_exploit.off(); seq_ptc_page.init_poly(); - note_interface.state = false; #ifdef OLED_DISPLAY oled_display.clearDisplay(); #endif diff --git a/avr/cores/megacommand/MCL/SDDrivePage.cpp b/avr/cores/megacommand/MCL/SDDrivePage.cpp index 7fd65c2ec..bb919bada 100644 --- a/avr/cores/megacommand/MCL/SDDrivePage.cpp +++ b/avr/cores/megacommand/MCL/SDDrivePage.cpp @@ -196,6 +196,7 @@ void SDDrivePage::load_snapshot() { mcl_actions.md_setsysex_recpos(2, i); { ElektronDataToSysexEncoder encoder(&MidiUart); + delay(20); MD.global.toSysex(encoder); #ifndef OLED_DISPLAY delay(20); @@ -211,6 +212,7 @@ void SDDrivePage::load_snapshot() { goto load_error; } mcl_actions.md_setsysex_recpos(8, i); + delay(20); MD.pattern.toSysex(); #ifndef OLED_DISPLAY delay(20); @@ -225,11 +227,12 @@ void SDDrivePage::load_snapshot() { goto load_error; } mcl_actions.md_setsysex_recpos(4, i); + delay(20); MD.kit.toSysex(); #ifndef OLED_DISPLAY delay(20); #endif - + } // Load complete progress_max = 0; diff --git a/avr/cores/megacommand/MCL/SeqPage.cpp b/avr/cores/megacommand/MCL/SeqPage.cpp index a4e647a58..c436daccd 100644 --- a/avr/cores/megacommand/MCL/SeqPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPage.cpp @@ -51,11 +51,12 @@ void SeqPage::init() { oled_display.clearDisplay(); #endif toggle_device = true; - if (mcl_cfg.track_select == 1) { seq_menu_page.menu.enable_entry(0, false); + if (mcl_cfg.track_select == 1) { + seq_menu_page.menu.enable_entry(1, false); } else { - seq_menu_page.menu.enable_entry(0, true); + seq_menu_page.menu.enable_entry(1, true); } } @@ -911,14 +912,14 @@ void step_menu_handler() { 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(3, true); + seq_menu_page.menu.enable_entry(4, 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(3, false); + seq_menu_page.menu.enable_entry(4, true); } void SeqPage::loop() { diff --git a/avr/cores/megacommand/MCL/SeqPages.cpp b/avr/cores/megacommand/MCL/SeqPages.cpp index 96b9b101b..c0ee0c92a 100644 --- a/avr/cores/megacommand/MCL/SeqPages.cpp +++ b/avr/cores/megacommand/MCL/SeqPages.cpp @@ -11,7 +11,7 @@ MCLEncoder seq_lock2(0, 127, ENCODER_RES_PARAM); MCLEncoder ptc_param_oct(0, 8, ENCODER_RES_SEQ); MCLEncoder ptc_param_finetune(0, 64, ENCODER_RES_SEQ); MCLEncoder ptc_param_len(0, 64, ENCODER_RES_SEQ); -MCLEncoder ptc_param_scale(0, 15, ENCODER_RES_SEQ); +MCLEncoder ptc_param_scale(0, 23, ENCODER_RES_SEQ); SeqParamPage seq_param_page[NUM_PARAM_PAGES]; SeqStepPage seq_step_page(&seq_param1, &seq_param2, &seq_param3, &seq_param4); @@ -26,9 +26,12 @@ 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<8> seq_menu_layout PROGMEM = { +ArpPage arp_page(&arp_und, &arp_mode, &arp_speed, &arp_oct); + +const menu_t<9> seq_menu_layout PROGMEM = { "SEQ", { + {"ARPEGGIATOR", 0, 0, 0, (uint8_t *)NULL, (Page *) &arp_page, NULL, {}}, {"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"}}}, @@ -43,7 +46,7 @@ const menu_t<8> seq_menu_layout PROGMEM = { 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); +MenuPage<9> 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 9511de334..13e589aca 100644 --- a/avr/cores/megacommand/MCL/SeqPages.h +++ b/avr/cores/megacommand/MCL/SeqPages.h @@ -6,6 +6,7 @@ #include "MCLEncoder.h" #include "MCLMemory.h" #include "MCLMenus.h" +#include "ArpPage.h" #ifdef OLED_DISPLAY #define ENCODER_RES_SEQ 2 @@ -51,10 +52,11 @@ extern MCLEncoder ptc_param_len; extern MCLEncoder ptc_param_scale; extern SeqPtcPage seq_ptc_page; +extern ArpPage arp_page; extern MCLEncoder seq_menu_value_encoder; extern MCLEncoder seq_menu_entry_encoder; -extern MenuPage<8> seq_menu_page; +extern MenuPage<9> seq_menu_page; extern MCLEncoder step_menu_value_encoder; extern MCLEncoder step_menu_entry_encoder; diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.cpp b/avr/cores/megacommand/MCL/SeqPtcPage.cpp index d21a0e382..638a8bf8d 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.cpp +++ b/avr/cores/megacommand/MCL/SeqPtcPage.cpp @@ -1,28 +1,22 @@ +#include "ArpPage.h" #include "MCL.h" #include "SeqPtcPage.h" #define MIDI_LOCAL_MODE 0 #define NUM_KEYS 24 -scale_t *scales[16]{ - &chromaticScale, &ionianScale, - //&dorianScale, - &phrygianScale, - //&lydianScale, - //&mixolydianScale, - //&aeolianScale, - //&locrianScale, - &harmonicMinorScale, &melodicMinorScale, +scale_t *scales[24]{ + &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, + &inSenScale, &bluesScale, &majorBebopScale, &dominantBebopScale, + &minorBebopScale, &majorArp, &minorArp, &majorMaj7Arp, &majorMin7Arp, + &minorMin7Arp, //&minorMaj7Arp, &majorMaj7Arp9, //&majorMaj7ArpMin9, @@ -37,8 +31,9 @@ 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", + "---", "MAJ", "DOR", "PHR", "LYD", "MIX", "MIN", "LOC", + "mHA", "mME", "MPE", "mPE", "sPE", "ISS", "BLU", "MBP", + "DBP", "mBP", "MA", "MIA", "MM7", "Mm7", "mm7", "M79", }; void SeqPtcPage::setup() { @@ -48,6 +43,7 @@ void SeqPtcPage::setup() { } void SeqPtcPage::cleanup() { SeqPage::cleanup(); + // trig_interface.off(); recording = false; if (MidiClock.state != 2) { MD.setTrackParam(last_md_track, 0, MD.kit.params[last_md_track][0]); @@ -90,12 +86,13 @@ void SeqPtcPage::init_poly() { void SeqPtcPage::init() { DEBUG_PRINT_FN(); SeqPage::init(); + seq_menu_page.menu.enable_entry(0, true); ptc_param_len.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); + trig_interface.on(); if (mcl_cfg.uart2_ctrl_mode == MIDI_LOCAL_MODE) { trig_interface.on(); note_interface.state = true; @@ -191,11 +188,13 @@ void ptc_pattern_len_handler(Encoder *enc) { void SeqPtcPage::loop() { if (re_init) { - init(); + init(); } #ifdef EXT_TRACKS if (ptc_param_oct.hasChanged() || ptc_param_scale.hasChanged()) { mcl_seq.ext_tracks[last_ext_track].buffer_notesoff(); + recalc_notemask(); + render_arp(); } #endif @@ -289,7 +288,6 @@ void SeqPtcPage::display() { oled_display.clearDisplay(); auto *oldfont = oled_display.getFont(); - if (midi_device == DEVICE_MD) { dev_num = last_md_track; } @@ -298,14 +296,14 @@ void SeqPtcPage::display() { dev_num = last_ext_track + 16; } #endif - + bool is_poly = IS_BIT_SET16(mcl_cfg.poly_mask, last_md_track); draw_knob_frame(); char buf1[4]; // draw OCTAVE itoa(ptc_param_oct.getValue(), buf1, 10); draw_knob(0, "OCT", buf1); - + // draw FREQ if (ptc_param_finetune.getValue() < 32) { strcpy(buf1, "-"); @@ -321,7 +319,11 @@ void SeqPtcPage::display() { // draw LEN if (midi_device == DEVICE_MD) { itoa(ptc_param_len.getValue(), buf1, 10); - draw_knob(2, "LEN", buf1); + if ((mcl_cfg.poly_mask > 0) && (is_poly)) { + draw_knob(2, "PLEN", buf1); + } else { + draw_knob(2, "LEN", buf1); + } } #ifdef EXT_TRACKS else { @@ -339,6 +341,11 @@ void SeqPtcPage::display() { // draw TI keyboard mcl_gui.draw_keyboard(32, 23, 6, 9, NUM_KEYS, note_mask); + oled_display.setFont(&TomThumb); + if ((mcl_cfg.poly_mask > 0) && (is_poly)) { + oled_display.setCursor(107, 32); + oled_display.print("POLY"); + } SeqPage::display(); oled_display.display(); oled_display.setFont(oldfont); @@ -357,7 +364,8 @@ uint8_t SeqPtcPage::get_next_voice(uint8_t pitch) { uint8_t voice = 255; uint8_t count = 0; - if (poly_max == 0 || (!IS_BIT_SET16(mcl_cfg.poly_mask, last_md_track) && (mcl_cfg.uart2_ctrl_mode == 0))) { + if (poly_max == 0 || (!IS_BIT_SET16(mcl_cfg.poly_mask, last_md_track) && + (mcl_cfg.uart2_ctrl_mode == 0))) { return last_md_track; } // If track previously played pitch, re-use this track @@ -407,19 +415,15 @@ uint8_t SeqPtcPage::get_machine_pitch(uint8_t track, uint8_t pitch) { return machine_pitch; } -void SeqPtcPage::trig_md(uint8_t note, uint8_t pitch) { - pitch = octave_to_pitch() + pitch; +void SeqPtcPage::trig_md(uint8_t pitch) { + pitch = ptc_param_oct.cur * 12 + 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); - if (!BUTTON_DOWN(Buttons.BUTTON2)) { - MD.triggerTrack(next_track, 127); - } + MD.triggerTrack(next_track, 127); if ((recording) && (MidiClock.state == 2)) { - if (!BUTTON_DOWN(Buttons.BUTTON2)) { - mcl_seq.md_tracks[next_track].record_track(note, 127); - } + mcl_seq.md_tracks[next_track].record_track(127); mcl_seq.md_tracks[next_track].record_track_pitch(machine_pitch); } } @@ -427,6 +431,7 @@ void SeqPtcPage::trig_md(uint8_t note, uint8_t pitch) { void SeqPtcPage::clear_trig_fromext(uint8_t note_num) { uint8_t pitch = seq_ext_pitch(note_num - 32); CLEAR_BIT64(note_mask, pitch); + render_arp(); } void SeqPtcPage::trig_md_fromext(uint8_t note_num) { @@ -434,20 +439,349 @@ void SeqPtcPage::trig_md_fromext(uint8_t note_num) { uint8_t next_track = get_next_voice(pitch); uint8_t machine_pitch = get_machine_pitch(next_track, pitch); SET_BIT64(note_mask, pitch); + render_arp(); MD.setTrackParam(next_track, 0, machine_pitch); - if (!BUTTON_DOWN(Buttons.BUTTON2)) { - MD.triggerTrack(next_track, 127); - } + MD.triggerTrack(next_track, 127); 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(127); mcl_seq.md_tracks[next_track].record_track_pitch(machine_pitch); } } void SeqPtcPage::queue_redraw() { deferred_timer = slowclock; } +void SeqPtcPage::setup_arp() { + if (arp_enabled) { + return; + } + arp_enabled = true; + arp_len = 0; + arp_idx = 0; + render_arp(); + MidiClock.addOn16Callback( + this, (midi_clock_callback_ptr_t)&SeqPtcPage::on_16_callback); +} + +void SeqPtcPage::remove_arp() { + if (!arp_enabled) { + return; + } + arp_enabled = false; + MidiClock.removeOn16Callback( + this, (midi_clock_callback_ptr_t)&SeqPtcPage::on_16_callback); +} + +uint8_t SeqPtcPage::arp_get_next_note_down(uint8_t cur) {} +#define NOTE_RANGE 24 + +uint8_t SeqPtcPage::arp_get_next_note_up(int8_t cur) { + + for (int8_t i = cur + 1; i < NOTE_RANGE; i++) { + if (IS_BIT_SET32(note_mask, i)) { + return i; + } + } + return 255; +} + +void SeqPtcPage::render_arp() { + DEBUG_PRINT_FN(); + if (!arp_enabled) { + return; + } + arp_len = 0; + + uint8_t num_of_notes; + uint8_t note = 0; + uint8_t b = 0; + + uint8_t sort_up[NOTE_RANGE]; + uint8_t sort_down[NOTE_RANGE]; + + note = arp_get_next_note_up(-1); + if (note != 255) { + num_of_notes++; + sort_up[0] = note; + } else { + return; + } + + // Collect notes, sort in ascending order + DEBUG_PRINTLN("collecting notes"); + for (uint8_t i = 1; i < NOTE_RANGE && note != 255; i++) { + note = arp_get_next_note_up(sort_up[i - 1]); + if (note != 255) { + num_of_notes++; + sort_up[i] = note; + DEBUG_PRINTLN(i); + } + } + DEBUG_PRINTLN("finish"); + DEBUG_PRINTLN(num_of_notes); + if (num_of_notes == 0) { + return; + } + // Sort notes in descending order + + for (uint8_t i = 0; i < num_of_notes; i++) { + sort_down[num_of_notes - i - 1] = sort_up[i]; + } + note = 255; + + switch (arp_mode.cur) { + case ARP_RND: + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_up[random(0, num_of_notes)]; + arp_notes[arp_len++] = calc_pitch(note) + 12 * random(0, arp_oct.cur); + } + break; + + case ARP_UP2: + case ARP_UPP: + case ARP_UP: + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_up[i]; + arp_notes[i] = calc_pitch(note); + arp_len++; + } + break; + case ARP_DOWN2: + case ARP_DOWNP: + case ARP_DOWN: + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_down[i]; + arp_notes[i] = calc_pitch(note); + arp_len++; + } + break; + + case ARP_UPDOWN: + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_up[i]; + arp_notes[i] = calc_pitch(note); + arp_len++; + } + for (uint8_t i = 1; i < num_of_notes - 1; i++) { + note = sort_down[i]; + arp_notes[arp_len] = calc_pitch(note); + arp_len++; + } + break; + case ARP_DOWNUP: + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_down[i]; + arp_notes[i] = calc_pitch(note); + arp_len++; + } + for (uint8_t i = 1; i < num_of_notes - 1; i++) { + note = sort_up[i]; + arp_notes[arp_len] = calc_pitch(note); + arp_len++; + } + + break; + case ARP_UPNDOWN: + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_up[i]; + arp_notes[i] = calc_pitch(note); + arp_len++; + } + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_down[i]; + arp_notes[arp_len] = calc_pitch(note); + arp_len++; + } + break; + case ARP_DOWNNUP: + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_down[i]; + arp_notes[i] = calc_pitch(note); + arp_len++; + } + for (uint8_t i = 0; i < num_of_notes; i++) { + note = sort_up[i]; + arp_notes[arp_len] = calc_pitch(note); + arp_len++; + } + break; + case ARP_CONV: + for (uint8_t i = 0; i < num_of_notes; i++) { + if (i & 1) { + note = sort_down[b]; + b++; + } else { + note = sort_up[b]; + } + arp_notes[i] = calc_pitch(note); + arp_len++; + } + break; + case ARP_DIV: + for (uint8_t i = 0; i < num_of_notes; i++) { + if (i & 1) { + note = sort_up[b]; + b++; + } else { + note = sort_down[b]; + } + arp_notes[i] = calc_pitch(note); + arp_len++; + } + break; + case ARP_CONVDIV: + for (uint8_t i = 0; i < num_of_notes; i++) { + if (i & 1) { + note = sort_down[b]; + b++; + } else { + note = sort_up[b]; + } + arp_notes[i] = calc_pitch(note); + arp_len++; + } + b = 0; + for (uint8_t i = 1; i < num_of_notes; i++) { + if (i & 1) { + note = sort_down[b]; + b++; + } else { + note = sort_up[b]; + } + arp_notes[i] = calc_pitch(note); + arp_len++; + } + break; + case ARP_PINKUP: + if (num_of_notes == 1) { + note = sort_up[0]; + arp_notes[arp_len++] = calc_pitch(note); + } + for (uint8_t i = 0; i < num_of_notes - 1; i++) { + note = sort_up[i]; + arp_notes[arp_len++] = calc_pitch(note); + note = sort_down[0]; + arp_notes[arp_len++] = calc_pitch(note); + } + + break; + + case ARP_PINKDOWN: + for (uint8_t i = 1; i < num_of_notes; i++) { + note = sort_down[i]; + arp_notes[arp_len++] = calc_pitch(note); + note = sort_down[0]; + arp_notes[arp_len++] = calc_pitch(note); + } + break; + + case ARP_THUMBUP: + if (num_of_notes == 1) { + note = sort_down[0]; + arp_notes[arp_len++] = calc_pitch(note); + } + for (uint8_t i = 0; i < num_of_notes - 1; i++) { + note = sort_up[i]; + arp_notes[arp_len++] = calc_pitch(note); + note = sort_down[0]; + arp_notes[arp_len++] = calc_pitch(note); + } + + break; + + case ARP_THUMBDOWN: + for (uint8_t i = 1; i < num_of_notes; i++) { + note = sort_down[i]; + arp_notes[arp_len++] = calc_pitch(note); + note = sort_down[0]; + arp_notes[arp_len++] = calc_pitch(note); + } + break; + } + + // Generate subsequent octave itterations + for (uint8_t n = 0; n < arp_oct.cur; n++) { + for (uint8_t i = 0; i < num_of_notes && arp_len < ARP_MAX_NOTES; i++) { + switch (arp_mode.cur) { + case ARP_UP2: + case ARP_DOWN2: + arp_notes[arp_len] = arp_notes[i]; + if (!(i & 1)) { + arp_notes[arp_len] += (n + 1) * 12; + } + break; + case ARP_UPP: + case ARP_DOWNP: + arp_notes[arp_len] = arp_notes[i]; + if (i == num_of_notes - 1) { + arp_notes[arp_len] += (n + 1) * 12; + } + break; + default: + arp_notes[arp_len] = arp_notes[i] + (n + 1) * 12; + break; + } + arp_len++; + } + } + + if (arp_idx >= arp_len) { + arp_idx = arp_len - 1; + } +} +void SeqPtcPage::on_16_callback() { + bool trig = false; + uint8_t note; + + switch (arp_speed.cur) { + case 0: + trig = true; + break; + case 1: + if ((arp_count == 0) || (arp_count == 2) || (arp_count == 4) || + (arp_count == 6)) { + trig = true; + } + break; + case 2: + if ((arp_count == 0) || (arp_count == 4)) { + trig = true; + } + break; + case 3: + if (arp_count == 0) { + trig = true; + } + break; + } + + bool ignore_base = false; + + if (trig == true) { + if (arp_len > 0) { + trig_md(arp_notes[arp_idx]); + arp_idx++; + if (arp_idx == arp_len) { + arp_idx = 0; + } + } + } + + arp_count++; + if (arp_count > 7) { + arp_count = 0; + } +} + +void SeqPtcPage::recalc_notemask() { + note_mask = 0; + for (uint8_t i = 0; i < 24; i++) { + if (note_interface.notes[i] == 1) { + uint8_t pitch = calc_pitch(i); + SET_BIT64(note_mask, pitch); + } + } +} + bool SeqPtcPage::handleEvent(gui_event_t *event) { if (SeqPage::handleEvent(event)) { @@ -475,6 +809,7 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { if (mask == EVENT_BUTTON_PRESSED) { SET_BIT64(note_mask, pitch); + render_arp(); if (midi_device != DEVICE_MD) { midi_device = device; config(); @@ -482,9 +817,15 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { config_encoders(); } midi_device = device; - trig_md(note, pitch); + + if ((!arp_enabled) || (MidiClock.state != 2)) { + trig_md(pitch); + } } else if (mask == EVENT_BUTTON_RELEASED) { - CLEAR_BIT(note_mask, pitch); + if (arp_und.cur != ARP_LATCH) { + CLEAR_BIT64(note_mask, pitch); + render_arp(); + } } // deferred trigger redraw to update TI keyboard feedback. @@ -495,12 +836,17 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { if (EVENT_RELEASED(event, Buttons.BUTTON1)) { if (BUTTON_DOWN(Buttons.BUTTON4)) { - re_init = true; - GUI.pushPage(&poly_page); - return true; + re_init = true; + GUI.pushPage(&poly_page); + return true; } seq_ptc_page.queue_redraw(); recording = !recording; +#ifdef OLED_DISPLAY + if (recording) { + oled_display.textbox("RECORDING", ""); + } +#endif return true; } /* @@ -512,31 +858,32 @@ bool SeqPtcPage::handleEvent(gui_event_t *event) { if (EVENT_RELEASED(event, Buttons.BUTTON4)) { if (BUTTON_DOWN(Buttons.BUTTON1)) { - GUI.pushPage(&poly_page); - return true; + re_init = true; + GUI.pushPage(&poly_page); + return true; } if (midi_device == DEVICE_MD) { if (poly_count > 1) { #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR ", "POLY TRACKS"); + 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 { #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR ", "TRACK"); + 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 else { #ifdef OLED_DISPLAY - oled_display.textbox("CLEAR ", "EXT TRACK"); + oled_display.textbox("CLEAR ", "EXT TRACK"); #endif mcl_seq.ext_tracks[last_ext_track].clear_track(); } @@ -561,8 +908,8 @@ uint8_t SeqPtcPage::seq_ext_pitch(uint8_t note_num) { // } // if (seq_param5.getValue() > 0) { - pitch = - octave_to_pitch() + scales[ptc_param_scale.cur]->pitches[pos] + oct * 12; + pitch = ptc_param_oct.cur * 12 + scales[ptc_param_scale.cur]->pitches[pos] + + oct * 12; // } return pitch; @@ -595,9 +942,9 @@ void SeqPtcMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { return; } - uint8_t scaled_pitch = pitch - (pitch / 24) * 24; - SET_BIT64(seq_ptc_page.note_mask, scaled_pitch); - + uint8_t scaled_pitch = pitch - (pitch / 24) * 24; + SET_BIT64(seq_ptc_page.note_mask, scaled_pitch); + seq_ptc_page.render_arp(); #ifdef EXT_TRACKS // otherwise, translate the message and send it back to MIDI2. if (SeqPage::midi_device != midi_active_peering.get_device(UART2_PORT) || @@ -638,8 +985,10 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { uint8_t channel = MIDI_VOICE_CHANNEL(msg[0]); 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); - + if (arp_und.cur != ARP_LATCH) { + CLEAR_BIT64(seq_ptc_page.note_mask, scaled_pitch); + seq_ptc_page.render_arp(); + } if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { seq_ptc_page.clear_trig_fromext(pitch); @@ -662,14 +1011,38 @@ void SeqPtcMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { #endif } -void SeqPtcMidiEvents::onControlChangeCallback_Midi(uint8_t *msg) { +void SeqPtcMidiEvents::onControlChangeCallback_Midi2(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 + // If external keyboard controlling MD param, send parameter updates // to all polyphonic tracks + if ((param < 16) || (param > 39)) { + return; + } + // If Midi2 forwarding data to port 1 , ignore this to prevent double + // messages. + // + if (mcl_cfg.midi_forward == 2) { + return; + } + if ((mcl_cfg.uart2_ctrl_mode - 1 == channel) || + (mcl_cfg.uart2_ctrl_mode == MIDI_OMNI_MODE)) { + for (uint8_t n = 0; n < NUM_MD_TRACKS; n++) { + if (IS_BIT_SET16(mcl_cfg.poly_mask, n)) { + MD.setTrackParam(n, param - 16, value); + } + } + } +} +void SeqPtcMidiEvents::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; MD.parseCC(channel, param, &track, &track_param); uint8_t start_track; @@ -700,6 +1073,9 @@ void SeqPtcMidiEvents::setup_callbacks() { Midi.addOnControlChangeCallback( this, (midi_callback_ptr_t)&SeqPtcMidiEvents::onControlChangeCallback_Midi); + Midi2.addOnControlChangeCallback( + this, + (midi_callback_ptr_t)&SeqPtcMidiEvents::onControlChangeCallback_Midi2); state = true; } @@ -717,6 +1093,9 @@ void SeqPtcMidiEvents::remove_callbacks() { Midi.removeOnControlChangeCallback( this, (midi_callback_ptr_t)&SeqPtcMidiEvents::onControlChangeCallback_Midi); + Midi.removeOnControlChangeCallback( + this, + (midi_callback_ptr_t)&SeqPtcMidiEvents::onControlChangeCallback_Midi2); state = false; } diff --git a/avr/cores/megacommand/MCL/SeqPtcPage.h b/avr/cores/megacommand/MCL/SeqPtcPage.h index dfe9a4ed3..1b62d0a4a 100644 --- a/avr/cores/megacommand/MCL/SeqPtcPage.h +++ b/avr/cores/megacommand/MCL/SeqPtcPage.h @@ -6,10 +6,11 @@ #include "MidiActivePeering.h" #include "Scales.h" #include "SeqPage.h" +#include "SeqPages.h" #define MAX_POLY_NOTES 16 -extern scale_t *scales[16]; +extern scale_t *scales[24]; void ptc_pattern_len_handler(Encoder *enc); @@ -23,9 +24,34 @@ class SeqPtcMidiEvents : public MidiCallback { void onNoteOnCallback_Midi2(uint8_t *msg); void onNoteOffCallback_Midi2(uint8_t *msg); void onControlChangeCallback_Midi(uint8_t *msg); + void onControlChangeCallback_Midi2(uint8_t *msg); }; -class SeqPtcPage : public SeqPage { +#define ARP_UP 0 +#define ARP_DOWN 1 +#define ARP_UPDOWN 2 +#define ARP_DOWNUP 3 +#define ARP_UPNDOWN 4 +#define ARP_DOWNNUP 5 +#define ARP_CONV 6 +#define ARP_DIV 7 +#define ARP_CONVDIV 8 +#define ARP_PINKUP 9 +#define ARP_PINKDOWN 10 +#define ARP_THUMBUP 11 +#define ARP_THUMBDOWN 12 +#define ARP_UPP 13 +#define ARP_DOWNP 14 +#define ARP_UP2 15 +#define ARP_DOWN2 16 +#define ARP_RND 17 + +#define ARP_ON 1 +#define ARP_LATCH 2 +#define ARP_OFF 0 + +#define ARP_MAX_NOTES 16 * 3 +class SeqPtcPage : public SeqPage, public ClockCallback { public: bool re_init = false; @@ -47,15 +73,33 @@ 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, uint8_t pitch); + void trig_md(uint8_t pitch); 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(); + bool arp_enabled = false; + uint8_t arp_notes[ARP_MAX_NOTES]; + uint8_t arp_len; + + uint8_t arp_idx; + uint8_t arp_base; + uint8_t arp_dir; + uint8_t arp_count; + + void setup_arp(); + void remove_arp(); + void render_arp(); + + void recalc_notemask(); + + uint8_t arp_get_next_note_up(int8_t); + uint8_t arp_get_next_note_down(uint8_t); + void on_16_callback(); + virtual bool handleEvent(gui_event_t *event); virtual void display(); virtual void setup(); diff --git a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp index 7d47342ec..5ad5d9026 100644 --- a/avr/cores/megacommand/MCL/SeqRtrkPage.cpp +++ b/avr/cores/megacommand/MCL/SeqRtrkPage.cpp @@ -124,7 +124,7 @@ bool SeqRtrkPage::handleEvent(gui_event_t *event) { 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); + mcl_seq.md_tracks[last_md_track].record_track(127); return true; } diff --git a/avr/cores/megacommand/MCL/TrigInterface.cpp b/avr/cores/megacommand/MCL/TrigInterface.cpp index a7f312eb9..5085eae84 100644 --- a/avr/cores/megacommand/MCL/TrigInterface.cpp +++ b/avr/cores/megacommand/MCL/TrigInterface.cpp @@ -27,14 +27,14 @@ bool TrigInterface::on() { } bool TrigInterface::off() { - sysex->removeSysexListener(this); - note_interface.note_proceed = false; - if (!state) { + if (!state) { return false; } if (!MD.connected) { return false; } + sysex->removeSysexListener(this); + note_interface.note_proceed = false; DEBUG_PRINTLN("deactiviating trig interface"); state = false; MD.deactivate_trig_interface(); diff --git a/avr/cores/megacommand/MD/MDParams.cpp b/avr/cores/megacommand/MD/MDParams.cpp index 1d87e6567..294f87170 100644 --- a/avr/cores/megacommand/MD/MDParams.cpp +++ b/avr/cores/megacommand/MD/MDParams.cpp @@ -31,7 +31,7 @@ const char *MDLFONames[8] = { #ifndef DISABLE_MACHINE_NAMES -md_machine_name_t_short const machine_names_short[134] PROGMEM = { +md_machine_name_t_short const machine_names_short[135] PROGMEM = { { "GN","--", 0}, { "GN", "SN", 1}, { "GN", "NS", 2}, @@ -49,6 +49,7 @@ md_machine_name_t_short const machine_names_short[134] PROGMEM = { { "TR", "CL", 26}, { "TR", "XC", 27}, { "TR", "B2", 28}, + { "TR", "S2", 29}, { "FM", "BD", 32}, { "FM", "SD", 33}, { "FM", "XT", 34}, @@ -168,7 +169,7 @@ md_machine_name_t_short const machine_names_short[134] PROGMEM = { { "RO", "48", 191} }; -const md_machine_name_t machine_names[134] PROGMEM = { +const md_machine_name_t machine_names[135] PROGMEM = { { "GND---", 0}, { "GND-SN", 1}, { "GND-NS", 2}, @@ -186,6 +187,7 @@ const md_machine_name_t machine_names[134] PROGMEM = { { "TRX-CL", 26}, { "TRX-XC", 27}, { "TRX-B2", 28}, + { "TRX-S2", 29}, { "EFM-BD", 32}, { "EFM-SD", 33}, { "EFM-XT", 34}, @@ -362,6 +364,14 @@ const model_param_name_t trx_b2_model_names[] PROGMEM = { { "PTC", 0}, { "NS", 5}, { "DRT", 6}, { "DST", 7}, {"", 127} }; +const model_param_name_t trx_s2_model_names[] PROGMEM = { { "PTC", 0}, + { "DEC", 1}, + { "NS", 2}, + { "NDE", 3}, + { "PWR", 4}, + { "TUN", 5}, + { "NTU", 6}, + { "NTY", 7}, {"", 127} }; const model_param_name_t trx_sd_model_names[] PROGMEM = { { "PTC", 0}, { "DEC", 1}, { "BMP", 2}, @@ -860,6 +870,7 @@ model_to_param_names_t model_param_names[] = { { TRX_MA_MODEL, trx_ma_model_names }, { TRX_CL_MODEL, trx_cl_model_names }, { TRX_XC_MODEL, trx_xc_model_names }, + { TRX_S2_MODEL, trx_s2_model_names }, { EFM_BD_MODEL, efm_bd_model_names }, { EFM_SD_MODEL, efm_sd_model_names }, @@ -1047,6 +1058,11 @@ static const uint8_t trx_bd_tuning[] PROGMEM = { 1, 7, 12, 17, 23, 28, 33, 39, 44, 49, 55, 60, 66, 71, 76, 82, 87, 92, 98, 103, 108, 114, 119, 124, }; + +static const uint8_t trx_s2_tuning[] PROGMEM = { + 3, 7, 11, 15, 20, 24, 30, 35, 41, 47, 54, 60, 68, 76, 84, 92, 101, 111, 121 +}; + static const uint8_t rom_tuning[] PROGMEM = { 0, 2, 5, 7, 9, 12, 14, 16, 19, 21, 23, 26, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 102, 105, 107, @@ -1114,6 +1130,7 @@ static const tuning_t tunings[] = { { GND_SN_MODEL, MIDI_NOTE_F2, sizeof(gnd_sn_tuning), 3, gnd_sn_tuning }, { TRX_B2_MODEL, MIDI_NOTE_A1, sizeof(trx_b2_tuning), 8, trx_b2_tuning }, { TRX_RS_MODEL, MIDI_NOTE_F4, sizeof(trx_rs_tuning), 13, trx_rs_tuning }, + { TRX_S2_MODEL, MIDI_NOTE_F2, sizeof(trx_s2_tuning), 0, trx_s2_tuning }, { EFM_CB_MODEL, MIDI_NOTE_F3, sizeof(efm_cb_tuning), 5, efm_cb_tuning }, { ROM_MODEL, MIDI_NOTE_A3, sizeof(rom_tuning), 4, rom_tuning }, { EFM_CY_MODEL, MIDI_NOTE_B2, sizeof(efm_cy_tuning), 6, efm_cy_tuning }, diff --git a/avr/cores/megacommand/MD/MDParams.hh b/avr/cores/megacommand/MD/MDParams.hh index 9d0400c23..58a78904f 100644 --- a/avr/cores/megacommand/MD/MDParams.hh +++ b/avr/cores/megacommand/MD/MDParams.hh @@ -88,6 +88,7 @@ #define TRX_CL_MODEL 26 #define TRX_XC_MODEL 27 #define TRX_B2_MODEL 28 +#define TRX_S2_MODEL 29 #define EFM_BD_MODEL 32 #define EFM_SD_MODEL 33 @@ -356,6 +357,15 @@ #define TRX_XC_DIST 5 #define TRX_XC_DTYP 6 +#define TRX_S2_PTCH 0 +#define TRX_S2_DEC 1 +#define TRX_S2_NOISE 2 +#define TRX_S2_NDEC 3 +#define TRX_S2_POWER 4 +#define TRX_S2_TUNE 5 +#define TRX_S2_NTUNE 6 +#define TRX_S2_NTYPE 7 + #define EFM_BD_PTCH 0 #define EFM_BD_DEC 1 #define EFM_BD_RAMP 2 @@ -868,7 +878,7 @@ PGM_P model_param_name(uint8_t model, uint8_t param); extern const char *MDLFONames[8]; -extern const md_machine_name_t machine_names[134] PROGMEM; +extern const md_machine_name_t machine_names[135] PROGMEM; typedef struct md_machine_name_s_short { char name1[3]; @@ -876,7 +886,7 @@ typedef struct md_machine_name_s_short { uint8_t id; } md_machine_name_t_short; -extern md_machine_name_t_short const machine_names_short[134] PROGMEM; +extern md_machine_name_t_short const machine_names_short[135] PROGMEM; PGM_P getMachineNameShort(uint8_t machine, uint8_t type); extern PGM_P fx_param_name(uint8_t fx_type, uint8_t param); diff --git a/avr/cores/megacommand/Midi/MidiClock.h b/avr/cores/megacommand/Midi/MidiClock.h index 2fa3b67d3..b3446ad44 100644 --- a/avr/cores/megacommand/Midi/MidiClock.h +++ b/avr/cores/megacommand/Midi/MidiClock.h @@ -391,7 +391,6 @@ class MidiClockClass { } else if (state == STARTING && (mode == INTERNAL_MIDI || useImmediateClock)) { state = STARTED; - callCallbacks(true); } } diff --git a/avr/cores/megacommand/Midi/MidiSysex.hh b/avr/cores/megacommand/Midi/MidiSysex.hh index 7003872cc..5ea6a6da5 100644 --- a/avr/cores/megacommand/Midi/MidiSysex.hh +++ b/avr/cores/megacommand/Midi/MidiSysex.hh @@ -156,7 +156,12 @@ public: } bool addSysexListener(MidiSysexListenerClass *listener) { for (int i = 0; i < NUM_SYSEX_SLAVES; i++) { - if (listeners[i] == NULL || listeners[i] == listener) { + if (listeners[i] == listener) { + return true; + } + } + for (int i = 0; i < NUM_SYSEX_SLAVES; i++) { + if (listeners[i] == NULL) { listeners[i] = listener; listener->sysex = this; return true;