about summary refs log tree commit diff
path: root/HexBoard_V1.1.ino
diff options
context:
space:
mode:
authorZach DeCook <zachdecook@librem.one>2023-11-12 08:03:07 -0500
committerZach DeCook <zachdecook@librem.one>2023-11-12 08:03:07 -0500
commite1de159acef3ef33958b18244e5ad79a95c5d456 (patch)
tree4c60ed9da052968239d54c9be20b4697cc0f5c0c /HexBoard_V1.1.ino
parent44ab755203cbc8c18e6c7f8e018ae51e014ecada (diff)
downloadHexBoard-e1de159acef3ef33958b18244e5ad79a95c5d456.tar.gz
Buzzer: Implement Arpeggiator
Diffstat (limited to 'HexBoard_V1.1.ino')
-rw-r--r--HexBoard_V1.1.ino45
1 files changed, 43 insertions, 2 deletions
diff --git a/HexBoard_V1.1.ino b/HexBoard_V1.1.ino
index 57d1bf1..d051997 100644
--- a/HexBoard_V1.1.ino
+++ b/HexBoard_V1.1.ino
@@ -319,6 +319,14 @@ unsigned long previousTime = 0;  // Used to check speed of the loop in diagnosti
 int loopTime = 0;                // Used to keep track of how long each loop takes. Useful for rate-limiting.
 int screenTime = 0;              // Used to dim screen after a set time to prolong the lifespan of the OLED
 
+// Arpeggiator variables
+int arpTime = 0;            // Measures time per note
+// TODO: Make this a configurable variable?
+#define ARP_THRESHOLD 100   // Set arp speed (in milliseconds)
+byte curr_pitch = 128;
+// Keep track of whether this note is held or not.
+bool pitchRef[256];
+
 // Pitch bend and mod wheel variables
 int pitchBendNeutral = 0;   // The center position for the pitch bend "wheel." Could be adjusted for global tuning?
 int pitchBendPosition = 0;  // The actual pitch bend variable used for sending MIDI
@@ -482,8 +490,12 @@ SelectOptionByte selectLightingOptions[] = { { "Button", 0 }, { "Note", 1 }, { "
 GEMSelect selectLighting(sizeof(selectLightingOptions) / sizeof(SelectOptionByte), selectLightingOptions);
 GEMItem menuItemLighting("Lighting:", lightMode, selectLighting);
 
-bool buzzer = false;  // For enabling built-in buzzer for sound generation without a computer
-GEMItem menuItemBuzzer("Buzzer:", buzzer);
+int buzzer = 0;  // For enabling built-in buzzer for sound generation without a computer
+#define BUZZER_ARP_UP 2
+#define BUZZER_ARP_DOWN 3
+SelectOptionInt selectBuzzerOptions[] = {{"Off", 0}, {"Mono", 1}, {"Arp Up", BUZZER_ARP_UP}, {"Arp Down", BUZZER_ARP_DOWN}};
+GEMSelect selectBuzzer(sizeof(selectBuzzerOptions)/sizeof(SelectOptionInt), selectBuzzerOptions);
+GEMItem menuItemBuzzer("Buzzer:", buzzer, selectBuzzer);
 
 // For use when testing out unfinished features
 GEMItem menuItemTesting("Testing", menuPageTesting);
@@ -612,6 +624,11 @@ void loop() {
   } else {
     // Act on those buttons
     playNotes();
+    if (buzzer == BUZZER_ARP_UP) {
+      arp(1);
+    } else if (buzzer == BUZZER_ARP_DOWN) {
+      arp(-1);
+    }
 
     if (pitchModToggle) {
       // Pitch bend stuff
@@ -1260,8 +1277,30 @@ byte getHeldNote() {
   }
   return 128;
 }
+byte getNextNote(int direction, byte note) {
+  // Find the notes after note in the direction which is held.
+  for (int i = 0; i < 127; i++) {
+    byte target = (128 + note + ((1 + i)*direction)) % 128;
+    if (pitchRef[target]) {
+      return target;
+    }
+  }
+  return 128;
+}
+
+void arp(int direction) {
+    if (currentTime - arpTime > ARP_THRESHOLD){
+        arpTime = millis();
+        byte target = 128;
+        target = getNextNote(direction, curr_pitch);
+        if (target != 128) {
+            do_tone(target);
+        }
+    }
+}
 
 void do_tone(byte pitch) {
+    curr_pitch = pitch;
     if (pitch > 127 || pitch < 0) {
         noTone(TONEPIN);
         return;
@@ -1285,6 +1324,7 @@ void do_tone(byte pitch) {
 // Send Note On
 void noteOn(byte channel, byte pitch, byte velocity) {
   MIDI.sendNoteOn(pitch, velocity, channel);
+  pitchRef[pitch] = true;
   if (diagnostics == 3) {
     Serial.print(pitch);
     Serial.print(", ");
@@ -1299,6 +1339,7 @@ void noteOn(byte channel, byte pitch, byte velocity) {
 // Send Note Off
 void noteOff(byte channel, byte pitch, byte velocity) {
   MIDI.sendNoteOff(pitch, velocity, channel);
+  pitchRef[pitch] = false;
   noTone(TONEPIN);
   if (buzzer) {
     byte anotherPitch = getHeldNote();