Firmware for HexBoard MIDI controller
Diffstat (limited to 'HexBoard_V1.1.ino')
| -rw-r--r-- | HexBoard_V1.1.ino | 516 |
1 files changed, 428 insertions, 88 deletions
diff --git a/HexBoard_V1.1.ino b/HexBoard_V1.1.ino index 6c5b3fd..7265bf3 100644 --- a/HexBoard_V1.1.ino +++ b/HexBoard_V1.1.ino @@ -1,25 +1,48 @@ // Hardware Information: // Generic RP2040 running at 133MHz with 16MB of flash +// https://github.com/earlephilhower/arduino-pico // Brilliant resource for dealing with hexagonal coordinates. https://www.redblobgames.com/grids/hexagons/ // Might be useful for animations and stuff like that. +// Menu library documentation https://github.com/Spirik/GEM + #include <Arduino.h> #include <Adafruit_TinyUSB.h> #include <MIDI.h> #include <Adafruit_NeoPixel.h> +#define GEM_DISABLE_GLCD +#include <GEM_u8g2.h> +#include <Wire.h> +#include <Rotary.h> -// USB MIDI object +// USB MIDI object // Adafruit_USBD_MIDI usb_midi; - // Create a new instance of the Arduino MIDI Library, // and attach usb_midi as the transport. MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI); +// LED SETUP // #define LED_PIN 22 #define LED_COUNT 140 - Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGB + NEO_KHZ800); +int defaultBrightness = 50; +int dimBrightness = 15; +int pressedBrightness = 180; + + +// ENCODER SETUP // +#define ROTA 20 // Rotary encoder A +#define ROTB 21 // Rotary encoder B +Rotary rotary = Rotary(ROTA, ROTB); +const int encoderClick = 24; +int encoderState = 0; +int encoderLastState = 1; +int8_t encoder_val = 0; +uint8_t encoder_state; + +// Create an instance of the U8g2 graphics library. +U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/U8X8_PIN_NONE); // // Button matrix and LED locations @@ -39,13 +62,13 @@ Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGB + NEO_KHZ800); // 130 129 128 127 126 125 124 123 122 121 // 140 139 138 137 136 135 134 133 132 131 -// DIAGNOSTICS +// DIAGNOSTICS // // 1 = Full button test (1 and 0) // 2 = Button test (button number) // 3 = MIDI output test int diagnostics = 0; -// Define digital button matrix pins +// BUTTON MATRIX PINS // const byte columns[] = { 14, 15, 13, 12, 11, 10, 9, 8, 7, 6 }; // Column pins in order from right to left const int m1p = 4; // Multiplexing chip control pins const int m2p = 5; @@ -58,77 +81,81 @@ const byte elementCount = columnCount * rowCount; // The number of elements in // Since MIDI only uses 7 bits, we can give greater values special meanings. // (see commandPress) -const int OCT_DN = 128; -const int OCT_UP = 129; -const int BT_TOG = 130; -const int LAY_MD = 131; -const int LGH_MD = 132; -const int PTB_DN = 133; -const int PTB_UP = 134; -const int BK_TOG = 135; +const int CMDB_1 = 128; +const int CMDB_2 = 129; +const int CMDB_3 = 130; +const int CMDB_4 = 131; +const int CMDB_5 = 132; +const int CMDB_6 = 133; +const int CMDB_7 = 134; const int UNUSED = 255; +// LED addresses for CMD buttons. +const byte cmdBtn1 = 10 - 1; +const byte cmdBtn2 = 30 - 1; +const byte cmdBtn3 = 50 - 1; +const byte cmdBtn4 = 70 - 1; +const byte cmdBtn5 = 90 - 1; +const byte cmdBtn6 = 110 - 1; +const byte cmdBtn7 = 130 - 1; + +// MIDI NOTE LAYOUTS // #define ROW_FLIP(x, ix, viii, vii, vi, v, iv, iii, ii, i) i, ii, iii, iv, v, vi, vii, viii, ix, x //hacky macro because I (Jared) messed up the board layout - I'll do better next time! xD -// MIDI note value tables +// MIDI note layout tables const byte wickiHaydenLayout[elementCount] = { - ROW_FLIP(BK_TOG, 90, 92, 94, 96, 98, 100, 102, 104, 106), + ROW_FLIP(CMDB_1, 90, 92, 94, 96, 98, 100, 102, 104, 106), ROW_FLIP(83, 85, 87, 89, 91, 93, 95, 97, 99, 101), - ROW_FLIP(LGH_MD, 78, 80, 82, 84, 86, 88, 90, 92, 94), + ROW_FLIP(CMDB_2, 78, 80, 82, 84, 86, 88, 90, 92, 94), ROW_FLIP(71, 73, 75, 77, 79, 81, 83, 85, 87, 89), - ROW_FLIP(LAY_MD, 66, 68, 70, 72, 74, 76, 78, 80, 82), + ROW_FLIP(CMDB_3, 66, 68, 70, 72, 74, 76, 78, 80, 82), ROW_FLIP(59, 61, 63, 65, 67, 69, 71, 73, 75, 77), - ROW_FLIP(OCT_UP, 54, 56, 58, 60, 62, 64, 66, 68, 70), + ROW_FLIP(CMDB_4, 54, 56, 58, 60, 62, 64, 66, 68, 70), ROW_FLIP(47, 49, 51, 53, 55, 57, 59, 61, 63, 65), - ROW_FLIP(OCT_DN, 42, 44, 46, 48, 50, 52, 54, 56, 58), + ROW_FLIP(CMDB_5, 42, 44, 46, 48, 50, 52, 54, 56, 58), ROW_FLIP(35, 37, 39, 41, 43, 45, 47, 49, 51, 53), - ROW_FLIP(PTB_UP, 30, 32, 34, 36, 38, 40, 42, 44, 46), + ROW_FLIP(CMDB_6, 30, 32, 34, 36, 38, 40, 42, 44, 46), ROW_FLIP(23, 25, 27, 29, 31, 33, 35, 37, 39, 41), - ROW_FLIP(PTB_DN, 18, 20, 22, 24, 26, 28, 30, 32, 34), + ROW_FLIP(CMDB_7, 18, 20, 22, 24, 26, 28, 30, 32, 34), ROW_FLIP(11, 13, 15, 17, 19, 21, 23, 25, 27, 29) }; const byte harmonicTableLayout[elementCount] = { - ROW_FLIP(BK_TOG, 83, 76, 69, 62, 55, 48, 41, 34, 27), + ROW_FLIP(CMDB_1, 83, 76, 69, 62, 55, 48, 41, 34, 27), ROW_FLIP(86, 79, 72, 65, 58, 51, 44, 37, 30, 23), - ROW_FLIP(LGH_MD, 82, 75, 68, 61, 54, 47, 40, 33, 26), + ROW_FLIP(CMDB_2, 82, 75, 68, 61, 54, 47, 40, 33, 26), ROW_FLIP(85, 78, 71, 64, 57, 50, 43, 36, 29, 22), - ROW_FLIP(LAY_MD, 81, 74, 67, 60, 53, 46, 39, 32, 25), + ROW_FLIP(CMDB_3, 81, 74, 67, 60, 53, 46, 39, 32, 25), ROW_FLIP(84, 77, 70, 63, 56, 49, 42, 35, 28, 21), - ROW_FLIP(OCT_UP, 80, 73, 66, 59, 52, 45, 38, 31, 24), + ROW_FLIP(CMDB_4, 80, 73, 66, 59, 52, 45, 38, 31, 24), ROW_FLIP(83, 76, 69, 62, 55, 48, 41, 34, 27, 20), - ROW_FLIP(OCT_DN, 79, 72, 65, 58, 51, 44, 37, 30, 23), + ROW_FLIP(CMDB_5, 79, 72, 65, 58, 51, 44, 37, 30, 23), ROW_FLIP(82, 75, 68, 61, 54, 47, 40, 33, 26, 19), - ROW_FLIP(PTB_UP, 78, 71, 64, 57, 50, 43, 36, 29, 22), + ROW_FLIP(CMDB_6, 78, 71, 64, 57, 50, 43, 36, 29, 22), ROW_FLIP(81, 74, 67, 60, 53, 46, 39, 32, 25, 18), - ROW_FLIP(PTB_DN, 77, 70, 63, 56, 49, 42, 35, 28, 21), + ROW_FLIP(CMDB_7, 77, 70, 63, 56, 49, 42, 35, 28, 21), ROW_FLIP(80, 73, 66, 59, 52, 45, 38, 31, 24, 17) }; const byte gerhardLayout[elementCount] = { - ROW_FLIP(BK_TOG, 74, 73, 72, 71, 70, 69, 68, 67, 66), + ROW_FLIP(CMDB_1, 74, 73, 72, 71, 70, 69, 68, 67, 66), ROW_FLIP(71, 70, 69, 68, 67, 66, 65, 64, 63, 62), - ROW_FLIP(LGH_MD, 67, 66, 65, 64, 63, 62, 61, 60, 59), + ROW_FLIP(CMDB_2, 67, 66, 65, 64, 63, 62, 61, 60, 59), ROW_FLIP(64, 63, 62, 61, 60, 59, 58, 57, 56, 55), - ROW_FLIP(LAY_MD, 60, 59, 58, 57, 56, 55, 54, 53, 52), + ROW_FLIP(CMDB_3, 60, 59, 58, 57, 56, 55, 54, 53, 52), ROW_FLIP(57, 56, 55, 54, 53, 52, 51, 50, 49, 48), - ROW_FLIP(OCT_UP, 53, 52, 51, 50, 49, 48, 47, 46, 45), + ROW_FLIP(CMDB_4, 53, 52, 51, 50, 49, 48, 47, 46, 45), ROW_FLIP(50, 49, 48, 47, 46, 45, 44, 43, 42, 41), - ROW_FLIP(OCT_DN, 46, 45, 44, 43, 42, 41, 40, 39, 38), + ROW_FLIP(CMDB_5, 46, 45, 44, 43, 42, 41, 40, 39, 38), ROW_FLIP(43, 42, 41, 40, 39, 38, 37, 36, 35, 34), - ROW_FLIP(PTB_UP, 39, 38, 37, 36, 35, 34, 33, 32, 31), + ROW_FLIP(CMDB_6, 39, 38, 37, 36, 35, 34, 33, 32, 31), ROW_FLIP(36, 35, 34, 33, 32, 31, 30, 29, 28, 27), - ROW_FLIP(PTB_DN, 32, 31, 30, 29, 28, 27, 26, 25, 24), + ROW_FLIP(CMDB_7, 32, 31, 30, 29, 28, 27, 26, 25, 24), ROW_FLIP(29, 28, 27, 26, 25, 24, 23, 22, 21, 20) }; -// LEDs for OCT_UP/OCT_DN status. -const byte octUpSW = 70 - 1; -const byte octDnSW = 90 - 1; -const byte layMdSW = 50 - 1; - const byte *currentLayout = wickiHaydenLayout; // Global time variables -unsigned long currentTime; // Program loop consistent variable for time in milliseconds since power on +unsigned long currentTime; // Program loop consistent variable for time in milliseconds since power on const byte debounceTime = 2; // Global digital button debounce time in milliseconds // Variables for holding digital button states and activation times @@ -136,34 +163,77 @@ byte activeButtons[elementCount]; // Array to hold current note bu byte previousActiveButtons[elementCount]; // Array to hold previous note button states for comparison unsigned long activeButtonsTime[elementCount]; // Array to track last note button activation time for debounce +// MENU SYSTEM SETUP // +// Create menu page object of class GEMPage. Menu page holds menu items (GEMItem) and represents menu level. +// Menu can have multiple menu pages (linked to each other) with multiple menu items each +GEMPage menuPageMain("HexBoard MIDI Controller"); +GEMPage menuPageLayout("Layout"); + +GEMItem menuItemLayout("Layout", menuPageLayout); +void wickiHayden(); //Forward declarations +void harmonicTable(); +void gerhard(); +GEMItem menuItemWickiHayden("Wicki-Hayden", wickiHayden); +GEMItem menuItemHarmonicTable("Harmonic Table", harmonicTable); +GEMItem menuItemGerhard("Gerhard", gerhard); + +void setLayoutLEDs(); //Forward declaration +byte key = 0; +SelectOptionByte selectKeyOptions[] = { { "C", 0 }, { "C#", 1 }, { "D", 2 }, { "D#", 3 }, { "E", 4 }, { "F", 5 }, { "F#", 6 }, { "G", 7 }, { "G#", 8 }, { "A", 9 }, { "A#", 10 }, { "B", 11 } }; +GEMSelect selectKey(sizeof(selectKeyOptions) / sizeof(SelectOptionByte), selectKeyOptions); +GEMItem menuItemKey("Key:", key, selectKey, setLayoutLEDs); + +byte scale = 0; +SelectOptionByte selectScaleOptions[] = { { "NONE", 0 }, { "Major", 1 }, { "HarMin", 2 }, { "MelMin", 3 }, { "NatMin", 4 }, { "NONE", 5 }, { "NONE", 6 }, { "NONE", 7 }, { "NONE", 8 }, { "NONE", 9 }, { "NONE", 10 }, { "NONE", 11 } }; +GEMSelect selectScale(sizeof(selectScaleOptions) / sizeof(SelectOptionByte), selectScaleOptions); +GEMItem menuItemScale("Scale:", scale, selectScale, setLayoutLEDs); + +int transpose = 0; +SelectOptionInt selectTransposeOptions[] = { + { "-12", -12 }, { "-11", -11 }, { "-10", -10 }, { "-9", -9 }, { "-8", -8 }, { "-7", -7 }, { "-6", -6 }, { "-5", -5 }, { "-4", -4 }, { "-3", -3 }, { "-2", -2 }, { "-1", -1 }, { "0", 0 }, { "+1", 1 }, { "+2", 2 }, { "+3", 3 }, { "+4", 4 }, { "+5", 5 }, { "+6", 6 }, { "+7", 7 }, { "+8", 8 }, { "+9", 9 }, { "+10", 10 }, { "+11", 11 }, { "+12", 12 } +}; +GEMSelect selectTranspose(sizeof(selectTransposeOptions) / sizeof(SelectOptionByte), selectTransposeOptions); +void validateTranspose(); // Forward declaration +GEMItem menuItemTranspose("Transpose:", transpose, selectTranspose, validateTranspose); + +//bool highlightScale = true; // whether the black keys should be dimmer REMOVING THIS SOON +//GEMItem menuItemHighlightScale("Scale Light:", highlightScale, setLayoutLEDs); + + + +// Create menu object of class GEM_u8g2. Supply its constructor with reference to u8g2 object we created earlier +byte menuItemHeight = 10; +byte menuPageScreenTopOffset = 10; +byte menuValuesLeftOffset = 86; +GEM_u8g2 menu(u8g2, GEM_POINTER_ROW, GEM_ITEMS_COUNT_AUTO, menuItemHeight, menuPageScreenTopOffset, menuValuesLeftOffset); + // MIDI channel assignment byte midiChannel = 1; // Current MIDI channel (changed via user input) -// Octave modifier -int octave = 0; // Apply a MIDI note number offset (changed via user input in steps of 12) - -bool blackKeys = true; // whether the black keys should be dimmer // Velocity levels -byte midiVelocity = 95; // Default velocity +byte midiVelocity = 100; // Default velocity // END SETUP SECTION // ------------------------------------------------------------------------------------------------------------------------------------------------------------ void setup() { + #if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 TinyUSB_Device_Init(0); #endif - - //usb_midi.setStringDescriptor("HexBoard MIDI"); - + usb_midi.setStringDescriptor("HexBoard MIDI"); // Initialize MIDI, and listen to all MIDI channels // This will also call usb_midi's begin() MIDI.begin(MIDI_CHANNEL_OMNI); - // Set serial to make uploads work without bootsel button - Serial.begin(115200); + Wire.setSDA(16); + Wire.setSCL(17); + + pinMode(encoderClick, INPUT_PULLUP); + + Serial.begin(115200); // Set serial to make uploads work without bootsel button // Set pinModes for the digital button matrix. for (int pinNumber = 0; pinNumber < columnCount; pinNumber++) // For each column pin... @@ -174,13 +244,18 @@ void setup() { pinMode(m2p, OUTPUT); pinMode(m4p, OUTPUT); pinMode(m8p, OUTPUT); - Serial.begin(115200); - strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + + strip.begin(); // INITIALIZE NeoPixel strip object strip.show(); // Turn OFF all pixels ASAP - strip.setBrightness(128); // Set BRIGHTNESS (max = 255) - setOctLED(); + strip.setBrightness(255); // Set BRIGHTNESS (max = 255) + setCMD_LEDs(); + strip.setPixelColor(cmdBtn1, strip.ColorHSV(65536 / 12, 255, defaultBrightness)); setLayoutLEDs(); - strip.setPixelColor(layMdSW, 255, 0, 0); + + u8g2.begin(); //Menu and graphics setup + menu.init(); + setupMenu(); + menu.drawMenu(); // wait until device mounted while (!TinyUSBDevice.mounted()) delay(1); @@ -189,6 +264,12 @@ void setup() { diagnosticTest(); } +void setup1() { //Second core exclusively runs encoder + //pinMode(ROTA, INPUT_PULLUP); + //pinMode(ROTB, INPUT_PULLUP); + //encoder_init(); +} + // ------------------------------------------------------------------------------------------------------------------------------------------------------------ // START LOOP SECTION void loop() { @@ -201,7 +282,7 @@ void loop() { // Act on those buttons playNotes(); - // Held Buttons + // Held buttons heldButtons(); // Do the LEDS @@ -209,6 +290,28 @@ void loop() { // Read any new MIDI messages MIDI.read(); + + // Read menu functions + if (menu.readyForKey()) { + encoderState = digitalRead(encoderClick); + if (encoderState > encoderLastState) { + menu.registerKeyPress(GEM_KEY_OK); + } + encoderLastState = encoderState; + if (encoder_val < 0) { + menu.registerKeyPress(GEM_KEY_UP); + encoder_val = 0; + } + if (encoder_val > 0) { + menu.registerKeyPress(GEM_KEY_DOWN); + encoder_val = 0; + } + } +} + +void loop1() { + rotate(); + //readEncoder(); } // END LOOP SECTION // ------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -224,43 +327,280 @@ void diagnosticTest() { } void commandPress(byte command) { - // Keep octave between ~-12~ 0 and 24 - if (command == OCT_DN) { - if (octave >= 12) { - octave -= 12; - setOctLED(); - strip.setPixelColor(octDnSW, 120, 120, 120); + if (command == CMDB_1) { + midiVelocity = 100; + setCMD_LEDs(); + strip.setPixelColor(cmdBtn1, strip.ColorHSV(65536 / 12, 255, defaultBrightness)); + } + if (command == CMDB_2) { + midiVelocity = 60; + setCMD_LEDs(); + strip.setPixelColor(cmdBtn2, strip.ColorHSV(65536 / 3, 255, defaultBrightness)); + } + if (command == CMDB_3) { + midiVelocity = 20; + setCMD_LEDs(); + strip.setPixelColor(cmdBtn3, strip.ColorHSV(65536 / 2, 255, defaultBrightness)); + } + if (command == CMDB_4) { + } + if (command == CMDB_5) { + } + if (command == CMDB_6) { + } + if (command == CMDB_7) { + } +} +void commandRelease(byte command) { +} + +// BUTTONS // +void readDigitalButtons() { + if (diagnostics == 1) { + Serial.println(); + } + // Button Deck + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) // Iterate through each of the row pins on the multiplexing chip. + { + digitalWrite(m1p, rowIndex & 1); + digitalWrite(m2p, (rowIndex & 2) >> 1); + digitalWrite(m4p, (rowIndex & 4) >> 2); + digitalWrite(m8p, (rowIndex & 8) >> 3); + for (byte columnIndex = 0; columnIndex < columnCount; columnIndex++) // Now iterate through each of the column pins that are connected to the current row pin. + { + byte columnPin = columns[columnIndex]; // Hold the currently selected column pin in a variable. + pinMode(columnPin, INPUT_PULLUP); // Set that row pin to INPUT_PULLUP mode (+3.3V / HIGH). + byte buttonNumber = columnIndex + (rowIndex * columnCount); // Assign this location in the matrix a unique number. + delayMicroseconds(10); // Delay to give the pin modes time to change state (false readings are caused otherwise). + previousActiveButtons[buttonNumber] = activeButtons[buttonNumber]; // Track the "previous" variable for comparison. + byte buttonState = digitalRead(columnPin); // (don't)Invert reading due to INPUT_PULLUP, and store the currently selected pin state. + if (buttonState == LOW) { + if (diagnostics == 1) { + Serial.print("1"); + } else if (diagnostics == 2) { + Serial.println(buttonNumber); + } + if (!previousActiveButtons[buttonNumber]) { + // newpress time + activeButtonsTime[buttonNumber] = millis(); + } + // TODO: Implement debounce? + activeButtons[buttonNumber] = 1; + } else { + // Otherwise, the button is inactive, write a 0. + if (diagnostics == 1) { + Serial.print("0"); + } + activeButtons[buttonNumber] = 0; + } + // Set the selected column pin back to INPUT mode (0V / LOW). + pinMode(columnPin, INPUT); + } + } +} + +void playNotes() { + for (int i = 0; i < elementCount; i++) // For all buttons in the deck + { + if (activeButtons[i] != previousActiveButtons[i]) // If a change is detected + { + if (activeButtons[i] == 1) // If the button is active (newpress) + { + if (currentLayout[i] < 128) { + strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, pressedBrightness)); + noteOn(midiChannel, (currentLayout[i] + transpose) % 128, midiVelocity); + } else { + commandPress(currentLayout[i]); + } + } else { + // If the button is inactive (released) + if (currentLayout[i] < 128) { + setLayoutLED(i); + noteOff(midiChannel, (currentLayout[i] + transpose) % 128, 0); + } else { + commandRelease(currentLayout[i]); + } + } } - } else if (command == OCT_UP) { - if (octave <= 12) { - octave += 12; - setOctLED(); - strip.setPixelColor(octUpSW, 120, 120, 120); + } +} + +void heldButtons() { + for (int i = 0; i < elementCount; i++) { + if (activeButtons[i]) { + //if ( } - } else if (command == LAY_MD) { - if (currentLayout == wickiHaydenLayout) { - currentLayout = harmonicTableLayout; - } else if (currentLayout == harmonicTableLayout) { - currentLayout = gerhardLayout; - } else { - currentLayout = wickiHaydenLayout; + } +} + +// MIDI AND OTHER OUTPUTS // +// Send Note On +void noteOn(byte channel, byte pitch, byte velocity) { + MIDI.sendNoteOn(pitch, velocity, channel); + if (diagnostics == 3) { + Serial.print(pitch); + Serial.print(", "); + Serial.print(velocity); + Serial.print(", "); + Serial.println(channel); + } +} +// Send Note Off +void noteOff(byte channel, byte pitch, byte velocity) { + MIDI.sendNoteOff(pitch, velocity, channel); +} + +// LEDS // +void setCMD_LEDs() { + strip.setPixelColor(cmdBtn1, strip.ColorHSV(65536 / 12, 255, dimBrightness)); + strip.setPixelColor(cmdBtn2, strip.ColorHSV(65536 / 3, 255, dimBrightness)); + strip.setPixelColor(cmdBtn3, strip.ColorHSV(65536 / 2, 255, dimBrightness)); + strip.setPixelColor(cmdBtn4, strip.ColorHSV(0, 255, defaultBrightness)); + strip.setPixelColor(cmdBtn5, strip.ColorHSV(0, 255, defaultBrightness)); + strip.setPixelColor(cmdBtn6, strip.ColorHSV(0, 255, defaultBrightness)); + strip.setPixelColor(cmdBtn7, strip.ColorHSV(0, 255, defaultBrightness)); +} + +void setLayoutLEDs() { + for (int i = 0; i < elementCount; i++) { + if (currentLayout[i] <= 127) { + setLayoutLED(i); } - setLayoutLEDs(); - } else if (command == BK_TOG) { - blackKeys = !blackKeys; - setLayoutLEDs(); } } -void commandRelease(byte command) { - if (command == OCT_DN) { - setOctLED(); - } else if (command == OCT_UP) { - setOctLED(); +void setLayoutLED(int i) { + strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, defaultBrightness)); + // Scale highlighting + if (scale == 0) { //NONE + switch ((currentLayout[i] - key + transpose) % 12) { + default: break; // No changes since there is no scale selected + } + } + if (scale == 1) { //Major + switch ((currentLayout[i] - key + transpose) % 12) { + // If it is one of the dark keys, fall through to case 10. + case 1: + case 3: + case 6: + case 8: + case 10: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break; + // Otherwise it was a highlighted key. Do nothing + default: break; + } + } + if (scale == 2) { //HarMin + switch ((currentLayout[i] - key + transpose) % 12) { + // If it is one of the dark keys, fall through to case 10. + case 1: + case 4: + case 6: + case 9: + case 10: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break; + // Otherwise it was a highlighted key. Do nothing + default: break; + } + } + if (scale == 3) { //MelMin + switch ((currentLayout[i] - key + transpose) % 12) { + // If it is one of the dark keys, fall through to case 10. + case 1: + case 4: + case 6: + case 8: + case 10: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break; + // Otherwise it was a highlighted key. Do nothing + default: break; + } + } + if (scale == 4) { //NatMin + switch ((currentLayout[i] - key + transpose) % 12) { + // If it is one of the dark keys, fall through to case 10. + case 1: + case 4: + case 6: + case 9: + case 11: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break; + // Otherwise it was a highlighted key. Do nothing + default: break; + } } } -// END FUNCTIONS SECTION -// ------------------------------------------------------------------------------------------------------------------------------------------------------------ +// ENCODER // +// rotary encoder pin change interrupt handler +void readEncoder() { + encoder_state = (encoder_state << 4) | (digitalRead(ROTB) << 1) | digitalRead(ROTA); + Serial.println(encoder_val); + switch (encoder_state) { + case 0x23: encoder_val++; break; + case 0x32: encoder_val--; break; + default: break; + } +} +void rotate() { + unsigned char result = rotary.process(); + if (result == DIR_CW) { + encoder_val++; + } else if (result == DIR_CCW) { + encoder_val--; + } +} +// rotary encoder init +void encoder_init() { + // enable pin change interrupts + attachInterrupt(digitalPinToInterrupt(ROTA), readEncoder, RISING); + attachInterrupt(digitalPinToInterrupt(ROTB), readEncoder, RISING); + encoder_state = (digitalRead(ROTB) << 1) | digitalRead(ROTA); + interrupts(); +} + +// MENU // +void setupMenu() { + // Add menu items to Main menu page + menuPageMain.addMenuItem(menuItemLayout); + menuPageMain.addMenuItem(menuItemKey); + menuPageMain.addMenuItem(menuItemScale); + //menuPageMain.addMenuItem(menuItemHighlightScale); REMOVING SOON + menuPageMain.addMenuItem(menuItemTranspose); + // Add menu items to Layout Select page + menuPageLayout.addMenuItem(menuItemWickiHayden); + menuPageLayout.addMenuItem(menuItemHarmonicTable); + menuPageLayout.addMenuItem(menuItemGerhard); + // Specify parent menu page for the Settings menu page + menuPageLayout.setParentMenuPage(menuPageMain); + + // Add menu page to menu and set it as current + menu.setMenuPageCurrent(menuPageMain); +} + +void wickiHayden() { + currentLayout = wickiHaydenLayout; + setLayoutLEDs(); + menu.setMenuPageCurrent(menuPageMain); + menu.drawMenu(); +} +void harmonicTable() { + currentLayout = harmonicTableLayout; + setLayoutLEDs(); + menu.setMenuPageCurrent(menuPageMain); + menu.drawMenu(); +} +void gerhard() { + currentLayout = gerhardLayout; + setLayoutLEDs(); + menu.setMenuPageCurrent(menuPageMain); + menu.drawMenu(); +} + + +// Validation routine of transpose variable +void validateTranspose() { + //Need to add some code here to make sure transpose doesn't get out of hand + /*something like + if ((transpose + LOWEST NOTE IN ARRAY) < 0) { + transpose = 0; + } */ + setLayoutLEDs(); +} -// END OF PROGRAM -// ------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file +// END FUNCTIONS SECTION
\ No newline at end of file |