save, load, reset, PPQN, run

This commit is contained in:
Dominic DiTaranto 2026-03-09 23:35:35 -04:00
parent 5f762f0907
commit 5c7fe5e88d
8 changed files with 233 additions and 35 deletions

View file

@ -35,8 +35,20 @@ class DisplayHandler {
"Mute" "Mute"
}; };
bool onOutScreen = 0; bool onOutScreen = 0;
bool onSettingsScreen = 0;
std::array<std::string, 6> settings = {
"Save",
"Load",
"Reset",
"PPQN",
"Run",
"Exit"
};
void renderMainPage(); void renderMainPage();
void renderOutPage(); void renderOutPage();
void renderSettingsPage();
public: public:
@ -51,6 +63,8 @@ class DisplayHandler {
void setup(); void setup();
void render(); void render();
void handleClick(); void handleClick();
void exitSettingsScreen();
void flashMessage(std::string msg);
void moveCursor(bool dir = 1); void moveCursor(bool dir = 1);
}; };

View file

@ -21,7 +21,7 @@ public:
uint16_t clk_last_state; uint16_t clk_last_state;
void setup(); void setup();
static void gpio_callback(uint gpio, uint32_t events); // static void gpio_callback(uint gpio, uint32_t events);
void moveCursor(bool dir = 1); void moveCursor(bool dir = 1);
void update(); void update();
}; };

View file

@ -21,6 +21,10 @@ struct DeviceSettings {
uint32_t version; uint32_t version;
OutputConfig configs[MAX_OUTPUTS]; OutputConfig configs[MAX_OUTPUTS];
ModSlot slots[16]; ModSlot slots[16];
bool play;
uint8_t bpm;
bool run;
uint8_t ppqnidx;
}; };
void save(); void save();

View file

@ -18,6 +18,8 @@ static constexpr uint8_t OUT_6_PIN = 10;
static constexpr uint8_t OUT_7_PIN = 12; static constexpr uint8_t OUT_7_PIN = 12;
static constexpr uint8_t OUT_8_PIN = 14; static constexpr uint8_t OUT_8_PIN = 14;
static constexpr uint8_t IN_RUN_PIN = 17;
static constexpr uint8_t SCREEN_SCL_PIN = 18; static constexpr uint8_t SCREEN_SCL_PIN = 18;
static constexpr uint8_t SCREEN_SDA_PIN = 19; static constexpr uint8_t SCREEN_SDA_PIN = 19;
@ -28,6 +30,8 @@ static constexpr uint8_t ENCODER_CLK_PIN = 20;
static constexpr uint8_t ENCODER_DT_PIN = 21; static constexpr uint8_t ENCODER_DT_PIN = 21;
static constexpr uint8_t ENCODER_SW_PIN = 22; static constexpr uint8_t ENCODER_SW_PIN = 22;
void gpio_callback(uint gpio, uint32_t events);
// TIME BASED // TIME BASED
extern volatile bool PLAY; extern volatile bool PLAY;
extern volatile uint8_t BPM; extern volatile uint8_t BPM;
@ -35,6 +39,11 @@ static constexpr uint32_t MINUTE_US = 60000000;
static constexpr uint8_t PPQN = 96; static constexpr uint8_t PPQN = 96;
extern volatile uint32_t MASTER_TICK; extern volatile uint32_t MASTER_TICK;
extern volatile bool RUN;
extern volatile uint8_t EXTPPQNIdx;
#define PPQN_OPTS_LEN 5
extern const uint16_t PPQNOPTS[PPQN_OPTS_LEN];
enum WaveShape { SQUARE, TRIANGLE, SAW, RAMP, EXP, HALFSINE, REXP, LOG, SINE, BOUNCE, SIGMO, WOBBLE, STEPDW, STEPUP, SH, SHAPE_COUNT}; enum WaveShape { SQUARE, TRIANGLE, SAW, RAMP, EXP, HALFSINE, REXP, LOG, SINE, BOUNCE, SIGMO, WOBBLE, STEPDW, STEPUP, SH, SHAPE_COUNT};

View file

@ -1,15 +1,20 @@
// DisplayHandler.cpp // DisplayHandler.cpp
#include "DisplayHandler.h" #include "DisplayHandler.h"
#include "Mod.h" #include "Mod.h"
#include "Settings.h"
#include "globals.h" #include "globals.h"
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <pico/time.h>
#include <string> #include <string>
#include "hardware/i2c.h" #include "hardware/i2c.h"
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h" #include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
#include "pico-ssd1306/ssd1306.h" #include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h" #include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "textRenderer/12x16_font.h"
#include "textRenderer/8x8_font.h"
pico_ssd1306::SSD1306 *display = nullptr; pico_ssd1306::SSD1306 *display = nullptr;
extern void update_BPM(bool up); extern void update_BPM(bool up);
@ -292,6 +297,7 @@ void DisplayHandler::moveCursor(bool dir) {
cursorPosition--; cursorPosition--;
} }
if (!onSettingsScreen) {
if (cursorPosition > mainMaxCursorPosition) { if (cursorPosition > mainMaxCursorPosition) {
cursorPosition = 0; cursorPosition = 0;
} }
@ -299,6 +305,16 @@ void DisplayHandler::moveCursor(bool dir) {
if (cursorPosition < 0) { if (cursorPosition < 0) {
cursorPosition = mainMaxCursorPosition; cursorPosition = mainMaxCursorPosition;
} }
} else if (onSettingsScreen) {
if (cursorPosition > 5) {
cursorPosition = 0;
}
if (cursorPosition < 0) {
cursorPosition = 5;
}
}
} }
} }
@ -308,7 +324,7 @@ void DisplayHandler::moveCursor(bool dir) {
void DisplayHandler::handleClick() { void DisplayHandler::handleClick() {
cursorClick ^= true; cursorClick ^= true;
if (onOutScreen) { if (onOutScreen && !onSettingsScreen) {
if (currentScreen == 0) { // exit screen if (currentScreen == 0) { // exit screen
cursorPosition = currentOut + 1; cursorPosition = currentOut + 1;
currentOut = -1; currentOut = -1;
@ -324,7 +340,7 @@ void DisplayHandler::handleClick() {
} }
} else { } else {
if (currentScreen == 0) { // on main screen if (currentScreen == 0 && !onSettingsScreen) { // on main screen
if (cursorPosition == 0) { // Change BPM if (cursorPosition == 0) { // Change BPM
} else if (cursorPosition > 0 && cursorPosition < 9) { // go to out screen } else if (cursorPosition > 0 && cursorPosition < 9) { // go to out screen
@ -335,6 +351,81 @@ void DisplayHandler::handleClick() {
cursorClick = 0; cursorClick = 0;
} else if (cursorPosition == 9) { // PLAY/PAUSE BUTTON } else if (cursorPosition == 9) { // PLAY/PAUSE BUTTON
PLAY ^= true; 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();
} }
} }
} }
@ -342,14 +433,25 @@ void DisplayHandler::handleClick() {
updateScreen = 1; updateScreen = 1;
} }
void DisplayHandler::exitSettingsScreen() {
onOutScreen = 0;
onSettingsScreen = 0;
cursorPosition = 10;
cursorClick = 0;
currentScreen = 0;
}
void DisplayHandler::render() { void DisplayHandler::render() {
if (updateScreen) { if (updateScreen) {
display->clear(); display->clear();
if (currentScreen == 0 && currentOut == -1) { // main screen if (!onSettingsScreen && currentScreen == 0 &&
currentOut == -1) { // main screen
renderMainPage(); renderMainPage();
} else if (currentOut != -1) { } else if (!onSettingsScreen && currentOut != -1) {
renderOutPage(); renderOutPage();
} else if (onSettingsScreen) {
renderSettingsPage();
} }
display->sendBuffer(); display->sendBuffer();
@ -503,3 +605,38 @@ void DisplayHandler::renderOutPage() {
pico_ssd1306::drawText(display, font_12x16, param_string.c_str(), 1, 45); pico_ssd1306::drawText(display, font_12x16, param_string.c_str(), 1, 45);
} }
} }
void DisplayHandler::flashMessage(std::string msg) {
display->clear();
pico_ssd1306::drawText(display, font_12x16, msg.c_str(), 1, 1);
display->sendBuffer();
}
void DisplayHandler::renderSettingsPage() {
uint8_t y = 3;
for (int i = 0; i < settings.size(); i++) {
if (cursorPosition == i) {
pico_ssd1306::fillRect(display, 0, y, 50, y + 8);
pico_ssd1306::drawText(display, font_8x8, settings[i].c_str(), 1, y,
pico_ssd1306::WriteMode::SUBTRACT);
if (settings[i] == "Run") {
std::string param = RUN ? "ON" : "OFF";
pico_ssd1306::drawText(display, font_12x16, param.c_str(), 70, 20);
}
if (settings[i] == "PPQN") {
std::string param = std::to_string(PPQNOPTS[EXTPPQNIdx]);
pico_ssd1306::drawText(display, font_12x16, param.c_str(), 70, 20);
}
} else {
pico_ssd1306::drawText(display, font_8x8, settings[i].c_str(), 1, y);
}
y += 10;
}
}

View file

@ -17,18 +17,6 @@ EncoderHandler::EncoderHandler(DisplayHandler *display_handler) {
button_pressed = 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;
if (gpio == ENCODER_SW_PIN) {
if (now - last_sw_time > 200000) { // 200ms debounce
self->display_handler->handleClick();
last_sw_time = now;
}
}
}
void EncoderHandler::setup() { void EncoderHandler::setup() {
self = this; self = this;
@ -36,8 +24,7 @@ void EncoderHandler::setup() {
gpio_set_dir(ENCODER_SW_PIN, GPIO_IN); gpio_set_dir(ENCODER_SW_PIN, GPIO_IN);
gpio_pull_up(ENCODER_SW_PIN); gpio_pull_up(ENCODER_SW_PIN);
gpio_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, gpio_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
&EncoderHandler::gpio_callback);
PIO pio = pio0; PIO pio = pio0;
uint offset = pio_add_program(pio, &quadrature_encoder_program); uint offset = pio_add_program(pio, &quadrature_encoder_program);

View file

@ -48,6 +48,9 @@ void load_default() {
s->p = 100; s->p = 100;
s->level = 100; s->level = 100;
s->shape = 0; s->shape = 0;
BPM = 60;
PLAY = true;
} }
} }

View file

@ -6,6 +6,7 @@
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <hardware/gpio.h>
#include <stdio.h> #include <stdio.h>
#include "DisplayHandler.h" #include "DisplayHandler.h"
@ -22,6 +23,9 @@ volatile uint8_t BPM = 60;
volatile bool PLAY = true; volatile bool PLAY = true;
volatile uint32_t period_us = 0; volatile uint32_t period_us = 0;
volatile uint32_t MASTER_TICK; volatile uint32_t MASTER_TICK;
volatile bool RUN = false;
const uint16_t PPQNOPTS[] = {1, 2, 4, 24, 48};
volatile uint8_t EXTPPQNIdx = 0;
ModMatrix matrix; ModMatrix matrix;
@ -89,6 +93,11 @@ void full_save() {
outputs[i]->pack(globalSettings.configs[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); memcpy(globalSettings.slots, matrix.slots, sizeof(ModSlot) * 16);
save(); save();
@ -110,16 +119,6 @@ void setup_outs() {
g->setLen(period_us); g->setLen(period_us);
g->setupPatches(); g->setupPatches();
} }
// // 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() { void handle_outs() {
@ -130,6 +129,40 @@ void handle_outs() {
} }
} }
void gpio_callback(uint gpio, uint32_t events) {
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;
if (now - last_sw_time > 200000) { // 200ms debounce
display_handler.handleClick();
last_sw_time = now;
}
}
}
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 CLOCK
// SETUP CV INS
}
int main() { int main() {
stdio_init_all(); stdio_init_all();
sleep_ms(200); sleep_ms(200);
@ -145,6 +178,11 @@ int main() {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
matrix.slots[i] = globalSettings.slots[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++) { for (int i = 0; i < 8; i++) {
@ -159,6 +197,12 @@ int main() {
bool lastPlayState = false; bool lastPlayState = false;
setup_ins();
if (RUN) {
PLAY = false;
}
while (true) { while (true) {
encoder_handler.update(); encoder_handler.update();