about summary refs log tree commit diff
path: root/HexBoard_V1.1.ino
diff options
context:
space:
mode:
authorJared DeCook <jared@shapingthesilence.com>2023-07-19 21:44:03 -0400
committerZach DeCook <zachdecook@librem.one>2023-07-19 21:44:03 -0400
commitd3ba5fe23db39aaf3b415220cf7a48c7afc4127c (patch)
treefd10d0c01bb62a45b628cbb24d58fe8ef4894356 /HexBoard_V1.1.ino
parentae8ff80102308ca7906860777c57e47f4c7391f4 (diff)
downloadHexBoard-d3ba5fe23db39aaf3b415220cf7a48c7afc4127c.tar.gz
Animations: Enable different animations
Diffstat (limited to 'HexBoard_V1.1.ino')
-rw-r--r--HexBoard_V1.1.ino173
1 files changed, 169 insertions, 4 deletions
diff --git a/HexBoard_V1.1.ino b/HexBoard_V1.1.ino
index 209c81d..30892ba 100644
--- a/HexBoard_V1.1.ino
+++ b/HexBoard_V1.1.ino
@@ -239,7 +239,8 @@ bool pitchModToggle = 1;    // Used to toggle between pitch bend and mod wheel
 byte activeButtons[elementCount];               // Array to hold current note button states
 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
-
+byte animationStep[elementCount];               // Array to track reactive lighting steps
+int animationTime = 0;                          // Used for tracking how long since last lighting update
 // Variables for sequencer mode
 typedef struct {
   bool steps[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -386,7 +387,7 @@ GEMItem menuItemBuzzer("Buzzer:", buzzer);
 // For use when testing out unfinished features
 GEMItem menuItemTesting("Testing", menuPageTesting);
 boolean release = false;  // Whether this is a release or not
-GEMItem menuItemVersion("V0.3.1 ", release, GEM_READONLY);
+GEMItem menuItemVersion("V0.4.0 ", release, GEM_READONLY);
 void sequencerSetup();  //Forward declaration
 // For enabling basic sequencer mode - not complete
 GEMItem menuItemSequencer("Sequencer:", sequencerMode, sequencerSetup);
@@ -518,6 +519,9 @@ void loop() {
     heldButtons();
   }
 
+  // Animations
+  reactiveLighting();
+
   // Do the LEDS
   strip.show();
 
@@ -792,7 +796,7 @@ void playNotes() {
       {
         if (currentLayout[i] < 128) {
           if (isNotePlayable(currentLayout[i])) {  // If note is within the selected scale, light up and play
-            strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, pressedBrightness));
+            //strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, pressedBrightness));
             noteOn(midiChannel, (currentLayout[i] + transpose) % 128, midiVelocity);
           }
         } else {
@@ -802,7 +806,7 @@ void playNotes() {
         // If the button is inactive (released)
         if (currentLayout[i] < 128) {
           if (isNotePlayable(currentLayout[i])) {
-            setLayoutLED(i);
+            //setLayoutLED(i);
             noteOff(midiChannel, (currentLayout[i] + transpose) % 128, 0);
           }
         } else {
@@ -813,6 +817,166 @@ void playNotes() {
   }
 }
 
+void reactiveLighting() {
+  animationTime = animationTime + loopTime;
+  if (animationTime >= 33) {                                             // If it has been at least 33 ms (30fps) from last run,
+    animationTime = 0;                                                   // reset clock.
+    setLayoutLEDs();                                                     // Start by setting the lights to their defaults so we can "paint" on top of it.
+    for (int i = 0; i < elementCount; i++) {                             // Scanning through the buttons
+      if (isNotePlayable(currentLayout[i]) && currentLayout[i] < 128) {  // (if they are playable)
+        switch (lightMode) {                                             // and implementing the selected lighting pattern
+          case 0:
+            buttonPattern(i);  // Lights up the button pressed.
+            break;
+          case 1:
+            notePattern(i);  // Lights up the same exact notes as played across the array.
+            break;
+          case 2:
+            octavePattern(i);  // Lights up the same notes as played in all octaves across the array.
+            break;
+          case 3:
+            splashPattern(i);  // Creates an expanding ring around the pressed button.
+            break;
+          case 4:
+            starPattern(i);  // Creates a starburst around the pressed button.
+            break;
+          default:  // Just in case something goes wrong?
+            buttonPattern(i);
+            break;
+        }
+      }
+    }
+  }
+}
+
+void buttonPattern(int i) {
+  if (activeButtons[i] == 1) {  // If it's an active button...
+    // ...then we light it up!
+    strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 240, pressedBrightness));
+  }
+}
+
+void notePattern(int i) {
+  if (activeButtons[i] == 1) {                       // Check to see if the it's an active button.
+    for (int m = 0; m < elementCount; m++) {         // Scanning through all the lights
+      if (currentLayout[m] < 128) {                  // Only runs on lights in the playable area
+        if (currentLayout[m] == currentLayout[i]) {  // If it's the same note as the active button...
+          // ...then we light it up!
+          strip.setPixelColor(m, strip.ColorHSV(((currentLayout[m] - key + transpose) % 12) * 5006, 240, pressedBrightness));
+        }
+      }
+    }
+  }
+}
+
+void octavePattern(int i) {
+  if (activeButtons[i] == 1) {                                 // Check to see if the it's an active button.
+    for (int m = 0; m < elementCount; m++) {                   // Scanning through all the lights
+      if (currentLayout[m] < 128) {                            // Only runs on lights in the playable area
+        if (currentLayout[m] % 12 == currentLayout[i] % 12) {  // If it's in different octaves as the active button...
+          // ...then we light it up!
+          strip.setPixelColor(m, strip.ColorHSV(((currentLayout[m] - key + transpose) % 12) * 5006, 240, pressedBrightness));
+        }
+      }
+    }
+  }
+}
+
+void splashPattern(int i) {
+  int x1 = i % 10;  // Calculate the coordinates of the pressed button
+  int y1 = i / 10;
+  if (animationStep[i] > 0) {                 // Oh boy, animation time! Might be overcomplicating this...
+    for (int m = 0; m < elementCount; m++) {  // Scanning through all the lights
+      if (currentLayout[m] < 128) {           // Only runs on lights in the playable area
+        int x2 = m % 10;                      // Coordinates of lights
+        int y2 = m / 10;
+        int dx = x1 - x2;  // Difference between light and button pressed
+        int dy = y1 - y2;
+// Penalty int shifts the lights over depending on if they are on odd or even rows to correct for the staggered rows
+#if ModelNumber == 1
+        int penalty = (((y1 % 2 == 0) && (y2 % 2 != 0) && (x1 > x2)) || ((y2 % 2 == 0) && (y1 % 2 != 0) && (x2 > x1))) ? 1 : 0;
+#elif ModelNumber == 2
+        int penalty = (((y1 % 2 == 0) && (y2 % 2 != 0) && (x1 < x2)) || ((y2 % 2 == 0) && (y1 % 2 != 0) && (x2 < x1))) ? 1 : 0;
+#endif
+        // If the light is the correct distance from the button...
+        if (max(abs(dy), abs(dx) + floor(abs(dy) / 2) + penalty) == animationStep[i]) {
+          // light it up!
+          strip.setPixelColor(m, strip.ColorHSV(((currentLayout[m] - key + transpose) % 12) * 5006, 240, pressedBrightness));
+          // or we could have it fade as it moves
+          //strip.setPixelColor(m, strip.ColorHSV(((currentLayout[m] - key + transpose) % 12) * 5006, 240, (pressedBrightness - animationStep[i]*12)));
+        }
+      }
+    }
+  }
+  if (activeButtons[i] == 1) {  // Check to see if the it's an active button.
+    // Then we light up the pressed button
+    strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 240, pressedBrightness));
+    if (animationStep[i] < 16) {
+      animationStep[i]++;  // Increment the animation to the next step for next time.
+    }
+  } else {
+    animationStep[i] = 0;  // Stop the animation if the key is released
+  }
+}
+
+void starPattern(int i) {
+  int x1 = i % 10;  // Calculate the coordinates of the pressed button
+  int y1 = i / 10;
+  // Define the relative offsets of neighboring buttons in the pattern
+#if ModelNumber == 1
+  int offsets[][2] = {
+    { 0, 1 },                        // Left
+    { 0, -1 },                       // Right
+    { -1, (y1 % 2 == 0) ? 0 : -1 },  // Top Left (adjusted based on row parity)
+    { -1, (y1 % 2 == 0) ? 1 : 0 },   // Top Right (adjusted based on row parity)
+    { 1, (y1 % 2 == 0) ? 1 : 0 },    // Bottom Right (adjusted based on row parity)
+    { 1, (y1 % 2 == 0) ? 0 : -1 }    // Bottom Left (adjusted based on row parity)
+  };
+#elif ModelNumber == 2
+  int offsets[][2] = {
+    { 0, -1 },                       // Left
+    { 0, 1 },                        // Right
+    { -1, (y1 % 2 == 0) ? 0 : 1 },  // Top Left (adjusted based on row parity)
+    { -1, (y1 % 2 == 0) ? -1 : 0 },   // Top Right (adjusted based on row parity)
+    { 1, (y1 % 2 == 0) ? -1 : 0 },    // Bottom Right (adjusted based on row parity)
+    { 1, (y1 % 2 == 0) ? 0 : 1 }    // Bottom Left (adjusted based on row parity)
+  };
+#endif
+
+
+  if (animationStep[i] > 0) {  // Oh boy, animation time!
+    for (const auto& offset : offsets) {
+      // Calculate the neighboring button coordinates
+      int y2 = y1 + offset[0] * animationStep[i];
+#if ModelNumber == 1
+      int x2 = x1 + offset[1] * animationStep[i] + ((y1 % 2 == 0) ? -1 : 1) * ((y1 == y2) ? 0 : 1) * (animationStep[i] / 2);
+#elif ModelNumber == 2
+      int x2 = x1 + offset[1] * animationStep[i] + ((y1 % 2 == 0) ? 1 : -1) * ((y1 == y2) ? 0 : 1) * (animationStep[i] / 2);
+#endif
+      // Check if the neighboring button is within the layout boundaries
+      if (y2 >= 0 && y2 < 14 && x2 >= 0 && x2 < 10) {
+        // Calculate the index of the neighboring button
+        int neighborIndex = y2 * 10 + x2;
+        if (currentLayout[neighborIndex] < 128) {  // If it's in the playable area...
+          // ...set the color for the neighboring button
+          strip.setPixelColor(neighborIndex, strip.ColorHSV(((currentLayout[neighborIndex] - key + transpose) % 12) * 5006, 240, pressedBrightness));
+        }
+      }
+    }
+  }
+
+
+  if (activeButtons[i] == 1) {  // Check to see if the it's an active button.
+    // Then we light up the pressed button
+    strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 240, pressedBrightness));
+    if (animationStep[i] < 16) {
+      animationStep[i]++;  // Increment the animation to the next step for next time.
+    }
+  } else {
+    animationStep[i] = 0;  // Stop the animation if the key is released
+  }
+}
+
 void heldButtons() {
   for (int i = 0; i < elementCount; i++) {
     if (activeButtons[i]) {
@@ -1033,6 +1197,7 @@ void setupMenu() {
   menuPageMain.addMenuItem(menuItemBendSpeed);
   menuPageMain.addMenuItem(menuItemModSpeed);
   menuPageMain.addMenuItem(menuItemBrightness);
+  menuPageMain.addMenuItem(menuItemLighting);
   menuPageMain.addMenuItem(menuItemBuzzer);
   menuPageMain.addMenuItem(menuItemTesting);
   // Add menu items to Layout Select page