Firmware for HexBoard MIDI controller
This version has the mono/arpeggio buzzer fixed, includes scales for 53 72
BP alpha beta and gamma tunings, and has an additional color algorithm palette called "alternate" to choose from. In that palette, the key is always bright white, and the rest of the keys are colored based on how that color "feels" against the root key. for example, in 12-EDO, C is white. F and G are white-ish because they're basically perfect 5th or 4ths. D E A and B are orange because they're major intervals, C# Eb Ab Bb are sky blue because they're minor. F# is a tritone so it is hot pink. in 31-EDO, C F and G are the same., but there are more colors for different "flavors" of microtone. For example look at the keys D# Eb Ed E and E+. they're indigo (really flat, bluesy), blue/cyan (plain minor), green (neutral, kinda sick sounding), yellow (major, sunny), and orange/red (sharp, angry). this color theory is popular among some microtone folks, it's often called "Kite colors" after the guy who invented it, Kite Giedraitis. anyway, enjoy!
Nicholas Fox 2024-04-28
parent 3fa7fc3 · commit 7d54f35
-rw-r--r--Hexperiment.ino178
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());
}
}
}