Firmware for HexBoard MIDI controller
Diffstat (limited to 'HexBoard_V1.1.ino')
-rw-r--r--HexBoard_V1.1.ino409
1 files changed, 136 insertions, 273 deletions
diff --git a/HexBoard_V1.1.ino b/HexBoard_V1.1.ino
index fb03d9a..d75c2f7 100644
--- a/HexBoard_V1.1.ino
+++ b/HexBoard_V1.1.ino
@@ -10,8 +10,7 @@
// list of things remaining to do:
// -- program the wheel -- OK!
// -- put back the animations -- OK!
- // -- test MPE working on iPad garageband -- works on pianoteq
-without MPE; need to get powered connection to iOS.
+ // -- test MPE working on iPad garageband -- works on pianoteq without MPE; need to get powered connection to iOS.
// -- volume control test on buzzer
// -- save and load presets
// -- sequencer restore
@@ -20,17 +19,13 @@ without MPE; need to get powered connection to iOS.
#include <Arduino.h>
#include <Wire.h>
#include <LittleFS.h>
- #include <queue> // std::queue construct to store open
-channels in microtonal mode
+ #include <queue> // std::queue construct to store open channels in microtonal mode
const byte diagnostics = 1;
// ====== initialize timers
- uint32_t runTime = 0; // Program loop consistent
-variable for time in milliseconds since power on
- uint32_t lapTime = 0; // Used to keep track of how
-long each loop takes. Useful for rate-limiting.
- uint32_t loopTime = 0; // Used to check speed of the
-loop in diagnostics mode 4
+ uint32_t runTime = 0; // Program loop consistent variable for time in milliseconds since power on
+ uint32_t lapTime = 0; // Used to keep track of how long each loop takes. Useful for rate-limiting.
+ uint32_t loopTime = 0; // Used to check speed of the loop in diagnostics mode 4
// ====== initialize SDA and SCL pins for hardware I/O
@@ -44,14 +39,10 @@ loop in diagnostics mode 4
Adafruit_USBD_MIDI usb_midi;
MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI);
float concertA = 440.0; // tuning of A4 in Hz
- byte MPE = 0; // microtonal mode. if zero then attempt to
-self-manage multiple channels.
- // if one then on certain synths that are MPE compatible
-will send in that mode.
- int16_t channelBend[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-0, 0, 0 }; // what's the current note bend on this channel
- byte channelPoly[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-0, 0 }; // how many notes are playing on this channel
+ byte MPE = 0; // microtonal mode. if zero then attempt to self-manage multiple channels.
+ // if one then on certain synths that are MPE compatible will send in that mode.
+ int16_t channelBend[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // what's the current note bend on this channel
+ byte channelPoly[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // how many notes are playing on this channel
std::queue<byte> openChannelQueue;
const byte defaultPBRange = 2;
@@ -59,19 +50,15 @@ will send in that mode.
#include <Adafruit_NeoPixel.h>
const byte multiplexPins[] = { 4, 5, 2, 3 }; // m1p, m2p, m4p, m8p
- const byte rowCount = 14; // The number of rows
-in the matrix
+ const byte rowCount = 14; // The number of rows in the matrix
const byte columnPins[] = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
- const byte colCount = sizeof(columnPins); // The number of columns
-in the matrix
- const byte hexCount = colCount * rowCount; // The number of
-elements in the matrix
+ const byte colCount = sizeof(columnPins); // The number of columns in the matrix
+ const byte hexCount = colCount * rowCount; // The number of elements in the matrix
const byte LEDPin = 22;
Adafruit_NeoPixel strip(hexCount, LEDPin, NEO_GRB + NEO_KHZ800);
enum { NoAnim, StarAnim, SplashAnim, OrbitAnim, OctaveAnim, NoteAnim };
byte animationType = 0;
- byte animationFPS = 32; // actually frames per 2^10 seconds. close
-enough to 30fps
+ byte animationFPS = 32; // actually frames per 2^10 seconds. close enough to 30fps
int16_t rainbowDegreeTime = 64; // ms to go through 1/360 of rainbow.
// ====== initialize hex state object
@@ -81,26 +68,19 @@ enough to 30fps
int8_t col;
} coordinates;
typedef struct {
- byte keyState = 0; // binary 00 = off, 01 = just pressed,
-10 = just released, 11 = held
+ byte keyState = 0; // binary 00 = off, 01 = just pressed, 10 = just released, 11 = held
coordinates coords = {0,0};
uint32_t timePressed = 0; // timecode of last press
uint32_t LEDcolorAnim = 0; //
uint32_t LEDcolorPlay = 0; //
uint32_t LEDcolorOn = 0; //
uint32_t LEDcolorOff = 0; //
- bool animate = 0; // hex is flagged as part of the
-animation in this frame
- int16_t steps = 0; // number of steps from key center
-(semitones in 12EDO; microtones if >12EDO)
- bool isCmd = 0; // 0 if it's a MIDI note; 1 if it's a
-MIDI control cmd
- bool inScale = 0; // 0 if it's not in the selected
-scale; 1 if it is
- byte note = 255; // MIDI note or control parameter
-corresponding to this hex
- int16_t bend; // in microtonal mode, the pitch bend
-for this note needed to be tuned correctly
+ bool animate = 0; // hex is flagged as part of the animation in this frame
+ int16_t steps = 0; // number of steps from key center (semitones in 12EDO; microtones if >12EDO)
+ bool isCmd = 0; // 0 if it's a MIDI note; 1 if it's a MIDI control cmd
+ bool inScale = 0; // 0 if it's not in the selected scale; 1 if it is
+ byte note = 255; // MIDI note or control parameter corresponding to this hex
+ int16_t bend; // in microtonal mode, the pitch bend for this note needed to be tuned correctly
byte channel; // what MIDI channel this note is playing on
float frequency; // what frequency to ring on the buzzer
void updateKeyState(bool keyPressed) {
@@ -212,12 +192,10 @@ for this note needed to be tuned correctly
#define GEM_DISABLE_GLCD
U8G2_SH1107_SEEED_128X128_F_HW_I2C u8g2(U8G2_R2, U8X8_PIN_NONE);
GEM_u8g2 menu(u8g2, GEM_POINTER_ROW, GEM_ITEMS_COUNT_AUTO, 10, 10,
-78); // menu item height; page screen top offset; menu values left
-offset
+78); // menu item height; page screen top offset; menu values left offset
const byte defaultContrast = 63; // GFX default contrast
bool screenSaverOn = 0; //
- uint32_t screenTime = 0; // GFX timer to count if
-screensaver should go on
+ uint32_t screenTime = 0; // GFX timer to count if screensaver should go on
const uint32_t screenSaverMillis = 10000; //
// ====== initialize piezo buzzer
@@ -227,8 +205,7 @@ screensaver should go on
//RP2040_Volume piezoBuzzer(tonePin, tonePin);
byte buzzer = 0; // buzzer state
byte currentBuzzNote = 255; // need to work on this
- uint32_t currentBuzzTime = 0; // Used to keep track of when
-this note started buzzin
+ uint32_t currentBuzzTime = 0; // Used to keep track of when this note started buzzin
uint32_t arpeggiateLength = 10; //
// ====== initialize tuning (microtonal) presets
@@ -245,8 +222,7 @@ this note started buzzin
CarlosA, CarlosB, CarlosG
};
tuningDef tuningOptions[] = {
- // replaces the idea of const byte EDO[] = { 12, 17, 19, 22, 24,
-31, 41, 53, 72 };
+ // replaces the idea of const byte EDO[] = { 12, 17, 19, 22, 24, 31, 41, 53, 72 };
{ (char*)"12 EDO", 12, 100.0 },
{ (char*)"17 EDO", 17, 1200.0 / 17 },
{ (char*)"19 EDO", 19, 1200.0 / 19 },
@@ -291,12 +267,9 @@ this note started buzzin
{ (char*)"Full Layout", 1, 65, -1, -9, TwentyFour },
{ (char*)"Bosanquet, 31", 0, 65, -2, -3, ThirtyOne },
{ (char*)"Full Layout", 1, 65, -1, -9, ThirtyOne },
- { (char*)"Bosanquet, 41", 0, 65, -4, -3, FortyOne }, //
-forty-one #1
- { (char*)"Gerhard, 41", 0, 65, 3, -10, FortyOne }, //
-forty-one #2
- { (char*)"Full Layout, 41", 0, 65, -1, -8, FortyOne }, //
-forty-one #3
+ { (char*)"Bosanquet, 41", 0, 65, -4, -3, FortyOne }, // forty-one #1
+ { (char*)"Gerhard, 41", 0, 65, 3, -10, FortyOne }, // forty-one #2
+ { (char*)"Full Layout, 41", 0, 65, -1, -8, FortyOne }, // forty-one #3
{ (char*)"Wicki-Hayden, 53", 1, 64, 9, -31, FiftyThree },
{ (char*)"Harmonic Tbl, 53", 0, 75, -31, 14, FiftyThree },
{ (char*)"Bosanquet, 53", 0, 65, -5, -4, FiftyThree },
@@ -319,66 +292,40 @@ forty-one #3
scaleDef scaleOptions[] = {
{ (char*)"None", 255, { 255, 255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255} },
- { (char*)"Major", Twelve, { 0b10101101,
-0b0101'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Minor, natural", Twelve, { 0b10110101,
-0b1010'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Minor, melodic", Twelve, { 0b10110101,
-0b0101'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Minor, harmonic", Twelve, { 0b10110101,
-0b1001'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Pentatonic, major", Twelve, { 0b10101001,
-0b0100'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Pentatonic, minor", Twelve, { 0b10010101,
-0b0010'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Blues", Twelve, { 0b10010111,
-0b0010'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Double Harmonic", Twelve, { 0b11001101,
-0b1001'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Phrygian", Twelve, { 0b11010101,
-0b1010'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Phrygian Dominant", Twelve, { 0b11001101,
-0b1010'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Dorian", Twelve, { 0b10110101,
-0b0110'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Lydian", Twelve, { 0b10101011,
-0b0101'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Lydian Dominant", Twelve, { 0b10101011,
-0b0110'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Mixolydian", Twelve, { 0b10101101,
-0b0110'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Locrian", Twelve, { 0b11010110,
-0b1010'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Whole tone", Twelve, { 0b10101010,
-0b1010'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Octatonic", Twelve, { 0b10110110,
-0b1101'0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Rast maqam", TwentyFour, { 0b10001001,
-0b00100010, 0b00101100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
- { (char*)"Rast makam", FiftyThree, { 0b10000000,
-0b01000000, 0b01000010, 0b00000001,
- 0b00000000,
-0b10001000, 0b10000'000, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Major", Twelve, { 0b10101101, 0b01010000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Minor, natural", Twelve, { 0b10110101, 0b10100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Minor, melodic", Twelve, { 0b10110101, 0b01010000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Minor, harmonic", Twelve, { 0b10110101, 0b10010000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Pentatonic, major", Twelve, { 0b10101001, 0b01000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Pentatonic, minor", Twelve, { 0b10010101, 0b00100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Blues", Twelve, { 0b10010111, 0b00100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Double Harmonic", Twelve, { 0b11001101, 0b10010000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Phrygian", Twelve, { 0b11010101, 0b10100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Phrygian Dominant", Twelve, { 0b11001101, 0b10100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Dorian", Twelve, { 0b10110101, 0b01100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Lydian", Twelve, { 0b10101011, 0b01010000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Lydian Dominant", Twelve, { 0b10101011, 0b01100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Mixolydian", Twelve, { 0b10101101, 0b01100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Locrian", Twelve, { 0b11010110, 0b10100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Whole tone", Twelve, { 0b10101010, 0b10100000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Octatonic", Twelve, { 0b10110110, 0b11010000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Rast maqam", TwentyFour, { 0b10001001, 0b00100010, 0b00101100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { (char*)"Rast makam", FiftyThree, { 0b10000000, 0b01000000, 0b01000010, 0b00000001,
+ 0b00000000, 0b10001000, 0b10000000, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
};
const byte scaleCount = sizeof(scaleOptions) / sizeof(scaleDef);
byte scaleLock = 0; // menu wants this to be an int, not a bool
// ====== initialize key coloring routines
- enum colors { W, R, O, Y, L, G, C, B,
-I, P, M,
- r, o, y, l, g, c, b, i,
- p, m };
+ enum colors { W, R, O, Y, L, G, C, B, I, P, M,
+ r, o, y, l, g, c, b, i, p, m };
enum { DARK = 0, VeryDIM = 1, DIM = 32, BRIGHT = 127, VeryBRIGHT = 255 };
enum { GRAY = 0, DULL = 127, VIVID = 255 };
- float hueCode[] = { 0.0, 0.0, 36.0, 72.0, 108.0, 144.0, 180.0,
-216.0, 252.0, 288.0, 324.0,
- 0.0, 36.0, 72.0, 108.0, 144.0, 180.0,
-216.0, 252.0, 288.0, 324.0 };
- byte satCode[] = { GRAY,
-VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,
- DULL, DULL, DULL, DULL, DULL, DULL, DULL,
-DULL, DULL, DULL };
+ float hueCode[] = { 0.0, 0.0, 36.0, 72.0, 108.0, 144.0, 180.0, 216.0, 252.0, 288.0, 324.0,
+ 0.0, 36.0, 72.0, 108.0, 144.0, 180.0, 216.0, 252.0, 288.0, 324.0 };
+ byte satCode[] = { GRAY, VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,VIVID,
+ DULL, DULL, DULL, DULL, DULL, DULL, DULL, DULL, DULL, DULL };
byte colorMode = 0;
byte perceptual = 1;
enum {assignDefault = -1}; // auto-determine this component of color
@@ -754,12 +701,9 @@ DULL, DULL, DULL };
const int keyCount = sizeof(keyOptions) / sizeof(keyDef);
// ====== initialize structure to store and recall user preferences
- typedef struct { // put all user-selectable options into a class so
-that down the line these can be saved and loaded.
+ typedef struct { // put all user-selectable options into a class so that down the line these can be saved and loaded.
char* presetName;
- int tuningIndex; // instead of using pointers, i chose to
-store index value of each option, to be saved to a .pref or .ini or
-something
+ int tuningIndex; // instead of using pointers, i chose to store index value of each option, to be saved to a .pref or .ini or something
int layoutIndex;
int scaleIndex;
int keyIndex;
@@ -856,17 +800,14 @@ something
temp.col = destination.col - origin.col;
return temp;
}
- float freqToMIDI(float Hz) { // formula to convert from
-Hz to MIDI note
+ float freqToMIDI(float Hz) { // formula to convert from Hz to MIDI note
return 69.0 + 12.0 * log2f(Hz / 440.0);
}
- float MIDItoFreq(float MIDI) { // formula to convert from
-MIDI note to Hz
+ float MIDItoFreq(float MIDI) { // formula to convert from MIDI note to Hz
return 440.0 * exp2((MIDI - 69.0) / 12.0);
}
float stepsToMIDI(int16_t stepsFromA) { // return the MIDI pitch associated
- return freqToMIDI(concertA) + ((float)stepsFromA *
-(float)current.tuning().stepSize / 100.0);
+ return freqToMIDI(concertA) + ((float)stepsFromA * (float)current.tuning().stepSize / 100.0);
}
// ====== diagnostic wrapper
@@ -883,32 +824,25 @@ MIDI note to Hz
if ((!perceptual) || (D > 360.0)) {
return 65536 * (D / 360.0);
} else {
- // red yellow green
- blue
- int hueIn[] = { 0, 9, 18, 90, 108, 126, 135, 150,
- 198, 243, 252, 261, 306, 333, 360};
- // #ff0000 #ffff00 #00ff00
-#00ffff #0000ff #ff00ff
- int hueOut[] = { 0, 3640,
-5461,10922,12743,16384,21845,27306,32768,38229,43690,49152,54613,58254,65535};
+ // red yellow green blue
+ int hueIn[] = { 0, 9, 18, 90, 108, 126, 135, 150, 198, 243, 252, 261, 306, 333, 360};
+ // #ff0000 #ffff00 #00ff00 #00ffff #0000ff #ff00ff
+ int hueOut[] = { 0, 3640, 5461,10922,12743,16384,21845,27306,32768,38229,43690,49152,54613,58254,65535};
byte B = 0;
while (D - hueIn[B] > 0) {
B++;
};
- return hueOut[B - 1] + (hueOut[B] - hueOut[B - 1]) * ((D -
-(float)hueIn[B - 1])/(float)(hueIn[B] - hueIn[B - 1]));
+ return hueOut[B - 1] + (hueOut[B] - hueOut[B - 1]) * ((D - (float)hueIn[B - 1])/(float)(hueIn[B] - hueIn[B - 1]));
};
}
- void resetHexLEDs() { // calculate color codes for each hex, store
-for playback
+ void resetHexLEDs() { // calculate color codes for each hex, store for playback
int16_t hue;
float hueDegrees;
byte sat;
colors c;
for (byte i = 0; i < hexCount; i++) {
if (!(h[i].isCmd)) {
- byte scaleDegree = positiveMod(h[i].steps +
-current.key().offset - current.findC(),current.tuning().cycleLength);
+ byte scaleDegree = positiveMod(h[i].steps + current.key().offset - current.findC(),current.tuning().cycleLength);
switch (colorMode) {
case 1:
c = keyOptions[current.keysBegin() + scaleDegree].tierColor;
@@ -916,8 +850,7 @@ current.key().offset - current.findC(),current.tuning().cycleLength);
sat = satCode[c];
break;
default:
- hueDegrees = 360.0 * ((float)scaleDegree /
-(float)current.tuning().cycleLength);
+ hueDegrees = 360.0 * ((float)scaleDegree / (float)current.tuning().cycleLength);
sat = 255;
break;
};
@@ -934,13 +867,11 @@ current.key().offset - current.findC(),current.tuning().cycleLength);
// ====== layout routines
- void assignPitches() { // run this if the layout, key, or
-transposition changes, but not if color or scale changes
+ void assignPitches() { // run this if the layout, key, or transposition changes, but not if color or scale changes
sendToLog("assignPitch was called:");
for (byte i = 0; i < hexCount; i++) {
if (!(h[i].isCmd)) {
- float N = stepsToMIDI(h[i].steps + current.key().offset +
-current.transpose);
+ float N = stepsToMIDI(h[i].steps + current.key().offset + current.transpose);
if (N < 0 || N >= 128) {
h[i].note = 255;
h[i].bend = 0;
@@ -989,8 +920,7 @@ current.transpose);
sendToLog("buildLayout was called:");
for (byte i = 0; i < hexCount; i++) {
if (!(h[i].isCmd)) {
- coordinates dist =
-hexDistance(h[current.layout().rootHex].coords, h[i].coords);
+ coordinates dist = hexDistance(h[current.layout().rootHex].coords, h[i].coords);
h[i].steps = (
(dist.col * current.layout().acrossSteps) +
(dist.row * (
@@ -1004,11 +934,9 @@ hexDistance(h[current.layout().rootHex].coords, h[i].coords);
));
};
};
- applyScale(); // when layout changes, have to re-apply
-scale and re-apply LEDs
+ applyScale(); // when layout changes, have to re-apply scale and re-apply LEDs
assignPitches(); // same with pitches
- u8g2.setDisplayRotation(current.layout().isPortrait ? U8G2_R2 :
-U8G2_R1); // and landscape / portrait rotation
+ u8g2.setDisplayRotation(current.layout().isPortrait ? U8G2_R2 : U8G2_R1); // and landscape / portrait rotation
sendToLog("buildLayout complete.");
}
// ====== buzzer routines
@@ -1027,10 +955,8 @@ U8G2_R1); // and landscape / portrait rotation
void buzz(byte x) { // send 128 or larger to turn off tone
currentBuzzNote = x;
if ((!(h[x].isCmd)) && (h[x].note < 128) && (h[x].frequency < 32767)) {
- //piezoBuzzer.tone(h[x].frequency, (float)velWheel.curValue *
-(100.0 / 128.0), 16384, TIME_MS);
- tone(tonePin, h[x].frequency); // stock TONE library, but
-frequency changed to float
+ //piezoBuzzer.tone(h[x].frequency, (float)velWheel.curValue * (100.0 / 128.0), 16384, TIME_MS);
+ tone(tonePin, h[x].frequency); // stock TONE library, but frequency changed to float
} else {
//piezoBuzzer.stop_tone();
noTone(tonePin); // stock TONE library
@@ -1057,23 +983,17 @@ frequency changed to float
));
}
void prepMIDIforMicrotones() {
- bool makeZone = (MPE && (current.tuningIndex != Twelve)); // if
-MPE flag is on and tuning <> 12EDO
- setMPEzone(1, (8 * makeZone)); // MPE zone 1 = ch 2 thru 9 (or
-reset if not using MPE)
+ bool makeZone = (MPE && (current.tuningIndex != Twelve)); // if MPE flag is on and tuning <> 12EDO
+ setMPEzone(1, (8 * makeZone)); // MPE zone 1 = ch 2 thru 9 (or reset if not using MPE)
delay(ccMsgCoolDown);
- setMPEzone(16, (5 * makeZone)); // MPE zone 16 = ch 11 thru 15
-(or reset if not using MPE)
+ setMPEzone(16, (5 * makeZone)); // MPE zone 16 = ch 11 thru 15 (or reset if not using MPE)
delay(ccMsgCoolDown);
for (byte i = 1; i <= 16; i++) {
- setPitchBendRange(i, defaultPBRange); // some synths try to set
-PB range to 48 semitones.
- delay(ccMsgCoolDown); // this forces it back to
-the expected range of 2 semitones.
+ setPitchBendRange(i, defaultPBRange); // some synths try to set PB range to 48 semitones.
+ delay(ccMsgCoolDown); // this forces it back to the expected range of 2 semitones.
if ((i != 10) && ((!makeZone) || ((i > 1) && (i < 16)))) {
openChannelQueue.push(i);
- sendToLog(String("pushed ch " + String(i) + " to the open
-channel queue"));
+ sendToLog(String("pushed ch " + String(i) + " to the open channel queue"));
};
channelBend[i - 1] = 0;
channelPoly[i - 1] = 0;
@@ -1082,40 +1002,32 @@ channel queue"));
void chgModulation() {
if (current.tuningIndex == Twelve) {
MIDI.sendControlChange(1, modWheel.curValue, 1);
- sendToLog(String("sent mod value " + String(modWheel.curValue) +
-" to ch 1"));
+ sendToLog(String("sent mod value " + String(modWheel.curValue) + " to ch 1"));
} else if (MPE) {
MIDI.sendControlChange(1, modWheel.curValue, 1);
- sendToLog(String("sent mod value " + String(modWheel.curValue) +
-" to ch 1"));
+ sendToLog(String("sent mod value " + String(modWheel.curValue) + " to ch 1"));
MIDI.sendControlChange(1, modWheel.curValue, 16);
- sendToLog(String("sent mod value " + String(modWheel.curValue) +
-" to ch 16"));
+ sendToLog(String("sent mod value " + String(modWheel.curValue) + " to ch 16"));
} else {
for (byte i = 0; i < 16; i++) {
MIDI.sendControlChange(1, modWheel.curValue, i + 1);
- sendToLog(String("sent mod value " + String(modWheel.curValue)
-+ " to ch " + String(i+1)));
+ sendToLog(String("sent mod value " + String(modWheel.curValue) + " to ch " + String(i+1)));
};
};
};
void chgUniversalPB() {
if (current.tuningIndex == Twelve) {
MIDI.sendPitchBend(pbWheel.curValue, 1);
- sendToLog(String("sent pb value " + String(pbWheel.curValue) + "
-to ch 1"));
+ sendToLog(String("sent pb value " + String(pbWheel.curValue) + " to ch 1"));
} else if (MPE) {
MIDI.sendPitchBend(pbWheel.curValue, 1);
- sendToLog(String("sent pb value " + String(pbWheel.curValue) + "
-to ch 1"));
+ sendToLog(String("sent pb value " + String(pbWheel.curValue) + " to ch 1"));
MIDI.sendPitchBend(pbWheel.curValue, 16);
- sendToLog(String("sent pb value " + String(pbWheel.curValue) + "
-to ch 16"));
+ sendToLog(String("sent pb value " + String(pbWheel.curValue) + " to ch 16"));
} else {
for (byte i = 0; i < 16; i++) {
MIDI.sendPitchBend(channelBend[i] + pbWheel.curValue, i + 1);
- sendToLog(String("sent pb value " + String(channelBend[i] +
-pbWheel.curValue) + " to ch " + String(i+1)));
+ sendToLog(String("sent pb value " + String(channelBend[i] + pbWheel.curValue) + " to ch " + String(i+1)));
};
};
}
@@ -1125,20 +1037,16 @@ pbWheel.curValue) + " to ch " + String(i+1)));
return 1;
} else {
byte temp = 17;
- for (byte c = MPE; c < (16 - MPE); c++) { // MPE - look at ch 2
-thru 15 [c 1-14]; otherwise ch 1 thru 16 [c 0-15]
- if ((c + 1 != 10) && (h[x].bend == channelBend[c])) { // not
-using drum channel ch 10 in either case
+ for (byte c = MPE; c < (16 - MPE); c++) { // MPE - look at ch 2 thru 15 [c 1-14]; otherwise ch 1 thru 16 [c 0-15]
+ if ((c + 1 != 10) && (h[x].bend == channelBend[c])) { // not using drum channel ch 10 in either case
temp = c + 1;
- sendToLog(String("found a matching channel: ch " +
-String(temp) + " has pitch bend " + String(channelBend[c])));
+ sendToLog(String("found a matching channel: ch " + String(temp) + " has pitch bend " + String(channelBend[c])));
break;
};
};
if (temp = 17) {
if (openChannelQueue.empty()) {
- sendToLog(String("channel queue was empty so we didn't send
-a note on"));
+ sendToLog(String("channel queue was empty so we didn't send a note on"));
} else {
temp = openChannelQueue.front();
openChannelQueue.pop();
@@ -1226,20 +1134,14 @@ a note on"));
}
void animateMirror() {
for (byte i = 0; i < hexCount; i++) { // check every hex
- if ((!(h[i].isCmd)) && (h[i].channel)) { // that is
-a held note
- for (byte j = 0; j < hexCount; j++) { // compare
-to every hex
- if ((!(h[j].isCmd)) && (!(h[j].channel))) { // that is
-a note not being played
- int16_t temp = h[i].steps - h[j].steps; // look at
-difference between notes
- if (animationType == OctaveAnim) { // set
-octave diff to zero if need be
+ if ((!(h[i].isCmd)) && (h[i].channel)) { // that is a held note
+ for (byte j = 0; j < hexCount; j++) { // compare to every hex
+ if ((!(h[j].isCmd)) && (!(h[j].channel))) { // that is a note not being played
+ int16_t temp = h[i].steps - h[j].steps; // look at difference between notes
+ if (animationType == OctaveAnim) { // set octave diff to zero if need be
temp = positiveMod(temp, current.tuning().cycleLength);
};
- if (temp == 0) { //
-highlight if diff is zero
+ if (temp == 0) { // highlight if diff is zero
h[j].animate = 1;
};
};
@@ -1248,12 +1150,9 @@ highlight if diff is zero
};
}
void animateOrbit() {
- for (byte i = 0; i < hexCount; i++) {
- // check every hex
- if ((!(h[i].isCmd)) && (h[i].channel)) {
- // that is a held note
- flagToAnimate(hexOffset(h[i].coords,hexVector((h[i].animFrame()
-% 6),1))); // different neighbor each frame
+ for (byte i = 0; i < hexCount; i++) { // check every hex
+ if ((!(h[i].isCmd)) && (h[i].channel)) { // that is a held note
+ flagToAnimate(hexOffset(h[i].coords,hexVector((h[i].animFrame() % 6),1))); // different neighbor each frame
};
};
}
@@ -1268,8 +1167,7 @@ highlight if diff is zero
byte steps = ((animationType == SplashAnim) ? radius : 1);
// star = 1 step to next corner; ring = 1 step per hex
coordinates temp =
-hexOffset(h[i].coords,hexVector(DnLeft,radius)); // start at one
-corner of the ring
+hexOffset(h[i].coords,hexVector(DnLeft,radius)); // start at one corner of the ring
for (byte dir = 0; dir < 6; dir++) {
// walk along the ring in each of the 6 hex directions
for (byte i = 0; i < steps; i++) {
@@ -1304,36 +1202,28 @@ corner of the ring
{ "Splash" , SplashAnim},
{ "Orbit" , OrbitAnim} };
- GEMSelect selectYesOrNo(sizeof(optionByteYesOrNo) /
-sizeof(SelectOptionByte), optionByteYesOrNo);
- GEMSelect selectBuzzer( sizeof(optionByteBuzzer) /
-sizeof(SelectOptionByte), optionByteBuzzer);
- GEMSelect selectColor( sizeof(optionByteColor) /
-sizeof(SelectOptionByte), optionByteColor);
- GEMSelect selectAnimate(sizeof(optionByteAnimate) /
-sizeof(SelectOptionByte), optionByteAnimate);
+ GEMSelect selectYesOrNo(sizeof(optionByteYesOrNo) / sizeof(SelectOptionByte), optionByteYesOrNo);
+ GEMSelect selectBuzzer( sizeof(optionByteBuzzer) / sizeof(SelectOptionByte), optionByteBuzzer);
+ GEMSelect selectColor( sizeof(optionByteColor) / sizeof(SelectOptionByte), optionByteColor);
+ GEMSelect selectAnimate(sizeof(optionByteAnimate) / sizeof(SelectOptionByte), optionByteAnimate);
GEMPage menuPageMain("HexBoard MIDI Controller");
GEMPage menuPageTuning("Tuning");
GEMItem menuGotoTuning("Tuning", menuPageTuning);
- GEMItem* menuItemTuning[tuningCount]; // dynamically generate item
-based on tunings
+ GEMItem* menuItemTuning[tuningCount]; // dynamically generate item based on tunings
GEMPage menuPageLayout("Layout");
GEMItem menuGotoLayout("Layout", menuPageLayout);
- GEMItem* menuItemLayout[layoutCount]; // dynamically generate item
-based on presets
+ GEMItem* menuItemLayout[layoutCount]; // dynamically generate item based on presets
GEMPage menuPageScales("Scales");
GEMItem menuGotoScales("Scales", menuPageScales);
- GEMItem* menuItemScales[scaleCount]; // dynamically generate item
-based on presets and if allowed in given EDO tuning
+ GEMItem* menuItemScales[scaleCount]; // dynamically generate item based on presets and if allowed in given EDO tuning
GEMPage menuPageKeys("Keys");
GEMItem menuGotoKeys("Keys", menuPageKeys);
- GEMItem* menuItemKeys[keyCount]; // dynamically generate item
-based on presets
+ GEMItem* menuItemKeys[keyCount]; // dynamically generate item based on presets
GEMItem menuItemScaleLock( "Scale lock?", scaleLock, selectYesOrNo);
GEMItem menuItemMPE( "MPE Mode:", MPE,
@@ -1349,30 +1239,25 @@ selectYesOrNo, resetHexLEDs);
menu.setMenuPageCurrent(menuPageMain);
menu.drawMenu();
}
- void showOnlyValidLayoutChoices() { // re-run at setup and whenever
-tuning changes
+ void showOnlyValidLayoutChoices() { // re-run at setup and whenever tuning changes
for (byte L = 0; L < layoutCount; L++) {
menuItemLayout[L]->hide((layoutOptions[L].tuning != current.tuningIndex));
};
sendToLog(String("menu: Layout choices were updated."));
}
- void showOnlyValidScaleChoices() { // re-run at setup and whenever
-tuning changes
+ void showOnlyValidScaleChoices() { // re-run at setup and whenever tuning changes
for (int S = 0; S < scaleCount; S++) {
- menuItemScales[S]->hide((scaleOptions[S].tuning !=
-current.tuningIndex) && (scaleOptions[S].tuning != 255));
+ menuItemScales[S]->hide((scaleOptions[S].tuning != current.tuningIndex) && (scaleOptions[S].tuning != 255));
};
sendToLog(String("menu: Scale choices were updated."));
}
- void showOnlyValidKeyChoices() { // re-run at setup and whenever
-tuning changes
+ void showOnlyValidKeyChoices() { // re-run at setup and whenever tuning changes
for (int K = 0; K < keyCount; K++) {
menuItemKeys[K]->hide((keyOptions[K].tuning != current.tuningIndex));
};
sendToLog(String("menu: Key choices were updated."));
}
- void changeLayout(GEMCallbackData callbackData) { // when you
-change the layout via the menu
+ void changeLayout(GEMCallbackData callbackData) { // when you change the layout via the menu
byte selection = callbackData.valByte;
if (selection != current.layoutIndex) {
current.layoutIndex = selection;
@@ -1380,8 +1265,7 @@ change the layout via the menu
};
menuHome();
}
- void changeScale(GEMCallbackData callbackData) { // when you
-change the scale via the menu
+ void changeScale(GEMCallbackData callbackData) { // when you change the scale via the menu
int selection = callbackData.valInt;
if (selection != current.scaleIndex) {
current.scaleIndex = selection;
@@ -1389,8 +1273,7 @@ change the scale via the menu
};
menuHome();
}
- void changeKey(GEMCallbackData callbackData) { // when you
-change the key via the menu
+ void changeKey(GEMCallbackData callbackData) { // when you change the key via the menu
int selection = callbackData.valInt;
if (selection != current.keyIndex) {
current.keyIndex = selection;
@@ -1415,8 +1298,7 @@ change the key via the menu
}
void buildMenu() {
menuPageMain.addMenuItem(menuGotoTuning);
- for (byte T = 0; T < tuningCount; T++) { // create pointers to all
-tuning choices
+ for (byte T = 0; T < tuningCount; T++) { // create pointers to all tuning choices
menuItemTuning[T] = new GEMItem(tuningOptions[T].name, changeTuning, T);
menuPageTuning.addMenuItem(*menuItemTuning[T]);
};
@@ -1429,8 +1311,7 @@ tuning choices
showOnlyValidLayoutChoices();
menuPageMain.addMenuItem(menuGotoScales);
- for (int S = 0; S < scaleCount; S++) { // create pointers to all
-scale items, filter them as you go
+ for (int S = 0; S < scaleCount; S++) { // create pointers to all scale items, filter them as you go
menuItemScales[S] = new GEMItem(scaleOptions[S].name, changeScale, S);
menuPageScales.addMenuItem(*menuItemScales[S]);
};
@@ -1457,14 +1338,11 @@ scale items, filter them as you go
// ====== setup routines
void setupMIDI() {
- usb_midi.setStringDescriptor("HexBoard MIDI"); // Initialize
-MIDI, and listen to all MIDI channels
- MIDI.begin(MIDI_CHANNEL_OMNI); // This will also
-call usb_midi's begin()
+ usb_midi.setStringDescriptor("HexBoard MIDI"); // Initialize MIDI, and listen to all MIDI channels
+ MIDI.begin(MIDI_CHANNEL_OMNI); // This will also call usb_midi's begin()
}
void setupFileSystem() {
- Serial.begin(115200); // Set serial to make uploads work
-without bootsel button
+ Serial.begin(115200); // Set serial to make uploads work without bootsel button
LittleFSConfig cfg; // Configure file system defaults
cfg.setAutoFormat(true); // Formats file system if it cannot be mounted.
LittleFS.setConfig(cfg);
@@ -1476,13 +1354,11 @@ without bootsel button
void setupPins() {
for (byte p = 0; p < sizeof(columnPins); p++) // For each column pin...
{
- pinMode(columnPins[p], INPUT_PULLUP); // set the pinMode to
-INPUT_PULLUP (+3.3V / HIGH).
+ pinMode(columnPins[p], INPUT_PULLUP); // set the pinMode to INPUT_PULLUP (+3.3V / HIGH).
}
for (byte p = 0; p < sizeof(multiplexPins); p++) // For each column pin...
{
- pinMode(multiplexPins[p], OUTPUT); // Setting the row
-multiplexer pins to output.
+ pinMode(multiplexPins[p], OUTPUT); // Setting the row multiplexer pins to output.
}
Wire.setSDA(lightPinSDA);
Wire.setSCL(lightPinSCL);
@@ -1526,12 +1402,9 @@ multiplexer pins to output.
void timeTracker() {
lapTime = runTime - loopTime;
- // sendToLog(String(lapTime)); // Print out the time it takes to
-run each loop
- loopTime = runTime; // Update previousTime variable to give us a
-reference point for next loop
- runTime = millis(); // Store the current time in a uniform
-variable for this program loop
+ // sendToLog(String(lapTime)); // Print out the time it takes to run each loop
+ loopTime = runTime; // Update previousTime variable to give us a reference point for next loop
+ runTime = millis(); // Store the current time in a uniform variable for this program loop
}
void screenSaver() {
if (screenTime <= screenSaverMillis) {
@@ -1548,24 +1421,17 @@ variable for this program loop
}
}
void readHexes() {
- for (byte r = 0; r < rowCount; r++) { // Iterate through each of
-the row pins on the multiplexing chip.
+ for (byte r = 0; r < rowCount; r++) { // Iterate through each of the row pins on the multiplexing chip.
for (byte d = 0; d < 4; d++) {
digitalWrite(multiplexPins[d], (r >> d) & 1);
}
- for (byte c = 0; c < colCount; c++) { // Now iterate through
-each of the column pins that are connected to the current row pin.
- byte p = columnPins[c]; // Hold the currently
-selected column pin in a variable.
- pinMode(p, INPUT_PULLUP); // Set that row pin to
-INPUT_PULLUP mode (+3.3V / HIGH).
- delayMicroseconds(10); // Delay to give the pin
-modes time to change state (false readings are caused otherwise).
- bool didYouPressHex = (digitalRead(p) == LOW); // hex is
-pressed if it returns LOW. else not pressed
+ for (byte c = 0; c < colCount; c++) { // Now iterate through each of the column pins that are connected to the current row pin.
+ byte p = columnPins[c]; // Hold the currently selected column pin in a variable.
+ pinMode(p, INPUT_PULLUP); // Set that row pin to INPUT_PULLUP mode (+3.3V / HIGH).
+ delayMicroseconds(10); // Delay to give the pin modes time to change state (false readings are caused otherwise).
+ bool didYouPressHex = (digitalRead(p) == LOW); // hex is pressed if it returns LOW. else not pressed
h[c + (r * colCount)].updateKeyState(didYouPressHex);
- pinMode(p, INPUT); // Set the selected column pin back to
-INPUT mode (0V / LOW).
+ pinMode(p, INPUT); // Set the selected column pin back to INPUT mode (0V / LOW).
}
}
}
@@ -1606,8 +1472,7 @@ INPUT mode (0V / LOW).
}
void updateWheels() {
velWheel.setTargetValue();
- bool upd = velWheel.updateValue(); // this function returns a
-boolean, gotta put it somewhere even if it isn't being used
+ bool upd = velWheel.updateValue(); // this function returns a boolean, gotta put it somewhere even if it isn't being used
if (upd) {
sendToLog(String("vel became " + String(velWheel.curValue)));
}
@@ -1754,8 +1619,7 @@ without built-in support for TinyUSB such as mbed rp2040
setupMIDI();
setupFileSystem();
setupPins();
- testDiagnostics(); // Print diagnostic troubleshooting
-information to serial monitor
+ testDiagnostics(); // Print diagnostic troubleshooting information to serial monitor
setupGrid();
setupLEDs();
setupGFX();
@@ -1770,8 +1634,7 @@ information to serial monitor
void loop() { // run on first core
timeTracker(); // Time tracking functions
screenSaver(); // Reduces wear-and-tear on OLED panel
- readHexes(); // Read and store the digital button states of
-the scanning matrix
+ readHexes(); // Read and store the digital button states of the scanning matrix
actionHexes(); // actions on hexes
arpeggiate(); // arpeggiate the buzzer
updateWheels(); // deal with the pitch/mod wheel