From 5f762f0907f336be84d61bdb06750c2fcaa1d34c Mon Sep 17 00:00:00 2001 From: Dominic DiTaranto Date: Mon, 9 Mar 2026 16:18:16 -0400 Subject: [PATCH] mod matrix and load/save initial functionality --- CMakeLists.txt | 2 + include/DisplayHandler.h | 22 +- include/EncoderHandler.h | 32 +- include/Gate.h | 102 ++++-- include/Mod.h | 39 +++ include/Output.h | 25 +- include/Settings.h | 32 ++ include/globals.h | 12 + src/DisplayHandler.cpp | 683 ++++++++++++++++++++++++--------------- src/EncoderHandler.cpp | 92 +++--- src/Gate.cpp | 111 ++++++- src/Mod.cpp | 53 +++ src/Output.cpp | 4 +- src/Settings.cpp | 66 ++++ src/main.cpp | 203 ++++++------ 15 files changed, 999 insertions(+), 479 deletions(-) create mode 100644 include/Mod.h create mode 100644 include/Settings.h create mode 100644 src/Mod.cpp create mode 100644 src/Settings.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f3e4941..26e4e0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ add_executable(clock src/main.cpp src/Output.cpp src/Gate.cpp + src/Mod.cpp + src/Settings.cpp src/DisplayHandler.cpp src/EncoderHandler.cpp ) diff --git a/include/DisplayHandler.h b/include/DisplayHandler.h index a978ebc..163a7ba 100644 --- a/include/DisplayHandler.h +++ b/include/DisplayHandler.h @@ -13,7 +13,27 @@ class DisplayHandler { char buffer[32]; uint8_t currentScreen; std::string screens[7]; - std::array out_pages = {"Exit", "Mod", "Shape", "Level", "Width", "Swing", "Prob", "Sticky", "Mute"}; + std::array out_pages = { + "Exit", + "Mod", + "Shape", + "Level", + "Width", + "Swing", + "Prob", + "Sticky", + "CV1 ON", + "CV1 SRC", + "CV1 TO", + "CV1 AMT", + "CV1 INV", + "CV2 ON", + "CV2 SRC", + "CV2 TO", + "CV2 AMT", + "CV2 INV", + "Mute" + }; bool onOutScreen = 0; void renderMainPage(); void renderOutPage(); diff --git a/include/EncoderHandler.h b/include/EncoderHandler.h index c3897d2..72ba174 100644 --- a/include/EncoderHandler.h +++ b/include/EncoderHandler.h @@ -2,30 +2,28 @@ #ifndef EncoderHandler_h #define EncoderHandler_h +#include "DisplayHandler.h" +#include "pico/multicore.h" #include #include -#include "pico/multicore.h" -#include "DisplayHandler.h" - class EncoderHandler { - private: - uint sm; - uint last_count; +private: + uint sm; + uint last_count; +public: + EncoderHandler(DisplayHandler *display_handler); - public: - EncoderHandler(DisplayHandler* display_handler); + DisplayHandler *display_handler; + uint8_t encoder_pos; + bool button_pressed; + uint16_t clk_last_state; - DisplayHandler* display_handler; - uint8_t encoder_pos; - bool button_pressed; - uint16_t clk_last_state; - - void setup(); - static void gpio_callback(uint gpio, uint32_t events); - void moveCursor(bool dir = 1); - void update(); + void setup(); + static void gpio_callback(uint gpio, uint32_t events); + void moveCursor(bool dir = 1); + void update(); }; #endif diff --git a/include/Gate.h b/include/Gate.h index cddfccb..3bbbebd 100644 --- a/include/Gate.h +++ b/include/Gate.h @@ -2,48 +2,84 @@ #ifndef Gate_h #define Gate_h -#include #include "Output.h" +#include "Settings.h" #include "globals.h" +#include +struct GateSettings { + uint32_t dur; + uint32_t len; + uint16_t modifier; + int8_t modifierSelectionIndex; + uint8_t divideMode; + uint8_t width; + uint8_t p; + uint8_t level; + uint8_t shape; +}; class Gate : public Output { - private: - uint32_t dur; - uint32_t triggerCount; - uint32_t scheduledTick; - float currentRandomVal; - uint32_t len; - uint32_t lastTriggerTick = 0xFFFFFFFF; - uint64_t startTimeUs; - uint32_t pulseDurationUs; +private: + float currentRandomVal; + uint32_t len; + uint64_t startTimeUs; + uint32_t pulseDurationUs; - public: - Gate(uint8_t pin); - - WaveShape shape = SQUARE; - uint32_t startTick = 0; - uint32_t pulseWidthTicks = 0; - bool sticky = false; - int8_t modifierSelectionIndex; - uint8_t divideMode; - uint8_t swing = 50; - uint16_t modifier; - uint16_t tickInterval; - uint8_t level; - uint8_t width; - uint8_t p; +public: + uint32_t dur; + Gate(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2); - void turnOn() override; - void update(); - void turnOff() override; + uint8_t modDest1; + uint8_t modDest2; - void calculatePulseWidth(); - void writeAnalog(uint16_t val); - void setLen(uint32_t currentPeriod); - void setDiv(uint8_t modifier_selection_index); - void setWidth(uint16_t newWidth); + uint8_t level; + float levelMod = 0.0f; + + uint8_t width; + float widthMod = 0.0f; + + uint8_t p; + float pMod = 0.0f; + + uint8_t swing = 50; + float swingMod = 0.0f; + + void resetMods() { + levelMod = 0.0f; + pMod = 0.0f; + widthMod = 0.0f; + swingMod = 0.0f; + } + + float lastOutVal = 0.0f; + uint32_t triggerCount; + uint32_t scheduledTick; + uint32_t lastTriggerTick = 0xFFFFFFFF; + + WaveShape shape = SQUARE; + uint32_t startTick = 0; + uint32_t pulseWidthTicks = 0; + bool sticky = false; + int8_t modifierSelectionIndex; + uint8_t divideMode; + uint16_t modifier; + uint16_t tickInterval; + + void turnOn() override; + void update(); + void turnOff() override; + + void calculatePulseWidth(); + void setupPatches(); + void writeAnalog(uint16_t val); + void setLen(uint32_t currentPeriod); + void setDiv(uint8_t modifier_selection_index); + void setWidth(uint16_t newWidth); + + void pack(OutputConfig &cfg); + void unpack(const OutputConfig &cfg); }; #endif diff --git a/include/Mod.h b/include/Mod.h new file mode 100644 index 0000000..b624968 --- /dev/null +++ b/include/Mod.h @@ -0,0 +1,39 @@ +// Mod.h +#ifndef Mod_h +#define Mod_h +#include + +class Gate; + +enum ModDest { + DEST_LEVEL = 0, + DEST_PROBABILITY = 1, + DEST_WIDTH = 2, + DEST_COUNT = 3 +}; + +struct ModSlot { + uint8_t sourceIdx; + uint8_t destIdx; + uint8_t destParam; + uint8_t amount; + uint8_t active; + uint8_t inverted; + uint8_t reserved1; + uint8_t reserved2; +}; + +class ModMatrix { +public: + ModSlot slots[16]; + + void patch(uint8_t slotIdx, uint8_t src, uint8_t dest, ModDest param, + float amt, bool active); + + void process(Gate **gates, uint8_t gateCount); + + void clearPatch(uint8_t slotIdx); + ; +}; + +#endif diff --git a/include/Output.h b/include/Output.h index daea529..609286d 100644 --- a/include/Output.h +++ b/include/Output.h @@ -4,20 +4,21 @@ #include - class Output { - private: +private: +public: + Output(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2); + bool state; + uint8_t pin; + uint8_t idx; + uint8_t slotIdx1; + uint8_t slotIdx2; + uint8_t editing; + bool isEnabled; - public: - Output(uint8_t pin); - bool state; - uint8_t pin; - uint8_t editing; - bool isEnabled; - - virtual ~Output() {}; - virtual void turnOn(); - virtual void turnOff(); + virtual ~Output() {}; + virtual void turnOn(); + virtual void turnOff(); }; #endif diff --git a/include/Settings.h b/include/Settings.h new file mode 100644 index 0000000..ea35fb1 --- /dev/null +++ b/include/Settings.h @@ -0,0 +1,32 @@ +// Settings.h +#ifndef Settings_h +#define Settings_h + +#include "Mod.h" +#include + +#define MAX_OUTPUTS 8 +#define DATA_PER_OUTPUT 64 + +enum OutputType : uint8_t { TYPE_GATE }; + +struct OutputConfig { + uint8_t type; + uint8_t _padding[3]; + alignas(4) uint8_t data[DATA_PER_OUTPUT]; +}; + +struct DeviceSettings { + uint32_t magic; + uint32_t version; + OutputConfig configs[MAX_OUTPUTS]; + ModSlot slots[16]; +}; + +void save(); +bool load(); +void load_default(); + +extern DeviceSettings globalSettings; + +#endif diff --git a/include/globals.h b/include/globals.h index 9ae888f..3035cc6 100644 --- a/include/globals.h +++ b/include/globals.h @@ -2,6 +2,7 @@ #define GLOBALS_H #include "pico/stdlib.h" +#include "Mod.h" #include #include #include @@ -66,6 +67,17 @@ static std::array MOD_TYPES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, static std::array MODIFIER_STRINGS = {"x32", "x16", "x12", "x8", "x6", "x4", "x3", "x2", "x0", "/1", "/2", "/3", "/4", "/6", "/8", "/16", "/32"}; +extern ModMatrix matrix; + +inline const char* modDestToString(ModDest modType) { + switch (modType) { + case DEST_LEVEL: return "LVL"; + case DEST_PROBABILITY: return "PROB"; + case DEST_WIDTH: return "WIDTH"; + default: return "?"; + } +} + inline uint32_t millis() { return to_ms_since_boot(get_absolute_time()); } diff --git a/src/DisplayHandler.cpp b/src/DisplayHandler.cpp index 1b851b5..eb6879f 100644 --- a/src/DisplayHandler.cpp +++ b/src/DisplayHandler.cpp @@ -1,354 +1,505 @@ // DisplayHandler.cpp -#include "pico/stdlib.h" #include "DisplayHandler.h" +#include "Mod.h" #include "globals.h" -#include +#include "pico/stdlib.h" #include +#include #include "hardware/i2c.h" -#include "pico-ssd1306/ssd1306.h" #include "pico-ssd1306/shapeRenderer/ShapeRenderer.h" +#include "pico-ssd1306/ssd1306.h" #include "pico-ssd1306/textRenderer/TextRenderer.h" - -pico_ssd1306::SSD1306* display = nullptr; +pico_ssd1306::SSD1306 *display = nullptr; extern void update_BPM(bool up); - -DisplayHandler::DisplayHandler(Gate* outputs[]) { - this->outputs = outputs; - currentScreen = 0; - currentOut = -1; - updateScreen = 1; - cursorPosition = 0; - mainMaxCursorPosition = 10; - outMaxCursorPosition = std::size(out_pages) - 1; - cursorClick = 0; +DisplayHandler::DisplayHandler(Gate *outputs[]) { + this->outputs = outputs; + currentScreen = 0; + currentOut = -1; + updateScreen = 1; + cursorPosition = 0; + mainMaxCursorPosition = 10; + outMaxCursorPosition = std::size(out_pages) - 1; + cursorClick = 0; } - void DisplayHandler::setup() { - i2c_init(i2c1, 400 * 1000); + i2c_init(i2c1, 400 * 1000); - gpio_set_function(SCREEN_SDA_PIN, GPIO_FUNC_I2C); - gpio_set_function(SCREEN_SCL_PIN, GPIO_FUNC_I2C); + gpio_set_function(SCREEN_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(SCREEN_SCL_PIN, GPIO_FUNC_I2C); - gpio_pull_up(SCREEN_SDA_PIN); - gpio_pull_up(SCREEN_SCL_PIN); + gpio_pull_up(SCREEN_SDA_PIN); + gpio_pull_up(SCREEN_SCL_PIN); - display = new pico_ssd1306::SSD1306(i2c1, 0x3C, pico_ssd1306::Size::W128xH64); - display->setOrientation(0); + display = new pico_ssd1306::SSD1306(i2c1, 0x3C, pico_ssd1306::Size::W128xH64); + display->setOrientation(0); } - void DisplayHandler::moveCursor(bool dir) { - if (onOutScreen) { - if (cursorClick == 0) { - if (dir == 1) { - cursorPosition++; - currentScreen++; - } else { - cursorPosition--; - currentScreen--; - } + if (onOutScreen) { + if (cursorClick == 0) { + if (dir == 1) { + cursorPosition++; + currentScreen++; + } else { + cursorPosition--; + currentScreen--; + } - if (cursorPosition > outMaxCursorPosition) { - cursorPosition = 0; - currentScreen = 0; - } + 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 (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 (outputs[currentOut]->modifierSelectionIndex < 0) { + outputs[currentOut]->modifierSelectionIndex = 0; + } - if (dir == 1) { - currentShape++; - } else { - currentShape--; - } + if (outputs[currentOut]->modifierSelectionIndex > + std::size(MOD_TYPES) - 1) { + outputs[currentOut]->modifierSelectionIndex = + std::size(MOD_TYPES) - 1; + } - if (currentShape >= SHAPE_COUNT) { - currentShape = 0; - } + } else if (currentScreen == 2) { // shape control + int currentShape = (int)outputs[currentOut]->shape; - if (currentShape < 0) { - currentShape = SHAPE_COUNT - 1; - } + if (dir == 1) { + currentShape++; + } else { + currentShape--; + } - outputs[currentOut]->shape = (WaveShape)currentShape; - outputs[currentOut]->writeAnalog(0); + if (currentShape >= SHAPE_COUNT) { + currentShape = 0; + } - } else if (currentScreen == 3) { // level control - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->level++; - } else { - outputs[currentOut]->level--; - } + if (currentShape < 0) { + currentShape = SHAPE_COUNT - 1; + } - if (outputs[currentOut]->level > 100) { - outputs[currentOut]->level = 100; - } + outputs[currentOut]->shape = (WaveShape)currentShape; + outputs[currentOut]->writeAnalog(0); - if (outputs[currentOut]->level < 1) { - outputs[currentOut]->level = 1; - } + } else if (currentScreen == 3) { // level control + outputs[currentOut]->editing = 1; + if (dir == 1) { + outputs[currentOut]->level++; + } else { + outputs[currentOut]->level--; + } - } else if (currentScreen == 4) { // width control - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->width++; - } else { - outputs[currentOut]->width--; - } + if (outputs[currentOut]->level > 100) { + outputs[currentOut]->level = 100; + } - if (outputs[currentOut]->width > 100) { - outputs[currentOut]->width = 100; - } + if (outputs[currentOut]->level < 1) { + outputs[currentOut]->level = 1; + } - if (outputs[currentOut]->width < 1) { - outputs[currentOut]->width = 1; - } + } else if (currentScreen == 4) { // width control + outputs[currentOut]->editing = 1; + if (dir == 1) { + outputs[currentOut]->width++; + } else { + outputs[currentOut]->width--; + } - outputs[currentOut]->setWidth(outputs[currentOut]->width); + if (outputs[currentOut]->width > 100) { + outputs[currentOut]->width = 100; + } - } else if (currentScreen == 5) { // SWING + if (outputs[currentOut]->width < 1) { + outputs[currentOut]->width = 1; + } - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->swing++; - } else { - outputs[currentOut]->swing--; - } + outputs[currentOut]->setWidth(outputs[currentOut]->width); - if (outputs[currentOut]->swing > 100) { - outputs[currentOut]->swing = 100; - } + } else if (currentScreen == 5) { // SWING - if (outputs[currentOut]->swing < 50) { - outputs[currentOut]->swing = 50; - } - } else if (currentScreen == 6) { // PROBABILITY + outputs[currentOut]->editing = 1; + if (dir == 1) { + outputs[currentOut]->swing++; + } else { + outputs[currentOut]->swing--; + } - outputs[currentOut]->editing = 1; - if (dir == 1) { - outputs[currentOut]->p++; - } else { - outputs[currentOut]->p--; - } + if (outputs[currentOut]->swing > 100) { + outputs[currentOut]->swing = 100; + } - if (outputs[currentOut]->p > 100) { - outputs[currentOut]->p = 100; - } + if (outputs[currentOut]->swing < 50) { + outputs[currentOut]->swing = 50; + } + } else if (currentScreen == 6) { // PROBABILITY - if (outputs[currentOut]->p < 0) { - outputs[currentOut]->p = 0; - } - } else if (currentScreen == 7) { // STICKY - outputs[currentOut]->sticky ^= true; + outputs[currentOut]->editing = 1; + if (dir == 1) { + outputs[currentOut]->p++; + } else { + outputs[currentOut]->p--; + } - } else if (currentScreen == 8) { // MUTE + if (outputs[currentOut]->p > 100) { + outputs[currentOut]->p = 100; + } - outputs[currentOut]->editing = 1; - outputs[currentOut]->isEnabled ^= true; + 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 (cursorPosition == 0 && cursorClick == 1) { // Engage BPM on Main Screen - update_BPM(dir); - } else { - if (dir == 1) { - cursorPosition++; - } else { - cursorPosition--; - } + } else if (currentScreen == 9) { // CV1 Source - if (cursorPosition > mainMaxCursorPosition) { - cursorPosition = 0; - } + 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 (cursorPosition < 0) { - cursorPosition = mainMaxCursorPosition; - } - } - } + if (matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx > 7) { + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx = 0; + } - updateScreen = 1; + 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; + } + } + + } else { + if (cursorPosition == 0 && cursorClick == 1) { // Engage BPM on Main Screen + update_BPM(dir); + } else { + if (dir == 1) { + cursorPosition++; + } else { + cursorPosition--; + } + + if (cursorPosition > mainMaxCursorPosition) { + cursorPosition = 0; + } + + if (cursorPosition < 0) { + cursorPosition = mainMaxCursorPosition; + } + } + } + + updateScreen = 1; } - void DisplayHandler::handleClick() { - cursorClick ^= true; + cursorClick ^= true; - if (onOutScreen) { - if (currentScreen == 0) { // exit screen - cursorPosition = currentOut + 1; - currentOut = -1; - currentScreen = 0; - onOutScreen = 0; - cursorClick = false; - } + if (onOutScreen) { + 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 == 1 && outputs[currentOut]->editing == 1) { + outputs[currentOut]->setDiv(outputs[currentOut]->modifierSelectionIndex); + outputs[currentOut]->editing = 0; + cursorClick = false; + } + } else { - if (currentScreen == 0) { // on main screen - if (cursorPosition == 0) { // Change BPM + if (currentScreen == 0) { // 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 > 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; + } + } + } - } - } - - updateScreen = 1; + updateScreen = 1; } - void DisplayHandler::render() { - if (updateScreen) { - display->clear(); + if (updateScreen) { + display->clear(); - if (currentScreen == 0 && currentOut == -1) { // main screen - renderMainPage(); - } else if (currentOut != -1) { - renderOutPage(); - } + if (currentScreen == 0 && currentOut == -1) { // main screen + renderMainPage(); + } else if (currentOut != -1) { + renderOutPage(); + } - display->sendBuffer(); + display->sendBuffer(); - updateScreen = 0; - } + updateScreen = 0; + } } - void DisplayHandler::renderMainPage() { - std::string bpm_string = "BPM: " + std::to_string(BPM); + std::string bpm_string = "BPM: " + std::to_string(BPM); - if (cursorPosition == 0) { - if (cursorClick == 1) { - pico_ssd1306::fillRect(display, 0, 0, 100, 18); - pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2, pico_ssd1306::WriteMode::SUBTRACT); + if (cursorPosition == 0) { + if (cursorClick == 1) { + pico_ssd1306::fillRect(display, 0, 0, 100, 18); + pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2, + pico_ssd1306::WriteMode::SUBTRACT); - } else { - pico_ssd1306::drawRect(display, 0, 0, 100, 18, pico_ssd1306::WriteMode::ADD); - pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2); + } else { + pico_ssd1306::drawRect(display, 0, 0, 100, 18, + pico_ssd1306::WriteMode::ADD); + pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2); + } + } else { + pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2); + } - } - } else { - pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2); + uint8_t cursor_x = 2; + uint8_t cursor_y = 25; - } + for (uint8_t i = 1; i < 9; i++) { + if (i == 5) { + cursor_x = 2; + cursor_y += 20; + } - uint8_t cursor_x = 2; - uint8_t cursor_y = 25; + if (cursorPosition == i) { + pico_ssd1306::drawRect(display, cursor_x - 2, cursor_y - 4, cursor_x + 14, + cursor_y + 16, pico_ssd1306::WriteMode::ADD); + } + pico_ssd1306::drawText(display, font_12x16, std::to_string(i).c_str(), + cursor_x, cursor_y); + cursor_x += 30; + } - for (uint8_t i = 1; i < 9; i++) { - if (i == 5) { - cursor_x = 2; - cursor_y += 20; - } - - if (cursorPosition == i) { - pico_ssd1306::drawRect(display, cursor_x - 2, cursor_y - 4, cursor_x + 14, cursor_y + 16, pico_ssd1306::WriteMode::ADD); - - } - pico_ssd1306::drawText(display, font_12x16, std::to_string(i).c_str(), cursor_x, cursor_y); - cursor_x += 30; - } - - - if (cursorPosition == 9) { // PLAY BUTTON - pico_ssd1306::fillRect(display, 120, 8, 130, 18, pico_ssd1306::WriteMode::ADD); - pico_ssd1306::drawText(display, font_8x8, PLAY ? ">" : "#", 120, 10, pico_ssd1306::WriteMode::SUBTRACT); - } else { - pico_ssd1306::drawText(display, font_8x8, PLAY ? ">" : "#", 120, 10); - } - - if (cursorPosition == 10) { // OPTIONS BUTTON - pico_ssd1306::fillRect(display, 120, 28, 130, 38, pico_ssd1306::WriteMode::ADD); - pico_ssd1306::drawText(display, font_8x8, "*", 120, 30, pico_ssd1306::WriteMode::SUBTRACT); - } else { - pico_ssd1306::drawText(display, font_8x8, "*", 120, 30); - } + if (cursorPosition == 9) { // PLAY BUTTON + pico_ssd1306::fillRect(display, 120, 8, 130, 18, + pico_ssd1306::WriteMode::ADD); + pico_ssd1306::drawText(display, font_8x8, PLAY ? ">" : "#", 120, 10, + pico_ssd1306::WriteMode::SUBTRACT); + } else { + pico_ssd1306::drawText(display, font_8x8, PLAY ? ">" : "#", 120, 10); + } + if (cursorPosition == 10) { // OPTIONS BUTTON + pico_ssd1306::fillRect(display, 120, 28, 130, 38, + pico_ssd1306::WriteMode::ADD); + pico_ssd1306::drawText(display, font_8x8, "*", 120, 30, + pico_ssd1306::WriteMode::SUBTRACT); + } else { + pico_ssd1306::drawText(display, font_8x8, "*", 120, 30); + } } - void DisplayHandler::renderOutPage() { - uint8_t visualOut = currentOut + 1; - std::string title = std::to_string(visualOut) + "| " + out_pages[currentScreen]; - pico_ssd1306::drawText(display, font_12x16, title.c_str(), 1, 2); + uint8_t visualOut = currentOut + 1; + std::string title = + std::to_string(visualOut) + "| " + out_pages[currentScreen]; + pico_ssd1306::drawText(display, font_12x16, title.c_str(), 1, 2); - std::string param_string; + std::string param_string; - if (currentScreen == 0) { // exit screen - param_string = "<--"; + if (currentScreen == 0) { // exit screen + param_string = "<--"; - } else if (currentScreen == 1) { // modifier screen - uint8_t modifier_selection_index = outputs[currentOut]->modifierSelectionIndex; - param_string = MODIFIER_STRINGS[modifier_selection_index]; + } else if (currentScreen == 1) { // modifier screen + uint8_t modifier_selection_index = + outputs[currentOut]->modifierSelectionIndex; + param_string = MODIFIER_STRINGS[modifier_selection_index]; - } else if (currentScreen == 2) { // shape screen - param_string = waveShapeToString(outputs[currentOut]->shape); + } else if (currentScreen == 2) { // shape screen + param_string = waveShapeToString(outputs[currentOut]->shape); - } else if (currentScreen == 3) { // level screen - param_string = std::to_string(outputs[currentOut]->level) + "%"; + } else if (currentScreen == 3) { // level screen + param_string = std::to_string(outputs[currentOut]->level) + "%"; - } else if (currentScreen == 4) { // Width screen - param_string = std::to_string(outputs[currentOut]->width) + "%"; + } else if (currentScreen == 4) { // Width screen + param_string = std::to_string(outputs[currentOut]->width) + "%"; - } else if (currentScreen == 5) { // Swing screen - param_string = std::to_string(outputs[currentOut]->swing) + "%"; + } else if (currentScreen == 5) { // Swing screen + param_string = std::to_string(outputs[currentOut]->swing) + "%"; - } else if (currentScreen == 6) { // Probability screen - param_string = std::to_string(outputs[currentOut]->p) + "%"; + } else if (currentScreen == 6) { // Probability screen + param_string = std::to_string(outputs[currentOut]->p) + "%"; - } else if (currentScreen == 7) { // STICKY Screen - param_string = outputs[currentOut]->sticky ? "ON" : "OFF"; + } else if (currentScreen == 7) { // STICKY Screen + param_string = outputs[currentOut]->sticky ? "ON" : "OFF"; - } else if (currentScreen == 8) { // Mute Screen - param_string = outputs[currentOut]->isEnabled ? "ON" : "OFF"; - } + } else if (currentScreen == 8) { // CV1 Active Screen + param_string = + matrix.slots[outputs[currentOut]->slotIdx1].active ? "ON" : "OFF"; - if (cursorClick) { - pico_ssd1306::fillRect(display, 1, 42, 50, 60); - pico_ssd1306::drawText(display, font_12x16, param_string.c_str(), 1, 45, pico_ssd1306::WriteMode::SUBTRACT); - } else { - pico_ssd1306::drawText(display, font_12x16, param_string.c_str(), 1, 45); - } + } else if (currentScreen == 9) { // CV1 Source Chan Screen + param_string = std::to_string( + matrix.slots[outputs[currentOut]->slotIdx1].sourceIdx + 1); + } else if (currentScreen == 10) { // CV1 DEST Screen + param_string = modDestToString( + (ModDest)matrix.slots[outputs[currentOut]->slotIdx1].destParam); + + } else if (currentScreen == 11) { // CV1 AMT Screen + param_string = + std::to_string( + (int)matrix.slots[outputs[currentOut]->slotIdx1].amount) + + "%"; + + } else if (currentScreen == 12) { // CV1 INV Screen + param_string = + matrix.slots[outputs[currentOut]->slotIdx1].inverted ? "ON" : "OFF"; + + } else if (currentScreen == 13) { // CV2 Active Screen + param_string = + matrix.slots[outputs[currentOut]->slotIdx2].active ? "ON" : "OFF"; + + } else if (currentScreen == 14) { // CV2 Source Chan Screen + param_string = std::to_string( + matrix.slots[outputs[currentOut]->slotIdx2].sourceIdx + 1); + + } else if (currentScreen == 15) { // CV2 DEST Screen + param_string = modDestToString( + (ModDest)matrix.slots[outputs[currentOut]->slotIdx2].destParam); + + } else if (currentScreen == 16) { // CV2 AMT Screen + param_string = + std::to_string( + (int)matrix.slots[outputs[currentOut]->slotIdx2].amount) + + "%"; + + } else if (currentScreen == 17) { // CV1 INV Screen + param_string = + matrix.slots[outputs[currentOut]->slotIdx2].inverted ? "ON" : "OFF"; + + } else if (currentScreen == 18) { // Mute Screen + param_string = outputs[currentOut]->isEnabled ? "ON" : "OFF"; + } + + if (cursorClick) { + pico_ssd1306::fillRect(display, 1, 42, 50, 60); + pico_ssd1306::drawText(display, font_12x16, param_string.c_str(), 1, 45, + pico_ssd1306::WriteMode::SUBTRACT); + } else { + pico_ssd1306::drawText(display, font_12x16, param_string.c_str(), 1, 45); + } } diff --git a/src/EncoderHandler.cpp b/src/EncoderHandler.cpp index f6ccf67..77addb0 100644 --- a/src/EncoderHandler.cpp +++ b/src/EncoderHandler.cpp @@ -1,73 +1,71 @@ // EncoderHandler.cpp -#include "pico/stdlib.h" #include "EncoderHandler.h" #include "DisplayHandler.h" #include "globals.h" -#include -#include #include "hardware/pio.h" +#include "pico/stdlib.h" #include "quadrature_encoder.pio.h" +#include +#include +static EncoderHandler *self = nullptr; -static EncoderHandler* self = nullptr; - - -EncoderHandler::EncoderHandler(DisplayHandler* display_handler) { - this->display_handler = display_handler; - self = this; - encoder_pos = 0; - button_pressed = 0; +EncoderHandler::EncoderHandler(DisplayHandler *display_handler) { + this->display_handler = display_handler; + self = this; + encoder_pos = 0; + button_pressed = 0; } - void EncoderHandler::gpio_callback(uint gpio, uint32_t events) { - 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 (gpio == ENCODER_SW_PIN) { - if (now - last_sw_time > 200000) { // 200ms debounce - self->display_handler->handleClick(); - last_sw_time = now; - } + if (gpio == ENCODER_SW_PIN) { + if (now - last_sw_time > 200000) { // 200ms debounce + self->display_handler->handleClick(); + last_sw_time = now; } + } } void EncoderHandler::setup() { - self = this; + self = this; - gpio_init(ENCODER_SW_PIN); - gpio_set_dir(ENCODER_SW_PIN, GPIO_IN); - gpio_pull_up(ENCODER_SW_PIN); - - gpio_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, &EncoderHandler::gpio_callback); + gpio_init(ENCODER_SW_PIN); + gpio_set_dir(ENCODER_SW_PIN, GPIO_IN); + gpio_pull_up(ENCODER_SW_PIN); - PIO pio = pio0; - uint offset = pio_add_program(pio, &quadrature_encoder_program); - this->sm = pio_claim_unused_sm(pio, true); - - gpio_init(ENCODER_CLK_PIN); - gpio_pull_up(ENCODER_CLK_PIN); - gpio_init(ENCODER_DT_PIN); - gpio_pull_up(ENCODER_DT_PIN); + gpio_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, + &EncoderHandler::gpio_callback); - quadrature_encoder_program_init(pio, sm, ENCODER_CLK_PIN, 0); - - this->last_count = 0; + PIO pio = pio0; + uint offset = pio_add_program(pio, &quadrature_encoder_program); + this->sm = pio_claim_unused_sm(pio, true); + + gpio_init(ENCODER_CLK_PIN); + gpio_pull_up(ENCODER_CLK_PIN); + gpio_init(ENCODER_DT_PIN); + gpio_pull_up(ENCODER_DT_PIN); + + quadrature_encoder_program_init(pio, sm, ENCODER_CLK_PIN, 0); + + this->last_count = 0; } void EncoderHandler::update() { - int32_t current_count = quadrature_encoder_get_count(pio0, this->sm); - - int32_t delta = current_count - last_count; + int32_t current_count = quadrature_encoder_get_count(pio0, this->sm); - if (abs(delta) >= TICKS_PER_DETENT) { - - if (delta < 0) { - display_handler->moveCursor(); - } else { - display_handler->moveCursor(0); - } + int32_t delta = current_count - last_count; - last_count = current_count - (delta % TICKS_PER_DETENT); + if (abs(delta) >= TICKS_PER_DETENT) { + + if (delta < 0) { + display_handler->moveCursor(); + } else { + display_handler->moveCursor(0); } + + last_count = current_count - (delta % TICKS_PER_DETENT); + } } diff --git a/src/Gate.cpp b/src/Gate.cpp index 5e45451..e8e7478 100644 --- a/src/Gate.cpp +++ b/src/Gate.cpp @@ -1,12 +1,20 @@ // Gate.cpp #include "Gate.h" +#include "Mod.h" +#include "Settings.h" #include "globals.h" #include "hardware/pwm.h" #include +#include #include -Gate::Gate(uint8_t pin) : Output(pin) { + +Gate::Gate(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2) : Output(pin, idx, slotIdx1, slotIdx2) { this->pin = pin; + this->idx = idx; + this->slotIdx1 = slotIdx1; + this->slotIdx2 = slotIdx2; + state = 0; editing = 0; @@ -21,6 +29,49 @@ Gate::Gate(uint8_t pin) : Output(pin) { p = 100; // probability of a pulse level = 100; + + modDest1 = (idx + 1) % 8; + modDest2 = (idx + 2) % 8; +} + + +void Gate::pack(OutputConfig &cfg) { + cfg.type = TYPE_GATE; + + GateSettings* s = (GateSettings*)cfg.data; + + s->modifierSelectionIndex = this->modifierSelectionIndex; + s->divideMode = this->divideMode; + s->modifier = this->modifier; + s->width = this->width; + s->p = this->p; + s->level = this->level; + s->shape = (uint8_t)this->shape; +} + +void Gate::unpack(const OutputConfig &cfg) { + if (cfg.type != TYPE_GATE) return; + + GateSettings s; + memcpy(&s, cfg.data, sizeof(GateSettings)); + + this->modifierSelectionIndex = s.modifierSelectionIndex; + this->divideMode = s.divideMode; + this->modifier = s.modifier; + this->width = s.width; + this->p = s.p; + this->level = s.level; + this->shape = (WaveShape)s.shape; + + setDiv(this->modifierSelectionIndex); + setWidth(this->width); +} + +void Gate::setupPatches() { + + matrix.patch(this->slotIdx1, this->modDest1, this->idx, DEST_LEVEL, 100, false); + + matrix.patch(this->slotIdx2, this->modDest2, this->idx, DEST_LEVEL, 100, false); } void Gate::setLen(uint32_t currentPeriod) { @@ -137,6 +188,9 @@ void Gate::setDiv(uint8_t modifier_selecton_index) { divideMode = 1; } + this->triggerCount = 0; + this->lastTriggerTick = 0xFFFFFFFF; + setWidth(this->width); // this is called in width, check if needed still? calculatePulseWidth(); @@ -177,11 +231,9 @@ void Gate::calculatePulseWidth() { pulseWidthTicks = 0; return; } - // If tickInterval is 96 and width is 50, pulseWidthTicks becomes 48 this->pulseWidthTicks = (uint32_t)((float)this->tickInterval * (this->width / 100.0f)); - // Safety: ensure a pulse is at least 1 tick long if width > 0 if (this->width > 0 && this->pulseWidthTicks == 0) { this->pulseWidthTicks = 1; } @@ -195,10 +247,17 @@ void Gate::turnOn() { if (MASTER_TICK != lastTriggerTick) { lastTriggerTick = MASTER_TICK; - if (p < 100 && (rand() % 100) + 1 > p) { - scheduledTick = 0xFFFFFFFF; // ignore interval - return; - } + float baseP = (float)this->p; + + float effectiveP = baseP + (this->pMod * 100.0f); + + if (effectiveP > 100.0f) effectiveP = 100.0f; + if (effectiveP < 0.0f) effectiveP = 0.0f; + + if ((rand() % 100) + 1 > (uint8_t)effectiveP) { + scheduledTick = 0xFFFFFFFF; + return; + } // swing triggerCount++; @@ -214,17 +273,20 @@ void Gate::turnOn() { } } - if (MASTER_TICK == scheduledTick && !state) { + if (MASTER_TICK >= scheduledTick && !state) { state = 1; startTick = MASTER_TICK; startTimeUs = time_us_64(); + scheduledTick = 0xFFFFFFFF; currentRandomVal = (float)rand() / (float)RAND_MAX; } } void Gate::update() { - if (!state && !sticky) - return; + if (!state && !sticky) { + lastOutVal = 0.0f; + return; + } uint64_t now = time_us_64(); uint32_t elapsedUs = (uint32_t)(now - startTimeUs); @@ -289,8 +351,33 @@ void Gate::update() { break; } - writeAnalog((outVal * 1023.0f) * ((float)level / 100)); -} + this->lastOutVal = outVal; + + // handle width mod + float effectiveWidth = (float)width + (widthMod * 100.0f); + + if (effectiveWidth > 100.0f) effectiveWidth = 100.0f; + if (effectiveWidth < 1.0f) effectiveWidth = 1.0f; + + double us_per_tick = 625000.0 / (double)BPM; + uint32_t modulatedTicks = (uint32_t)((float)this->tickInterval * (effectiveWidth / 100.0f)); + if (modulatedTicks < 1) modulatedTicks = 1; + + this->pulseDurationUs = (uint32_t)(us_per_tick * (double)modulatedTicks); + + float baseLevel = (float)this->level / 100.0f; + + float normalizedMod = this->levelMod; + if (normalizedMod > 1.0f || normalizedMod < -1.0f) { + normalizedMod /= 100.0f; + } + + float finalLevel = baseLevel + normalizedMod; + + if (finalLevel > 1.0f) finalLevel = 1.0f; + if (finalLevel < 0.0f) finalLevel = 0.0f; + + writeAnalog((uint16_t)(outVal * 1023.0f * finalLevel));} void Gate::writeAnalog(uint16_t val) { pwm_set_gpio_level(pin, val); } diff --git a/src/Mod.cpp b/src/Mod.cpp new file mode 100644 index 0000000..277bbde --- /dev/null +++ b/src/Mod.cpp @@ -0,0 +1,53 @@ +#include "Mod.h" +#include "Gate.h" + +#include + +void ModMatrix::patch(uint8_t slotIdx, uint8_t src, uint8_t dest, ModDest param, + float amt, bool active) { + slots[slotIdx].sourceIdx = src; + slots[slotIdx].destIdx = dest; + slots[slotIdx].destParam = (uint8_t)param; + slots[slotIdx].amount = (uint8_t)amt; + slots[slotIdx].active = active ? 1 : 0; + slots[slotIdx].inverted = 0; + return; +} + +void ModMatrix::process(Gate **gates, uint8_t gateCount) { + for (int i = 0; i < gateCount; i++) { + gates[i]->resetMods(); + } + + for (int i = 0; i < 16; i++) { + if (slots[i].active == 0) + continue; + + float srcVal = gates[slots[i].sourceIdx]->lastOutVal; + + float amt = (float)slots[i].amount; + float normalizedAmt = amt / 100.0f; + + if (slots[i].inverted == 1) { + normalizedAmt *= -1.0f; + } + + float modValue = srcVal * normalizedAmt; + + Gate *dstGate = gates[slots[i].destIdx]; + + switch ((ModDest)slots[i].destParam) { + case DEST_LEVEL: + dstGate->levelMod += modValue; + break; + case DEST_PROBABILITY: + dstGate->pMod += modValue; + break; + case DEST_WIDTH: + dstGate->widthMod += modValue; + break; + default: + break; + } + } +} diff --git a/src/Output.cpp b/src/Output.cpp index eb335cb..6fae669 100644 --- a/src/Output.cpp +++ b/src/Output.cpp @@ -1,7 +1,9 @@ #include "Output.h" +#include -Output::Output(uint8_t pin) { +Output::Output(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2) { this->pin = pin; + this->idx = idx; state = 0; isEnabled = false; } diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 0000000..bbbbf3c --- /dev/null +++ b/src/Settings.cpp @@ -0,0 +1,66 @@ +#include "Settings.h" +#include "Gate.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "pico/multicore.h" +#include + +#define FLASH_TARGET_OFFSET (2048 * 1024 - FLASH_SECTOR_SIZE) + +DeviceSettings globalSettings; + +void save() { + const uint32_t WRITE_SIZE = (sizeof(DeviceSettings) + 255) & ~255; + + static uint8_t __attribute__((aligned(4))) write_buf[2048]; + + uint32_t copy_size = + sizeof(DeviceSettings) > 2048 ? 2048 : sizeof(DeviceSettings); + + memset(write_buf, 0, sizeof(write_buf)); + memcpy(write_buf, &globalSettings, copy_size); + + multicore_lockout_start_blocking(); + uint32_t ints = save_and_disable_interrupts(); + + flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE); + flash_range_program(FLASH_TARGET_OFFSET, write_buf, WRITE_SIZE); + + restore_interrupts(ints); + multicore_lockout_end_blocking(); +} + +void load_default() { + memset(&globalSettings.slots, 0, sizeof(globalSettings.slots)); + + globalSettings.magic = 0x434C4F4B; + globalSettings.version = 1; + + for (int i = 0; i < MAX_OUTPUTS; i++) { + globalSettings.configs[i].type = TYPE_GATE; + + GateSettings *s = (GateSettings *)globalSettings.configs[i].data; + + s->modifierSelectionIndex = 8; + s->divideMode = 0; + s->modifier = 0; + s->width = 50; + s->p = 100; + s->level = 100; + s->shape = 0; + } +} + +bool load() { + const uint8_t *flash_target_contents = + (const uint8_t *)(XIP_BASE + FLASH_TARGET_OFFSET); + DeviceSettings *stored = (DeviceSettings *)flash_target_contents; + + if (stored->magic == 0x434C4F4B) { + memcpy(&globalSettings, stored, sizeof(DeviceSettings)); + return false; + } else { + load_default(); + return true; + } +} diff --git a/src/main.cpp b/src/main.cpp index 1a9d348..8181a3f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,152 +1,175 @@ -#include -#include -#include +#include "Settings.h" #include "hardware/structs/rosc.h" #include "pico/multicore.h" #include "pico/stdlib.h" #include "pico/time.h" +#include +#include +#include +#include -#include "globals.h" -#include "Gate.h" #include "DisplayHandler.h" #include "EncoderHandler.h" +#include "Gate.h" +#include "Mod.h" +#include "Settings.h" +#include "globals.h" #include "hardware/pwm.h" - // Time based operations struct repeating_timer bpm_timer = {0}; -volatile uint8_t BPM = 60; +volatile uint8_t BPM = 60; volatile bool PLAY = true; volatile uint32_t period_us = 0; volatile uint32_t MASTER_TICK; +ModMatrix matrix; // Initialize Outputs -Gate out1(OUT_1_PIN); -Gate out2(OUT_2_PIN); -Gate out3(OUT_3_PIN); -Gate out4(OUT_4_PIN); -Gate out5(OUT_5_PIN); -Gate out6(OUT_6_PIN); -Gate out7(OUT_7_PIN); -Gate out8(OUT_8_PIN); - -static Gate* outputs[] = {&out1, &out2, &out3, &out4, &out5, &out6, &out7, &out8}; +Gate out1(OUT_1_PIN, 0, 0, 1); +Gate out2(OUT_2_PIN, 1, 2, 3); +Gate out3(OUT_3_PIN, 2, 4, 5); +Gate out4(OUT_4_PIN, 3, 6, 7); +Gate out5(OUT_5_PIN, 4, 8, 9); +Gate out6(OUT_6_PIN, 5, 10, 11); +Gate out7(OUT_7_PIN, 6, 12, 13); +Gate out8(OUT_8_PIN, 7, 14, 15); +static Gate *outputs[] = {&out1, &out2, &out3, &out4, + &out5, &out6, &out7, &out8}; // Initialize Handlers static DisplayHandler display_handler(outputs); -static EncoderHandler encoder_handler(&display_handler); - +static EncoderHandler encoder_handler(&display_handler); bool timer_callback(struct repeating_timer *t) { - if (PLAY == 1) { - MASTER_TICK += 1; - } - return true; + if (PLAY == 1) { + MASTER_TICK += 1; + } + return true; } - void init_timer(uint32_t period_us) { - cancel_repeating_timer(&bpm_timer); - add_repeating_timer_us(-(int64_t)period_us, timer_callback, NULL, &bpm_timer); + cancel_repeating_timer(&bpm_timer); + add_repeating_timer_us(-(int64_t)period_us, timer_callback, NULL, &bpm_timer); } - void update_period() { - period_us = (uint32_t)(MINUTE_US / (uint32_t)BPM / PPQN); - init_timer(period_us); + period_us = (uint32_t)(MINUTE_US / (uint32_t)BPM / PPQN); + init_timer(period_us); } - void update_BPM(bool up) { - if (up) { - BPM++; - } else { - BPM--; - } + if (up) { + BPM++; + } else { + BPM--; + } - update_period(); + update_period(); - - for (auto g : outputs) { - g->setWidth(g->width); - } + for (auto g : outputs) { + g->setWidth(g->width); + } } - void core1_entry() { - multicore_fifo_pop_blocking(); + multicore_fifo_pop_blocking(); + multicore_lockout_victim_init(); - char buffer[32]; + char buffer[32]; - while (true) { - display_handler.render(); - } + while (true) { + display_handler.render(); + } } +void full_save() { + for (int i = 0; i < 8; i++) { + outputs[i]->pack(globalSettings.configs[i]); + } + + memcpy(globalSettings.slots, matrix.slots, sizeof(ModSlot) * 16); + + save(); +} void setup_outs() { - for (auto g : outputs) { - gpio_init(g->pin); - gpio_set_dir(g->pin, GPIO_OUT); + for (auto g : outputs) { + gpio_init(g->pin); + gpio_set_dir(g->pin, GPIO_OUT); - gpio_set_function(g->pin, GPIO_FUNC_PWM); - uint slice_num = pwm_gpio_to_slice_num(g->pin); - pwm_set_wrap(slice_num, 1023); - pwm_set_clkdiv(slice_num, 1.0f); + gpio_set_function(g->pin, GPIO_FUNC_PWM); + uint slice_num = pwm_gpio_to_slice_num(g->pin); + pwm_set_wrap(slice_num, 1023); + pwm_set_clkdiv(slice_num, 1.0f); - pwm_set_gpio_level(g->pin, 0); - pwm_set_enabled(slice_num, true); - g->setLen(period_us); - } + pwm_set_gpio_level(g->pin, 0); + pwm_set_enabled(slice_num, true); + g->setLen(period_us); + g->setupPatches(); + } - // manual setup - // out1.setDiv(5); - // out1.setWidth(80); + // // manual setup + // out1.shape = SQUARE; + // out1.setDiv(3); + // out1.setWidth(50); + // out1.level = 100; + // + // out2.shape = SINE; + // out2.setDiv(14); + // out2.setWidth(100); } - void handle_outs() { - for (Gate* g: outputs) { - g->turnOn(); - g->update(); - } + matrix.process(outputs, 8); + for (Gate *g : outputs) { + g->turnOn(); + g->update(); + } } - int main() { - // initialize - stdio_init_all(); + stdio_init_all(); + sleep_ms(200); - // Seed random - srand(rosc_hw->randombit); + bool loaded_defaults = load(); - // Initialize display and multicore - display_handler.setup(); - multicore_launch_core1(core1_entry); - multicore_fifo_push_blocking(1); + display_handler.setup(); + encoder_handler.setup(); - // Initialize Encoder - encoder_handler.setup(); + setup_outs(); - setup_outs(); + if (!loaded_defaults) { + for (int i = 0; i < 16; i++) { + matrix.slots[i] = globalSettings.slots[i]; + } + } - update_period(); + for (int i = 0; i < 8; i++) { + outputs[i]->unpack(globalSettings.configs[i]); + } - bool lastPlayState = false; + multicore_launch_core1(core1_entry); + multicore_fifo_push_blocking(1); - while (true) { - encoder_handler.update(); - if (PLAY) { - handle_outs(); - } else { - for (Gate* g: outputs) { - g->turnOff(); - } - } + update_period(); + srand(rosc_hw->randombit); - lastPlayState = PLAY; - } + bool lastPlayState = false; + + while (true) { + encoder_handler.update(); + + if (PLAY) { + handle_outs(); + } else { + for (Gate *g : outputs) { + g->turnOff(); + } + } + + lastPlayState = PLAY; + } }