Firmware for HexBoard MIDI controller
Diffstat (limited to 'HexBoard_V1.1.ino')
-rw-r--r--HexBoard_V1.1.ino153
1 files changed, 93 insertions, 60 deletions
diff --git a/HexBoard_V1.1.ino b/HexBoard_V1.1.ino
index 612b338..ed6de00 100644
--- a/HexBoard_V1.1.ino
+++ b/HexBoard_V1.1.ino
@@ -197,7 +197,7 @@ const byte gerhardLayout[elementCount] = {
ROW_FLIP(CMDB_7, 44, 43, 42, 41, 40, 39, 38, 37, 36),
ROW_FLIP(41, 40, 39, 38, 37, 36, 35, 34, 33, 32)
};
-const byte *currentLayout = wickiHaydenLayout;
+const byte* currentLayout = wickiHaydenLayout;
const unsigned int pitches[128] = {
16, 17, 18, 19, 21, 22, 23, 25, 26, 28, 29, 31, // Octave 0
@@ -299,9 +299,56 @@ GEMSelect selectKey(sizeof(selectKeyOptions) / sizeof(SelectOptionByte), selectK
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 } };
+SelectOptionByte selectScaleOptions[] = { { "ALL", 0 }, { "Major", 1 }, { "HarMin", 2 }, { "MelMin", 3 }, { "NatMin", 4 }, { "PentMaj", 5 }, { "PentMin", 6 }, { "Blues", 7 }, { "NONE", 8 }, { "NONE", 9 }, { "NONE", 10 }, { "NONE", 11 } };
GEMSelect selectScale(sizeof(selectScaleOptions) / sizeof(SelectOptionByte), selectScaleOptions);
-GEMItem menuItemScale("Scale:", scale, selectScale, setLayoutLEDs);
+GEMItem menuItemScale("Scale:", scale, selectScale, applySelectedScale);
+
+std::array<byte, 12> selectedScale;
+// Scale arrays
+const std::array<byte, 12> chromaticScale = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+const std::array<byte, 12> majorScale = { 0, 2, 4, 5, 7, 9, 11, 0, 0, 0, 0, 0 };
+const std::array<byte, 12> harmonicMinorScale = { 0, 2, 3, 5, 7, 8, 11, 0, 0, 0, 0, 0 };
+const std::array<byte, 12> melodicMinorScale = { 0, 2, 3, 5, 7, 9, 11, 0, 0, 0, 0, 0 };
+const std::array<byte, 12> naturalMinorScale = { 0, 2, 3, 5, 7, 8, 10, 0, 0, 0, 0, 0 };
+const std::array<byte, 12> pentatonicMajorScale = { 0, 2, 4, 7, 9, 0, 0, 0, 0, 0, 0, 0 };
+const std::array<byte, 12> pentatonicMinorScale = { 0, 3, 5, 7, 10, 0, 0, 0, 0, 0, 0, 0 };
+const std::array<byte, 12> bluesScale = { 0, 3, 5, 6, 7, 10, 0, 0, 0, 0, 0, 0 };
+
+// Function to apply the selected scale
+void applySelectedScale() {
+ switch (scale) {
+ case 0: // All notes
+ selectedScale = chromaticScale;
+ break;
+ case 1: // Major scale
+ selectedScale = majorScale;
+ break;
+ case 2: // Harmonic minor scale
+ selectedScale = harmonicMinorScale;
+ break;
+ case 3: // Melodic minor scale
+ selectedScale = melodicMinorScale;
+ break;
+ case 4: // Natural minor scale
+ selectedScale = naturalMinorScale;
+ break;
+ case 5: // Pentatonic major scale
+ selectedScale = pentatonicMajorScale;
+ break;
+ case 6: // Pentatonic minor scale
+ selectedScale = pentatonicMinorScale;
+ break;
+ case 7: // Blues scale
+ selectedScale = bluesScale;
+ break;
+ default:
+ break;
+ }
+ setLayoutLEDs();
+}
+
+bool scaleLock = false; // For enabling built-in buzzer for sound generation without a computer
+GEMItem menuItemScaleLock("Scale Lock:", scaleLock, setLayoutLEDs);
int transpose = 0;
SelectOptionInt selectTransposeOptions[] = {
@@ -403,6 +450,7 @@ void setup() {
strip.setBrightness(stripBrightness); // Set BRIGHTNESS (max = 255)
setCMD_LEDs();
strip.setPixelColor(cmdBtn1, strip.ColorHSV(65536 / 12, 255, pressedBrightness));
+ selectedScale = chromaticScale; // Set default scale
setLayoutLEDs();
u8g2.begin(); //Menu and graphics setup
@@ -702,6 +750,28 @@ void readDigitalButtons() {
}
}
+// Function to check if a note is within the selected scale
+bool isNotePlayable(byte note) {
+ if (!scaleLock) {
+ return true; // Return true unconditionally if the toggle is disabled
+ }
+ for (int k = 0; k < 12; k++) {
+ if (note == selectedScale[k]) {
+ return true;
+ }
+ }
+ return false;
+}
+// Used by things not affected by scaleLock
+bool isNoteLit(byte note) {
+ for (int k = 0; k < 12; k++) {
+ if (note == selectedScale[k]) {
+ return true;
+ }
+ }
+ return false;
+}
+
void playNotes() {
for (int i = 0; i < elementCount; i++) // For all buttons in the deck
{
@@ -710,16 +780,22 @@ void playNotes() {
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);
+ byte note = (currentLayout[i] - key + transpose) % 12;
+ if (isNotePlayable(note)) { // If note is within the selected scale, light up and play
+ 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);
+ byte note = (currentLayout[i] - key + transpose) % 12;
+ if (isNotePlayable(note)) {
+ setLayoutLED(i);
+ noteOff(midiChannel, (currentLayout[i] + transpose) % 128, 0);
+ }
} else {
commandRelease(currentLayout[i]);
}
@@ -875,60 +951,16 @@ void setLayoutLEDs() {
}
}
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;
- }
+ int note = (currentLayout[i] - key + transpose) % 12;
+ if (scaleLock) {
+ strip.setPixelColor(i, strip.ColorHSV(note * 5006, 255, 0));
+ } else {
+ strip.setPixelColor(i, strip.ColorHSV(note * 5006, 255, dimBrightness));
}
- 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;
- }
+
+ // Scale highlighting
+ if (isNoteLit(note)) {
+ strip.setPixelColor(i, strip.ColorHSV(note * 5006, 255, defaultBrightness));
}
}
@@ -987,6 +1019,7 @@ void setupMenu() {
menuPageMain.addMenuItem(menuItemLayout);
menuPageMain.addMenuItem(menuItemKey);
menuPageMain.addMenuItem(menuItemScale);
+ menuPageMain.addMenuItem(menuItemScaleLock);
menuPageMain.addMenuItem(menuItemTranspose);
menuPageMain.addMenuItem(menuItemBendSpeed);
menuPageMain.addMenuItem(menuItemModSpeed);