Firmware for HexBoard MIDI controller
Diffstat (limited to 'Hexperiment.ino')
| -rw-r--r-- | Hexperiment.ino | 178 |
1 files changed, 109 insertions, 69 deletions
diff --git a/Hexperiment.ino b/Hexperiment.ino index 255d91f..38abbc5 100644 --- a/Hexperiment.ino +++ b/Hexperiment.ino @@ -1,10 +1,10 @@ // ====== Hexperiment v1.2 // Copyright 2022-2023 Jared DeCook and Zach DeCook // with help from Nicholas Fox - // Licensed under the GNU GPL Version 3. // Hardware Information: - // Generic RP2040 running at 133MHz with 16MB of flash // https://github.com/earlephilhower/arduino-pico + // Generic RP2040 running at 133MHz with 16MB of flash + // Licensed under the GNU GPL Version 3. // (Additional boards manager URL: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json) // Tools > USB Stack > (Adafruit TinyUSB) // Sketch > Export Compiled Binary @@ -141,7 +141,7 @@ #define VALUE_BLACK 0 #define VALUE_LOW 127 #define VALUE_SHADE 170 - #define VALUE_NORMAL 212 + #define VALUE_NORMAL 19 #define VALUE_FULL 255 // saturation ranges from 0..255 @@ -171,6 +171,7 @@ #define RAINBOW_MODE 0 #define TIERED_COLOR_MODE 1 + #define ALTERNATE_COLOR_MODE 2 // animations #define ANIMATE_NONE 0 @@ -566,58 +567,56 @@ { "Gerhard", 0, 65, -1, -3, TUNING_12EDO }, { "Accordion C-sys.", 1, 75, 2, -3, TUNING_12EDO }, { "Accordion B-sys.", 1, 64, 1, -3, TUNING_12EDO }, - { "Full Gamut", 1, 65, -1, -9, TUNING_12EDO }, + { "Full Gamut", 1, 65, 1, -9, TUNING_17EDO }, { "Bosanquet-Wilson", 0, 65, -2, -1, TUNING_17EDO }, { "Neutral Thirds A", 0, 65, -1, -2, TUNING_17EDO }, { "Neutral Thirds B", 0, 65, 1, -3, TUNING_17EDO }, - { "Full Gamut", 1, 65, -1, -9, TUNING_17EDO }, + { "Full Gamut", 1, 65, 1, -9, TUNING_19EDO }, { "Bosanquet-Wilson", 0, 65, -1, -2, TUNING_19EDO }, { "Kleismic", 0, 65, -1, -4, TUNING_19EDO }, - { "Full Gamut", 1, 65, -1, -9, TUNING_19EDO }, - + + { "Full Gamut", 1, 65, 1, -8, TUNING_22EDO }, { "Bosanquet-Wilson", 0, 65, -3, -1, TUNING_22EDO }, { "Porcupine", 0, 65, 1, -4, TUNING_22EDO }, - { "Full Gamut", 1, 65, 1, -8, TUNING_22EDO }, - + + { "Full Gamut", 1, 65, 1, -9, TUNING_24EDO }, { "Bosanquet-Wilson", 0, 65, -1, -3, TUNING_24EDO }, { "Inverted", 0, 65, 1, -4, TUNING_24EDO }, - { "Full Gamut", 1, 65, -1, -9, TUNING_24EDO }, - + + { "Full Gamut", 1, 65, 1, -7, TUNING_31EDO }, { "Bosanquet-Wilson", 0, 65, -2, -3, TUNING_31EDO }, { "Double Bosanquet", 0, 65, -1, -4, TUNING_31EDO }, { "Anti-Double Bos.", 0, 65, 1, -5, TUNING_31EDO }, - { "Full Gamut", 1, 65, -1, -7, TUNING_31EDO }, - + + { "Full Gamut", 0, 65, 1, -8, TUNING_41EDO }, // forty-one #3 { "Bosanquet-Wilson", 0, 65, -4, -3, TUNING_41EDO }, // forty-one #1 { "Gerhard", 0, 65, 3, -10, TUNING_41EDO }, // forty-one #2 { "Baldy", 0, 65, -1, -6, TUNING_41EDO }, { "Rodan", 1, 65, -1, -7, TUNING_41EDO }, - { "Full Gamut", 0, 65, -1, -8, TUNING_41EDO }, // forty-one #3 - + + { "Wicki-Hayden", 1, 64, 9, -31, TUNING_53EDO }, { "Bosanquet-Wilson", 0, 65, -5, -4, TUNING_53EDO }, { "Kleismic A", 0, 65, -8, -3, TUNING_53EDO }, { "Kleismic B", 0, 65, -5, -3, TUNING_53EDO }, - { "Wicki-Hayden", 1, 64, 9, -31, TUNING_53EDO }, { "Harmonic Table", 0, 75, -31, 14, TUNING_53EDO }, { "Buzzard", 0, 65, -9, -1, TUNING_53EDO }, - + + { "Full Gamut", 1, 65, 1, -9, TUNING_72EDO }, { "Expanded Janko", 0, 65, -1, -6, TUNING_72EDO }, - { "Full Gamut", 1, 65, -1, -9, TUNING_72EDO }, - + + { "Full Gamut", 1, 65, 1, -9, TUNING_BP }, { "Standard", 0, 65, -2, -1, TUNING_BP }, - { "Full Gamut", 1, 65, -1, -9, TUNING_BP }, - + + { "Full Gamut", 1, 65, 1, -9, TUNING_ALPHA }, { "Compressed", 0, 65, -2, -1, TUNING_ALPHA }, - { "Full Gamut", 1, 65, -1, -9, TUNING_ALPHA }, - + + { "Full Gamut", 1, 65, 1, -9, TUNING_BETA }, { "Compressed", 0, 65, -2, -1, TUNING_BETA }, - { "Full Gamut", 1, 65, -1, -9, TUNING_BETA }, - - { "Compressed", 0, 65, -2, -1, TUNING_GAMMA }, - { "Full Gamut", 1, 65, -1, -9, TUNING_GAMMA } + { "Full Gamut", 1, 65, 1, -9, TUNING_GAMMA }, + { "Compressed", 0, 65, -2, -1, TUNING_GAMMA } }; scaleDef scaleOptions[] = { @@ -667,7 +666,7 @@ { "Diatonic Soft", TUNING_24EDO, { 3,5,2,3,5,4,2 } }, { "Diatonic Neutral", TUNING_24EDO, { 4,3,3,4,3,4,3 } }, { "Pentatonic (12)", TUNING_24EDO, { 4,4,6,4,6 } }, - { "Pentatonic (Hába)", TUNING_24EDO, { 5,5,5,5,4 } }, + { "Pentatonic (Haba)", TUNING_24EDO, { 5,5,5,5,4 } }, { "Invert Pentatonic", TUNING_24EDO, { 6,3,6,6,3 } }, { "Rast maqam", TUNING_24EDO, { 4,3,3,4,4,2,1,3 } }, { "Bayati maqam", TUNING_24EDO, { 3,3,4,4,2,1,3,4 } }, @@ -698,13 +697,30 @@ { "Diatonic", TUNING_53EDO, { 9,9,4,9,9,9,4 } }, { "Pentatonic", TUNING_53EDO, { 9,9,13,9,13 } }, { "Rast makam", TUNING_53EDO, { 9,8,5,9,9,4,4,5 } }, + { "Usshak makam", TUNING_53EDO, { 7,6,9,9,4,4,5,9 } }, + { "Hicaz makam", TUNING_53EDO, { 5,12,5,9,4,9,9 } }, + { "Orwell", TUNING_53EDO, { 7,5,7,5,7,5,7,5,5 } }, + { "Sephiroth", TUNING_53EDO, { 6,5,5,6,5,5,6,5,5,5 } }, + { "Smitonic", TUNING_53EDO, { 11,11,3,11,3,11,3 } }, + { "Slendric", TUNING_53EDO, { 7,3,7,3,7,3,7,3,7,3,3 } }, + { "Semiquartal", TUNING_53EDO, { 9,2,9,2,9,2,9,2,9 } }, // 72 EDO { "Diatonic", TUNING_72EDO, { 12,12,6,12,12,12,6 } }, { "Pentatonic", TUNING_72EDO, { 12,12,18,12,18 } }, + { "Ben Johnston", TUNING_72EDO, { 6,6,6,5,5,5,9,8,4,4,7,7 } }, + { "18-EDO", TUNING_72EDO, { 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 } }, + { "Miracle", TUNING_72EDO, { 5,2,5,2,5,2,2,5,2,5,2,5,2,5,2,5,2,5,2,5,2 } }, + { "Marvolo", TUNING_72EDO, { 5,5,5,5,5,5,5,2,5,5,5,5,5,5 } }, + { "Catakleismic", TUNING_72EDO, { 4,7,4,4,4,7,4,4,4,7,4,4,4,7,4 } }, + { "Palace", TUNING_72EDO, { 10,9,11,12,10,9,11 } }, // BP + { "Lambda", TUNING_BP, { 2,1,2,1,2,1,2,1,1 } }, // Alpha + { "Super Meta Lydian", TUNING_ALPHA, { 3,2,2,2 } }, // Beta + { "Super Meta Lydian", TUNING_BETA, { 3,3,3,2 } }, // Gamma + { "Super Meta Lydian", TUNING_GAMMA, { 6,5,5,4 } } }; byte sine[] = { @@ -788,7 +804,7 @@ bool rotaryIsClicked = HIGH; // bool rotaryWasClicked = HIGH; // int8_t rotaryKnobTurns = 0; // - byte maxKnobTurns = 3; + byte maxKnobTurns = 1; // Create an instance of the U8g2 graphics library. U8G2_SH1107_SEEED_128X128_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE); @@ -947,8 +963,8 @@ GEMSelect selectTransposeSteps( 255, optionIntTransposeSteps); GEMItem menuItemTransposeSteps( "Transpose:", transposeSteps, selectTransposeSteps, changeTranspose); - byte colorMode = TIERED_COLOR_MODE; - SelectOptionByte optionByteColor[] = { { "Rainbow", RAINBOW_MODE }, { "Tiered" , TIERED_COLOR_MODE } }; + byte colorMode = RAINBOW_MODE; + SelectOptionByte optionByteColor[] = { { "Rainbow", RAINBOW_MODE }, { "Tiered" , TIERED_COLOR_MODE }, {"Alt", ALTERNATE_COLOR_MODE} }; GEMSelect selectColor( sizeof(optionByteColor) / sizeof(SelectOptionByte), optionByteColor); GEMItem menuItemColor( "Color mode:", colorMode, selectColor, setLEDcolorCodes); @@ -1075,13 +1091,34 @@ case TIERED_COLOR_MODE: setColor = palette[current.tuningIndex].getColor(paletteIndex); break; - default: + case RAINBOW_MODE: setColor = { 360.0 * ((float)paletteIndex / (float)current.tuning().cycleLength) , SAT_VIVID , VALUE_NORMAL }; break; + case ALTERNATE_COLOR_MODE: + float cents = current.tuning().stepSize * paletteIndex; + bool perf = 0; + float center = 0.0; + if (cents < 50) {perf = 1; center = 0.0;} + else if ((cents >= 50) && (cents < 250)) { center = 147.1;} + else if ((cents >= 250) && (cents < 450)) { center = 351.0;} + else if ((cents >= 450) && (cents < 600)) {perf = 1; center = 498.0;} + else if ((cents >= 600) && (cents <= 750)) {perf = 1; center = 702.0;} + else if ((cents > 750) && (cents <= 950)) { center = 849.0;} + else if ((cents > 950) && (cents <=1150)) { center = 1053.0;} + else if ((cents > 1150) && (cents < 1250)) {perf = 1; center = 1200.0;} + else if ((cents >=1250) && (cents < 1450)) { center = 1347.1;} + else if ((cents >=1450) && (cents < 1650)) { center = 1551.0;} + else if ((cents >=1650) && (cents < 1850)) {perf = 1; center = 1698.0;} + else if ((cents >=1800) && (cents <=1950)) {perf = 1; center = 1902.0;} + float offCenter = cents - center; + int16_t altHue = positiveMod((int)(150 + (perf * ((offCenter > 0) ? -72 : 72)) - round(1.44 * offCenter)), 360); + float deSaturate = perf * (abs(offCenter) < 20) * (1 - (0.02 * abs(offCenter))); + setColor = { (float)altHue, 255 - (255 * deSaturate), (cents ? VALUE_SHADE : VALUE_NORMAL) }; + break; } h[i].LEDcodeRest = getLEDcode(setColor); h[i].LEDcodePlay = getLEDcode(setColor.tint()); @@ -1350,11 +1387,44 @@ synth[c - 1].increment = round(FwithPB * POLL_INTERVAL_IN_MICROSECONDS * 0.065536); // cycle 0-65535 at resultant frequency synth[c - 1].eq = isoTwoTwentySix(FwithPB); } + // USE THIS IN MONO OR ARPEG MODE ONLY + + byte findNextHeldNote() { + byte n = UNUSED_NOTE; + for (byte i = 1; i <= LED_COUNT; i++) { + byte j = positiveMod(arpeggiatingNow + i, LED_COUNT); + if ((h[j].MIDIch) && (!h[j].isCmd)) { + n = j; + break; + } + } + return n; + } void replaceBuzzerWith(byte x) { - setBuzzer(0, 1); - arpeggiatingNow = x; - setBuzzer(h[arpeggiatingNow].frequency, 1); + if (arpeggiatingNow != x) { + arpeggiatingNow = x; + if (arpeggiatingNow != UNUSED_NOTE) { + setBuzzer(h[arpeggiatingNow].frequency, 1); + } else { + setBuzzer(0, 1); + } + } + } + + void resetBuzzers() { + while (!buzzChQueue.empty()) { + buzzChQueue.pop(); + } + for (byte i = 0; i < POLYPHONY_LIMIT; i++) { + synth[i].increment = 0; + synth[i].counter = 0; + } + if (playbackMode == BUZZ_POLY) { + for (byte i = 0; i < POLYPHONY_LIMIT; i++) { + buzzChQueue.push(i + 1); + } + } } // ====== MIDI routines void setPitchBendRange(byte Ch, byte semitones) { @@ -1376,20 +1446,6 @@ std::to_string(masterCh) + ", zone of this size: " + std::to_string(sizeOfZone) ); } - void resetBuzzers() { - while (!buzzChQueue.empty()) { - buzzChQueue.pop(); - } - for (byte i = 0; i < POLYPHONY_LIMIT; i++) { - synth[i].increment = 0; - synth[i].counter = 0; - } - if (playbackMode == BUZZ_POLY) { - for (byte i = 0; i < POLYPHONY_LIMIT; i++) { - buzzChQueue.push(i + 1); - } - } - } void resetTuningMIDI() { while (!MPEchQueue.empty()) { // empty the channel queue MPEchQueue.pop(); @@ -1473,8 +1529,6 @@ } } - // check - full death if over 8 poly. - void stopNote(byte x) { // this gets called on any non-command hex // that is not scale-locked. @@ -1491,9 +1545,8 @@ sendToLog("pushed " + std::to_string(h[x].MIDIch) + " on the MPE queue"); } h[x].MIDIch = 0; - if (!(playbackMode == BUZZ_OFF) && !(playbackMode == BUZZ_POLY)) { - setBuzzer(0, 1); - arpeggiateTime = 0; // trigger arpeggiate function early if any note changes + if (playbackMode && (playbackMode != BUZZ_POLY)) { + replaceBuzzerWith(findNextHeldNote()); } } if (playbackMode == BUZZ_POLY) { @@ -1643,8 +1696,7 @@ showOnlyValidScaleChoices(); // change list of choices in GEM Menu showOnlyValidKeyChoices(); // change list of choices in GEM Menu applyLayout(); // apply changes above - resetTuningMIDI - (); // clear out MIDI queue + resetTuningMIDI(); // clear out MIDI queue } menuHome(); } @@ -1853,23 +1905,11 @@ } } } - void arpeggiate() { if (playbackMode == BUZZ_ARPEGGIO) { if (runTime - arpeggiateTime > arpeggiateLength) { arpeggiateTime = runTime; - byte n = UNUSED_NOTE; - for (byte i = 1; i < LED_COUNT; i++) { - byte j = positiveMod(arpeggiatingNow + i, LED_COUNT); - if ((h[j].MIDIch) && (!h[j].isCmd)) { - n = j; - break; - } - } - arpeggiatingNow = n; - if (n != UNUSED_NOTE) { - replaceBuzzerWith(n); - } + replaceBuzzerWith(findNextHeldNote()); } } } |