From 83900d071178ec827378b9dd7bbbbd4229b2d507 Mon Sep 17 00:00:00 2001 From: Dominic DiTaranto Date: Fri, 13 Mar 2026 16:10:25 -0400 Subject: [PATCH 1/2] display handler --- include/DisplayHandler.h | 12 +- master_clock.ino | 263 --------------- src/DisplayHandler.cpp | 700 +++++++++++++++++---------------------- src/Settings.cpp | 10 + 4 files changed, 333 insertions(+), 652 deletions(-) delete mode 100644 master_clock.ino diff --git a/include/DisplayHandler.h b/include/DisplayHandler.h index ff48d36..57beb5d 100644 --- a/include/DisplayHandler.h +++ b/include/DisplayHandler.h @@ -34,9 +34,11 @@ class DisplayHandler { "CV2 INV", "Mute" }; - bool onOutScreen = 0; - bool onSettingsScreen = 0; + bool onOutScreen = false; + bool onMainScreen = true; + bool onSettingsScreen = false; + std::array settings = { "Save", "Load", @@ -49,6 +51,11 @@ class DisplayHandler { void renderMainPage(); void renderOutPage(); void renderSettingsPage(); + void handleCursorOnOutputScreen(bool dir); + void handleClickOnOutputScreen(); + void handleCursorOnMainScreen(bool dir); + void handleClickOnMainScreen(); + void handleClickOnSettingsScreen(); public: @@ -61,6 +68,7 @@ class DisplayHandler { int8_t currentOut; bool cursorClick; void setup(); + uint8_t modifyParameter(uint8_t dir, int8_t target, uint8_t clampMax, uint8_t clampMin = 0, bool overflow = true); void render(); void handleClick(); void exitSettingsScreen(); diff --git a/master_clock.ino b/master_clock.ino deleted file mode 100644 index c424f8f..0000000 --- a/master_clock.ino +++ /dev/null @@ -1,263 +0,0 @@ -#include -#include -#include -#include - -#include "globals.h" -#include "Out.h" - -#define SCREEN_WIDTH 128 -#define SCREEN_HEIGHT 64 -#define OLED_RESET -1 -#define SCREEN_ADDRESS 0x3C -#define CLK 2 -#define DT 3 - - -U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); - -byte play = 1; -byte BPM = 100; - -unsigned long minute = 60000000L; -byte ppqn = 96; -unsigned long period = (minute / BPM) / ppqn; - -volatile boolean beatToggle = false; - -Out out1(4); -Out out2(5); -Out out3(6); -Out out4(7); -Out out5(8); -Out out6(9); -Out out7(10); -Out out8(11); - -byte btnPin = 13; -byte btnState = 0; - -volatile long rPotPos = 0; -unsigned long rPotLastUpdate = 0; -const unsigned long rPotThrottle = 50; - -unsigned long screenLastUpdate = 0; -const unsigned long screenThrottle = 200; -byte forceScreenUpdate = 1; - -byte currentScreen = 0; -byte editMode = 0; -int mainScreenCursorPos = 0; -byte maxMainScreenPos = 8; - -void setup() { - Serial.begin(9600); - - pinMode(btnPin, INPUT_PULLUP); - - // rotary encoder set up - pinMode(CLK, INPUT_PULLUP); - pinMode(DT, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(CLK), updateEncoder, FALLING); - - // screen setup - u8g2.begin(); - u8g2.setFontMode(1); - - // display.clearDisplay(); - // display.setTextSize(2); - // display.setTextColor(WHITE); - // display.setCursor(0, 0); - // - // String bpmString = "BPM: "; - // String bpmCalculatedString = bpmString + BPM; - // display.println(bpmCalculatedString); - // - // display.display(); - - // init timer - Timer1.initialize(1000000); - Timer1.attachInterrupt(clock); - Timer1.setPeriod(period); - - // init outputs - out1.setLen(period); - out2.setLen(period); - out3.setLen(period); - out4.setLen(period); - out5.setLen(period); - out6.setLen(period); - out7.setLen(period); - out8.setLen(period); - - // manual setup - out1.setDiv(8, 0); - out1.setWidth(50); - - out2.setDiv(4, 0); - out2.setWidth(50); - - out3.setDiv(2, 0); - out3.setWidth(50); - - out4.setDiv(1); - out4.setWidth(50); - - out5.setDiv(2); - out5.setWidth(50); - - out6.setDiv(4); - out6.setWidth(50); - - out7.setDiv(8); - out7.setWidth(50); - - out8.setDiv(16); - out8.setWidth(50); -} - -void clock(void) { - if (play == 1) { - beatToggle = true; - } -} - -void loop() { - if (beatToggle) { - out1.turnOn(); - out2.turnOn(); - out3.turnOn(); - out4.turnOn(); - out5.turnOn(); - out6.turnOn(); - out7.turnOn(); - out8.turnOn(); - - beatToggle = false; - } - - out1.turnOff(); - out2.turnOff(); - out3.turnOff(); - out4.turnOff(); - out5.turnOff(); - out6.turnOff(); - out7.turnOff(); - out8.turnOff(); - - checkBtn(); - checkRPot(); - updateScreen(); -} - -void updateScreen() { - unsigned long currentTime = millis(); - if (currentTime - screenLastUpdate >= screenThrottle && forceScreenUpdate == 1) { - screenLastUpdate = currentTime; - forceScreenUpdate = 0; - u8g2.firstPage(); - do { - // 12px - nonbold - if (mainScreenCursorPos == 0) { - u8g2.setFont(u8g2_font_7x14B_mf); - } else { - u8g2.setFont(u8g2_font_7x14_mf); - } - - u8g2.setCursor(0, 15); - u8g2.print(F("BPM: ")); - u8g2.print(BPM); - - u8g2.setFont(u8g2_font_7x14_mf); - u8g2.setCursor(0, 45); - - char buffer[5]; - for (byte i = 1; i < 9; i++) { - if (i == 5) { - u8g2.setCursor(0, 60); - } - - if (mainScreenCursorPos == i) { - u8g2.setFont(u8g2_font_7x14B_mf); - } - - sprintf(buffer, "[%d] ", i); - u8g2.print(buffer); - - if (mainScreenCursorPos == i) { - u8g2.setFont(u8g2_font_7x14_mf); - } - }; - } while (u8g2.nextPage()); - } -} - -void checkBtn() { - byte currentBtnState = digitalRead(btnPin); - if (currentBtnState != btnState) { - if (currentBtnState == 1) { - play = (play == 1) ? 0 : 1; - }; - } - btnState = currentBtnState; -}; - -void checkRPot() { - if (millis() - rPotLastUpdate >= rPotThrottle) { - - noInterrupts(); - long currentPos = rPotPos; - interrupts(); - - // Print position only if it has changed - static long previousPos = -999; - if (currentPos != previousPos) { - Serial.print("Encoder Position: "); - Serial.println(currentPos); - - // on home screen - if (currentScreen == 0) { - if (editMode == 0) { - if (currentPos > previousPos) { - mainScreenCursorPos++; - if (mainScreenCursorPos > maxMainScreenPos) { - mainScreenCursorPos = 0; - } - } else { - mainScreenCursorPos--; - if (mainScreenCursorPos < 0) { - mainScreenCursorPos = maxMainScreenPos; - } - } - } - } - - forceScreenUpdate = 1; - - previousPos = currentPos; - } - - rPotLastUpdate = millis(); // Reset the timer for throttling the output - } -} - -void handleBPMChange(byte increase) { - if (increase == 1) { - BPM++; - } else { - BPM--; - } - - period = (minute / BPM) / ppqn; - Timer1.setPeriod(period); -} - -void updateEncoder() { - // The ISR should be as short and fast as possible - // Check the state of the DT pin to determine direction - if (digitalRead(3) == LOW) { - rPotPos++; // Clockwise - } else { - rPotPos--; // Counter-clockwise - } -} diff --git a/src/DisplayHandler.cpp b/src/DisplayHandler.cpp index 466a3f7..85b30b0 100644 --- a/src/DisplayHandler.cpp +++ b/src/DisplayHandler.cpp @@ -43,412 +43,338 @@ void DisplayHandler::setup() { display->setOrientation(0); } -void DisplayHandler::moveCursor(bool dir) { - if (onOutScreen) { - if (cursorClick == 0) { - if (dir == 1) { - cursorPosition++; - currentScreen++; - } else { - cursorPosition--; - currentScreen--; - } - - if (cursorPosition > outMaxCursorPosition) { - cursorPosition = 0; - currentScreen = 0; - } - - if (cursorPosition < 0) { - cursorPosition = outMaxCursorPosition; - currentScreen = outMaxCursorPosition; - } - } else { // click = 1 - if (currentScreen == 1) { // mod screen - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->modifierSelectionIndex++; - } else { - outputs[currentOut]->modifierSelectionIndex--; - } - - if (outputs[currentOut]->modifierSelectionIndex < 0) { - outputs[currentOut]->modifierSelectionIndex = 0; - } - - if (outputs[currentOut]->modifierSelectionIndex > - std::size(MOD_TYPES) - 1) { - outputs[currentOut]->modifierSelectionIndex = - std::size(MOD_TYPES) - 1; - } - - } else if (currentScreen == 2) { // shape control - int currentShape = (int)outputs[currentOut]->shape; - - if (dir == 1) { - currentShape++; - } else { - currentShape--; - } - - if (currentShape >= SHAPE_COUNT) { - currentShape = 0; - } - - if (currentShape < 0) { - currentShape = SHAPE_COUNT - 1; - } - - outputs[currentOut]->shape = (WaveShape)currentShape; - outputs[currentOut]->writeAnalog(0); - - } else if (currentScreen == 3) { // level control - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->level++; - } else { - outputs[currentOut]->level--; - } - - if (outputs[currentOut]->level > 100) { - outputs[currentOut]->level = 100; - } - - if (outputs[currentOut]->level < 1) { - outputs[currentOut]->level = 1; - } - - } else if (currentScreen == 4) { // width control - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->width++; - } else { - outputs[currentOut]->width--; - } - - if (outputs[currentOut]->width > 100) { - outputs[currentOut]->width = 100; - } - - if (outputs[currentOut]->width < 1) { - outputs[currentOut]->width = 1; - } - - outputs[currentOut]->setWidth(outputs[currentOut]->width); - - } else if (currentScreen == 5) { // SWING - - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->swing++; - } else { - outputs[currentOut]->swing--; - } - - if (outputs[currentOut]->swing > 100) { - outputs[currentOut]->swing = 100; - } - - if (outputs[currentOut]->swing < 50) { - outputs[currentOut]->swing = 50; - } - } else if (currentScreen == 6) { // PROBABILITY - - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->p++; - } else { - outputs[currentOut]->p--; - } - - if (outputs[currentOut]->p > 100) { - outputs[currentOut]->p = 100; - } - - if (outputs[currentOut]->p < 0) { - outputs[currentOut]->p = 0; - } - } else if (currentScreen == 7) { // STICKY - outputs[currentOut]->sticky ^= true; - - } else if (currentScreen == 8) { // CV1 Active - matrix.slots[outputs[currentOut]->slotIdx1].active ^= true; - - } else if (currentScreen == 9) { // CV1 Source - - if (dir == 1) { - matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx++; - if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx == - currentOut) { - matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx++; - } - } else { - matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx--; - if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx == - currentOut) { - matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx--; - } - } - - if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx > 7) { - matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx = 0; - } - - if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx < 0) { - matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx = 7; - } - - } else if (currentScreen == 10) { // CV1 DEST - int currentDest = - (int)matrix.slots[outputs[currentOut]->slotIdx1].destParam; - - if (dir == 1) { - currentDest++; - } else { - currentDest--; - } - - if (currentDest >= DEST_COUNT) { - currentDest = 0; - } - - if (currentDest < 0) { - currentDest = DEST_COUNT - 1; - } - - matrix.slots[outputs[currentOut]->slotIdx1].destParam = - (ModDest)currentDest; - - } else if (currentScreen == 11) { // CV1 AMT - if (dir == 1) { - matrix.slots[outputs[currentOut]->slotIdx1].amount++; - } else { - matrix.slots[outputs[currentOut]->slotIdx1].amount--; - } - - } else if (currentScreen == 12) { // CV1 INV - matrix.slots[outputs[currentOut]->slotIdx1].inverted ^= true; - - } else if (currentScreen == 13) { // CV2 Active - matrix.slots[outputs[currentOut]->slotIdx2].active ^= true; - - } else if (currentScreen == 14) { // CV2 Source - - if (dir == 1) { - matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx++; - - } else { - matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx--; - } - - if (matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx > 7) { - matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx = 0; - } - - if (matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx < 0) { - matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx = 7; - } - - } else if (currentScreen == 15) { // CV2 DEST - int currentDest = - (int)matrix.slots[outputs[currentOut]->slotIdx2].destParam; - - if (dir == 1) { - currentDest++; - } else { - currentDest--; - } - - if (currentDest >= DEST_COUNT) { - currentDest = 0; - } - - if (currentDest < 0) { - currentDest = DEST_COUNT - 1; - } - - matrix.slots[outputs[currentOut]->slotIdx2].destParam = - (ModDest)currentDest; - - } else if (currentScreen == 16) { // CV2 AMT - if (dir == 1) { - matrix.slots[outputs[currentOut]->slotIdx2].amount++; - } else { - matrix.slots[outputs[currentOut]->slotIdx2].amount--; - } - - } else if (currentScreen == 17) { // CV2 INV - matrix.slots[outputs[currentOut]->slotIdx2].inverted ^= true; - - } else if (currentScreen == 18) { // MUTE - - outputs[currentOut]->editing = 1; - outputs[currentOut]->isEnabled ^= true; - } - } +uint8_t DisplayHandler::modifyParameter(uint8_t dir, int8_t target, uint8_t clampMax, uint8_t clampMin, bool overflow) { + if (dir == 1) { + target++; } else { - if (cursorPosition == 0 && cursorClick == 1) { // Engage BPM on Main Screen - update_BPM(dir); - } else { - if (dir == 1) { - cursorPosition++; - } else { - cursorPosition--; - } - - if (!onSettingsScreen) { - if (cursorPosition > mainMaxCursorPosition) { - cursorPosition = 0; - } - - if (cursorPosition < 0) { - cursorPosition = mainMaxCursorPosition; - } - } else if (onSettingsScreen) { - - if (cursorPosition > 5) { - cursorPosition = 0; - } - - if (cursorPosition < 0) { - cursorPosition = 5; - } - } - } + target--; } + if (target > clampMax) { + if (overflow) { + target = clampMin; + } else { + target = clampMax; + } + } + + if (target < clampMin) { + if (overflow) { + target = clampMax; + } else { + target = clampMin; + } + } + + return target; +} + +void DisplayHandler::handleCursorOnMainScreen(bool dir) { + if (cursorPosition == 0 && cursorClick == 1) { // Engage BPM on Main Screen + update_BPM(dir); + } else { + cursorPosition = modifyParameter(dir, cursorPosition, mainMaxCursorPosition); + } +} + +void DisplayHandler::handleCursorOnOutputScreen(bool dir) { + if (cursorClick == 0) { // No click, scroll through options + currentScreen = modifyParameter(dir, cursorPosition, outMaxCursorPosition, 0); + cursorPosition = currentScreen; + } else { + if (currentScreen == 1) { // mod screen + outputs[currentOut]->editing = 1; + + outputs[currentOut]->modifierSelectionIndex = modifyParameter(dir, outputs[currentOut]->modifierSelectionIndex, std::size(MOD_TYPES) - 1); + + } else if (currentScreen == 2) { // shape control + int8_t currentShape = (int8_t)outputs[currentOut]->shape; + + currentShape = modifyParameter(dir, currentShape, SHAPE_COUNT -1, 0); + + outputs[currentOut]->shape = (WaveShape)currentShape; + outputs[currentOut]->writeAnalog(0); + + } else if (currentScreen == 3) { // level control + outputs[currentOut]->editing = 1; + outputs[currentOut]->level = modifyParameter(dir, outputs[currentOut]->level, 100, 1, false); + + } else if (currentScreen == 4) { // width control + outputs[currentOut]->editing = 1; + outputs[currentOut]->width = modifyParameter(dir, outputs[currentOut]->width, 100, 1, false); + outputs[currentOut]->setWidth(outputs[currentOut]->width); + + } else if (currentScreen == 5) { // SWING + + outputs[currentOut]->editing = 1; + outputs[currentOut]->swing = modifyParameter(dir, outputs[currentOut]->swing, 75, 50, false); + } else if (currentScreen == 6) { // PROBABILITY + outputs[currentOut]->editing = 1; + outputs[currentOut]->p = modifyParameter(dir, outputs[currentOut]->p, 100, 0, false); + } else if (currentScreen == 7) { // STICKY + outputs[currentOut]->sticky ^= true; + + } else if (currentScreen == 8) { // CV1 Active + matrix.slots[outputs[currentOut]->slotIdx1].active ^= true; + + } else if (currentScreen == 9) { // CV1 Source + + if (dir == 1) { + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx++; + if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx == + currentOut) { + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx++; + } + } else { + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx--; + if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx == + currentOut) { + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx--; + } + } + + if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx > 7) { + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx = 0; + } + + if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx < 0) { + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx = 7; + } + + } else if (currentScreen == 10) { // CV1 DEST + int currentDest = + (int)matrix.slots[outputs[currentOut]->slotIdx1].destParam; + + if (dir == 1) { + currentDest++; + } else { + currentDest--; + } + + if (currentDest >= DEST_COUNT) { + currentDest = 0; + } + + if (currentDest < 0) { + currentDest = DEST_COUNT - 1; + } + + matrix.slots[outputs[currentOut]->slotIdx1].destParam = (ModDest)currentDest; + + } else if (currentScreen == 11) { // CV1 AMT + matrix.slots[outputs[currentOut]->slotIdx1].amount = modifyParameter(dir, matrix.slots[outputs[currentOut]->slotIdx1].amount, 100, 0, false); + + } else if (currentScreen == 12) { // CV1 INV + matrix.slots[outputs[currentOut]->slotIdx1].inverted ^= true; + + } else if (currentScreen == 13) { // CV2 Active + matrix.slots[outputs[currentOut]->slotIdx2].active ^= true; + + } else if (currentScreen == 14) { // CV2 Source + + if (dir == 1) { + matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx++; + + } else { + matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx--; + } + + if (matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx > 7) { + matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx = 0; + } + + if (matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx < 0) { + matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx = 7; + } + + } else if (currentScreen == 15) { // CV2 DEST + int currentDest = + (int)matrix.slots[outputs[currentOut]->slotIdx2].destParam; + + if (dir == 1) { + currentDest++; + } else { + currentDest--; + } + + if (currentDest >= DEST_COUNT) { + currentDest = 0; + } + + if (currentDest < 0) { + currentDest = DEST_COUNT - 1; + } + + matrix.slots[outputs[currentOut]->slotIdx2].destParam = + (ModDest)currentDest; + + } else if (currentScreen == 16) { // CV2 AMT + matrix.slots[outputs[currentOut]->slotIdx2].amount = modifyParameter(dir, matrix.slots[outputs[currentOut]->slotIdx2].amount, 100, 0, false); + + } else if (currentScreen == 17) { // CV2 INV + matrix.slots[outputs[currentOut]->slotIdx2].inverted ^= true; + + } else if (currentScreen == 18) { // MUTE + outputs[currentOut]->isEnabled ^= true; + } + } +} + +void DisplayHandler::moveCursor(bool dir) { + if (onOutScreen) { + handleCursorOnOutputScreen(dir); + } + + if (onMainScreen) { + handleCursorOnMainScreen(dir); + } + + if (onSettingsScreen) { + cursorPosition = modifyParameter(dir, cursorPosition, 5, 0); + } updateScreen = 1; } +void DisplayHandler::handleClickOnOutputScreen() { + if (currentScreen == 0) { // exit screen + cursorPosition = currentOut + 1; + onMainScreen = true; + onOutScreen = false; + onSettingsScreen = false; + currentOut = -1; + currentScreen = 0; + cursorClick = false; + } + + // move this out to the other group + if (currentScreen == 1 && outputs[currentOut]->editing == 1) { + outputs[currentOut]->setDiv(outputs[currentOut]->modifierSelectionIndex); + outputs[currentOut]->editing = 0; + cursorClick = false; + } + updateScreen = 1; +} + + +void DisplayHandler::handleClickOnMainScreen() { + if (cursorPosition > 0 && cursorPosition < 9) { // GO TO OUTPUT + currentOut = cursorPosition - 1; + cursorPosition = 1; + currentScreen = 1; + onOutScreen = true; + onMainScreen = false; + onSettingsScreen = false;; + } else if (cursorPosition == 9) { // PLAY/PAUSE BUTTON + PLAY ^= true; + } else if (cursorPosition == 10) { // GLOBAL SETTINGS + cursorPosition = 0; + onSettingsScreen = true; + onMainScreen = false; + onOutScreen = false; + } + cursorClick = 0; +} + + +void DisplayHandler::handleClickOnSettingsScreen() { + if (cursorPosition == 0) { // SAVE + flashMessage("Saving..."); + + for (int i = 0; i < 8; i++) { + outputs[i]->pack(globalSettings.configs[i]); + } + + save(); + + exitSettingsScreen(); + } else if (cursorPosition == 1) { // LOAD + flashMessage("Loading..."); + PLAY = false; + bool loaded_defaults = load(); + + if (!loaded_defaults) { + for (int i = 0; i < 16; i++) { + matrix.slots[i] = globalSettings.slots[i]; + } + + BPM = globalSettings.bpm; + PLAY = globalSettings.play; + RUN = globalSettings.run; + EXTPPQNIdx = globalSettings.ppqnidx; + } + + for (int i = 0; i < 8; i++) { + outputs[i]->unpack(globalSettings.configs[i]); + } + + exitSettingsScreen(); + } else if (cursorPosition == 2) { // RESET + flashMessage("Resetting..."); + PLAY = false; + load_default(); + + for (int i = 0; i < 8; i++) { + outputs[i]->unpack(globalSettings.configs[i]); + } + + for (int i = 0; i < sizeof(outputs); i++) { + outputs[i]->setupPatches(); + } + + PLAY = true; + exitSettingsScreen(); + } else if (cursorPosition == 3) { // PPQN + EXTPPQNIdx++; + + if (EXTPPQNIdx >= PPQN_OPTS_LEN) { + EXTPPQNIdx = 0; + } + + } else if (cursorPosition == 4) { // RUN + RUN ^= true; + + if (RUN) { + PLAY = false; + } + + } else if (cursorPosition == 5) { // EXIT + exitSettingsScreen(); + } + cursorClick = 0; +} + +void DisplayHandler::exitSettingsScreen() { + onOutScreen = false; + onSettingsScreen = false; + onMainScreen = true; + + cursorPosition = 10; + cursorClick = 0; + currentScreen = 0; +} + void DisplayHandler::handleClick() { cursorClick ^= true; - if (onOutScreen && !onSettingsScreen) { - if (currentScreen == 0) { // exit screen - cursorPosition = currentOut + 1; - currentOut = -1; - currentScreen = 0; - onOutScreen = 0; - cursorClick = false; - } - - if (currentScreen == 1 && outputs[currentOut]->editing == 1) { - outputs[currentOut]->setDiv(outputs[currentOut]->modifierSelectionIndex); - outputs[currentOut]->editing = 0; - cursorClick = false; - } - } else { - - if (currentScreen == 0 && !onSettingsScreen) { // on main screen - if (cursorPosition == 0) { // Change BPM - - } else if (cursorPosition > 0 && cursorPosition < 9) { // go to out screen - currentOut = cursorPosition - 1; - cursorPosition = 1; - currentScreen = 1; - onOutScreen = 1; - cursorClick = 0; - } else if (cursorPosition == 9) { // PLAY/PAUSE BUTTON - PLAY ^= true; - } else if (cursorPosition == 10) { // GLOBAL SETTINGS - cursorPosition = 0; - onSettingsScreen = 1; - cursorClick = 0; - } - } else if (onSettingsScreen) { - if (cursorPosition == 0) { // SAVE - flashMessage("Saving..."); - - for (int i = 0; i < 8; i++) { - outputs[i]->pack(globalSettings.configs[i]); - } - - globalSettings.bpm = BPM; - globalSettings.play = PLAY; - globalSettings.run = RUN; - globalSettings.ppqnidx = EXTPPQNIdx; - - memcpy(globalSettings.slots, matrix.slots, sizeof(ModSlot) * 16); - - save(); - - exitSettingsScreen(); - } else if (cursorPosition == 1) { // LOAD - flashMessage("Loading..."); - PLAY = false; - bool loaded_defaults = load(); - - if (!loaded_defaults) { - for (int i = 0; i < 16; i++) { - matrix.slots[i] = globalSettings.slots[i]; - } - - BPM = globalSettings.bpm; - PLAY = globalSettings.play; - RUN = globalSettings.run; - EXTPPQNIdx = globalSettings.ppqnidx; - } - - for (int i = 0; i < 8; i++) { - outputs[i]->unpack(globalSettings.configs[i]); - } - - exitSettingsScreen(); - } else if (cursorPosition == 2) { // RESET - flashMessage("Resetting..."); - PLAY = false; - load_default(); - - for (int i = 0; i < 8; i++) { - outputs[i]->unpack(globalSettings.configs[i]); - } - - for (int i = 0; i < sizeof(outputs); i++) { - outputs[i]->setupPatches(); - } - - PLAY = true; - exitSettingsScreen(); - } else if (cursorPosition == 3) { // PPQN - EXTPPQNIdx++; - - if (EXTPPQNIdx >= PPQN_OPTS_LEN) { - EXTPPQNIdx = 0; - } - - } else if (cursorPosition == 4) { // RUN - RUN ^= true; - - if (RUN) { - PLAY = false; - } - - } else if (cursorPosition == 5) { // EXIT - exitSettingsScreen(); - } - } - } + if (onOutScreen) { + handleClickOnOutputScreen(); + } else if (onMainScreen) { // on main screen + handleClickOnMainScreen(); + } else if (onSettingsScreen) { + handleClickOnSettingsScreen(); + } updateScreen = 1; } -void DisplayHandler::exitSettingsScreen() { - onOutScreen = 0; - onSettingsScreen = 0; - cursorPosition = 10; - cursorClick = 0; - currentScreen = 0; -} - void DisplayHandler::render() { if (updateScreen) { display->clear(); - if (!onSettingsScreen && currentScreen == 0 && - currentOut == -1) { // main screen + if (onMainScreen) { // main screen renderMainPage(); - } else if (!onSettingsScreen && currentOut != -1) { + } else if (onOutScreen) { renderOutPage(); } else if (onSettingsScreen) { renderSettingsPage(); diff --git a/src/Settings.cpp b/src/Settings.cpp index 9fc3912..1ed2f24 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -10,6 +10,14 @@ DeviceSettings globalSettings; void save() { + + globalSettings.bpm = BPM; + globalSettings.play = PLAY; + globalSettings.run = RUN; + globalSettings.ppqnidx = EXTPPQNIdx; + + memcpy(globalSettings.slots, matrix.slots, sizeof(ModSlot) * 16); + const uint32_t WRITE_SIZE = (sizeof(DeviceSettings) + 255) & ~255; static uint8_t __attribute__((aligned(4))) write_buf[2048]; @@ -30,6 +38,8 @@ void save() { multicore_lockout_end_blocking(); } + + void load_default() { memset(&globalSettings.slots, 0, sizeof(globalSettings.slots)); -- 2.45.2 From 5e500086f0471b50270664ec2eb51b14f79cab11 Mon Sep 17 00:00:00 2001 From: Dominic DiTaranto Date: Fri, 13 Mar 2026 19:15:21 -0400 Subject: [PATCH 2/2] fix BPM, fix gates not closing, add experimental clk in --- include/DisplayHandler.h | 1 + include/globals.h | 4 ++ src/DisplayHandler.cpp | 16 ++++-- src/Gate.cpp | 4 ++ src/main.cpp | 107 ++++++++++++++++++++++++++++----------- 5 files changed, 99 insertions(+), 33 deletions(-) diff --git a/include/DisplayHandler.h b/include/DisplayHandler.h index 57beb5d..a3a83e1 100644 --- a/include/DisplayHandler.h +++ b/include/DisplayHandler.h @@ -38,6 +38,7 @@ class DisplayHandler { bool onOutScreen = false; bool onMainScreen = true; bool onSettingsScreen = false; + bool engageBPM = false; std::array settings = { "Save", diff --git a/include/globals.h b/include/globals.h index 5e85ee9..b11ecd3 100644 --- a/include/globals.h +++ b/include/globals.h @@ -18,6 +18,7 @@ static constexpr uint8_t OUT_6_PIN = 10; static constexpr uint8_t OUT_7_PIN = 12; static constexpr uint8_t OUT_8_PIN = 14; +static constexpr uint8_t IN_CLK_PIN = 16; static constexpr uint8_t IN_RUN_PIN = 17; static constexpr uint8_t SCREEN_SCL_PIN = 18; @@ -44,6 +45,9 @@ extern volatile bool RUN; extern volatile uint8_t EXTPPQNIdx; #define PPQN_OPTS_LEN 5 extern const uint16_t PPQNOPTS[PPQN_OPTS_LEN]; +extern volatile uint64_t last_clk_us; +extern volatile uint64_t last_valid_clk_us; +extern volatile bool EXTERNAL_CLOCK; enum WaveShape { SQUARE, TRIANGLE, SAW, RAMP, EXP, HALFSINE, REXP, LOG, SINE, BOUNCE, SIGMO, WOBBLE, STEPDW, STEPUP, SH, SHAPE_COUNT}; diff --git a/src/DisplayHandler.cpp b/src/DisplayHandler.cpp index 85b30b0..be1bdf4 100644 --- a/src/DisplayHandler.cpp +++ b/src/DisplayHandler.cpp @@ -258,22 +258,32 @@ void DisplayHandler::handleClickOnOutputScreen() { void DisplayHandler::handleClickOnMainScreen() { - if (cursorPosition > 0 && cursorPosition < 9) { // GO TO OUTPUT + if (cursorPosition == 0) { + if (engageBPM) { + cursorClick = 0; + engageBPM = false; + } else { + engageBPM = true; + cursorClick = 1; + } + } if (cursorPosition > 0 && cursorPosition < 9) { // GO TO OUTPUT currentOut = cursorPosition - 1; cursorPosition = 1; currentScreen = 1; onOutScreen = true; onMainScreen = false; - onSettingsScreen = false;; + onSettingsScreen = false; + cursorClick = 0; } else if (cursorPosition == 9) { // PLAY/PAUSE BUTTON PLAY ^= true; + cursorClick = 0; } else if (cursorPosition == 10) { // GLOBAL SETTINGS cursorPosition = 0; onSettingsScreen = true; onMainScreen = false; onOutScreen = false; + cursorClick = 0; } - cursorClick = 0; } diff --git a/src/Gate.cpp b/src/Gate.cpp index e8e7478..2a1bdcf 100644 --- a/src/Gate.cpp +++ b/src/Gate.cpp @@ -293,6 +293,10 @@ void Gate::update() { if (elapsedUs >= pulseDurationUs) { state = 0; + if (width < 100) { + scheduledTick = 0xFFFFFFFF; + lastTriggerTick = 0xFFFFFFFF; + } if (!sticky) writeAnalog(0); return; diff --git a/src/main.cpp b/src/main.cpp index 5e5198e..4b3dc06 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include "DisplayHandler.h" @@ -26,6 +28,9 @@ volatile uint32_t MASTER_TICK; volatile bool RUN = false; const uint16_t PPQNOPTS[] = {1, 2, 4, 24, 48}; volatile uint8_t EXTPPQNIdx = 0; +volatile uint64_t last_clk_us = 0; +volatile uint64_t last_valid_clk_us; +volatile bool EXTERNAL_CLOCK = false; ModMatrix matrix; @@ -75,6 +80,12 @@ void update_BPM(bool up) { for (auto g : outputs) { g->setWidth(g->width); } + + if (!EXTERNAL_CLOCK) { + init_timer(period_us); + } else { + cancel_repeating_timer(&bpm_timer); + } } void core1_entry() { @@ -93,10 +104,10 @@ void full_save() { outputs[i]->pack(globalSettings.configs[i]); } - globalSettings.bpm = BPM; - globalSettings.play = PLAY; - globalSettings.run = RUN; - globalSettings.ppqnidx = EXTPPQNIdx; + globalSettings.bpm = BPM; + globalSettings.play = PLAY; + globalSettings.run = RUN; + globalSettings.ppqnidx = EXTPPQNIdx; memcpy(globalSettings.slots, matrix.slots, sizeof(ModSlot) * 16); @@ -130,20 +141,49 @@ void handle_outs() { } void gpio_callback(uint gpio, uint32_t events) { + // CLK LOGIC + if (gpio == IN_CLK_PIN && (events & GPIO_IRQ_EDGE_RISE)) { + uint64_t now = to_us_since_boot(get_absolute_time()); - if (gpio == IN_RUN_PIN) { - if (RUN) { - if (events & GPIO_IRQ_EDGE_RISE) { - PLAY = true; - } else if (events & GPIO_IRQ_EDGE_FALL) { - PLAY = false; - } - } - } + if (now - last_valid_clk_us < 5000) { + return; + } + last_valid_clk_us = now; + + uint16_t incomingPPQN; + if (last_clk_us > 0) { + uint64_t diff = now - last_clk_us; + incomingPPQN = PPQNOPTS[EXTPPQNIdx]; + float calculatedBPM = 60000000.0f / (float)(diff * incomingPPQN); + + if (calculatedBPM >= 30 && calculatedBPM <= 255) { + if (fabsf((float)BPM - calculatedBPM) > 0.5f) { + BPM = (uint8_t)(calculatedBPM + 0.5f); + + update_period(); + for (auto g : outputs) { + g->setWidth(g->width); + } + } + } + } + MASTER_TICK += (PPQN / incomingPPQN); + last_clk_us = now; + } + + if (gpio == IN_RUN_PIN) { + if (RUN) { + if (events & GPIO_IRQ_EDGE_RISE) { + PLAY = true; + } else if (events & GPIO_IRQ_EDGE_FALL) { + PLAY = false; + } + } + } if (gpio == ENCODER_SW_PIN) { - uint64_t now = to_us_since_boot(get_absolute_time()); - static uint64_t last_sw_time = 0; + uint64_t now = to_us_since_boot(get_absolute_time()); + static uint64_t last_sw_time = 0; if (now - last_sw_time > 200000) { // 200ms debounce display_handler.handleClick(); last_sw_time = now; @@ -152,15 +192,22 @@ void gpio_callback(uint gpio, uint32_t events) { } void setup_ins() { - // SETUP RUN - gpio_init(IN_RUN_PIN); - gpio_set_dir(IN_RUN_PIN, GPIO_IN); - gpio_pull_down(IN_RUN_PIN); - gpio_set_irq_enabled_with_callback(IN_RUN_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback); + // SETUP RUN + gpio_init(IN_RUN_PIN); + gpio_set_dir(IN_RUN_PIN, GPIO_IN); + gpio_pull_down(IN_RUN_PIN); + gpio_set_irq_enabled_with_callback(IN_RUN_PIN, + GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, + true, &gpio_callback); - // SETUP CLOCK + // SETUP CLOCK + gpio_init(IN_CLK_PIN); + gpio_set_dir(IN_CLK_PIN, GPIO_IN); + gpio_pull_down(IN_CLK_PIN); + // Add to existing callback + gpio_set_irq_enabled(IN_CLK_PIN, GPIO_IRQ_EDGE_RISE, true); - // SETUP CV INS + // SETUP CV INS } int main() { @@ -179,10 +226,10 @@ int main() { matrix.slots[i] = globalSettings.slots[i]; } - BPM = globalSettings.bpm; - PLAY = globalSettings.play; - RUN = globalSettings.run; - EXTPPQNIdx = globalSettings.ppqnidx; + BPM = globalSettings.bpm; + PLAY = globalSettings.play; + RUN = globalSettings.run; + EXTPPQNIdx = globalSettings.ppqnidx; } for (int i = 0; i < 8; i++) { @@ -197,11 +244,11 @@ int main() { bool lastPlayState = false; - setup_ins(); + setup_ins(); - if (RUN) { - PLAY = false; - } + if (RUN) { + PLAY = false; + } while (true) { encoder_handler.update(); -- 2.45.2