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

View file

@ -21,7 +21,7 @@ public:
uint16_t clk_last_state;
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 update();
};

View file

@ -21,6 +21,10 @@ struct DeviceSettings {
uint32_t version;
OutputConfig configs[MAX_OUTPUTS];
ModSlot slots[16];
bool play;
uint8_t bpm;
bool run;
uint8_t ppqnidx;
};
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_8_PIN = 14;
static constexpr uint8_t IN_RUN_PIN = 17;
static constexpr uint8_t SCREEN_SCL_PIN = 18;
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_SW_PIN = 22;
void gpio_callback(uint gpio, uint32_t events);
// TIME BASED
extern volatile bool PLAY;
extern volatile uint8_t BPM;
@ -35,6 +39,11 @@ static constexpr uint32_t MINUTE_US = 60000000;
static constexpr uint8_t PPQN = 96;
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};

View file

@ -1,15 +1,20 @@
// DisplayHandler.cpp
#include "DisplayHandler.h"
#include "Mod.h"
#include "Settings.h"
#include "globals.h"
#include "pico/stdlib.h"
#include <cstdint>
#include <cstdlib>
#include <pico/time.h>
#include <string>
#include "hardware/i2c.h"
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "textRenderer/12x16_font.h"
#include "textRenderer/8x8_font.h"
pico_ssd1306::SSD1306 *display = nullptr;
extern void update_BPM(bool up);
@ -292,6 +297,7 @@ void DisplayHandler::moveCursor(bool dir) {
cursorPosition--;
}
if (!onSettingsScreen) {
if (cursorPosition > mainMaxCursorPosition) {
cursorPosition = 0;
}
@ -299,6 +305,16 @@ void DisplayHandler::moveCursor(bool dir) {
if (cursorPosition < 0) {
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() {
cursorClick ^= true;
if (onOutScreen) {
if (onOutScreen && !onSettingsScreen) {
if (currentScreen == 0) { // exit screen
cursorPosition = currentOut + 1;
currentOut = -1;
@ -324,7 +340,7 @@ void DisplayHandler::handleClick() {
}
} else {
if (currentScreen == 0) { // on main screen
if (currentScreen == 0 && !onSettingsScreen) { // on main screen
if (cursorPosition == 0) { // Change BPM
} else if (cursorPosition > 0 && cursorPosition < 9) { // go to out screen
@ -335,6 +351,81 @@ void DisplayHandler::handleClick() {
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();
}
}
}
@ -342,14 +433,25 @@ void DisplayHandler::handleClick() {
updateScreen = 1;
}
void DisplayHandler::exitSettingsScreen() {
onOutScreen = 0;
onSettingsScreen = 0;
cursorPosition = 10;
cursorClick = 0;
currentScreen = 0;
}
void DisplayHandler::render() {
if (updateScreen) {
display->clear();
if (currentScreen == 0 && currentOut == -1) { // main screen
if (!onSettingsScreen && currentScreen == 0 &&
currentOut == -1) { // main screen
renderMainPage();
} else if (currentOut != -1) {
} else if (!onSettingsScreen && currentOut != -1) {
renderOutPage();
} else if (onSettingsScreen) {
renderSettingsPage();
}
display->sendBuffer();
@ -503,3 +605,38 @@ void DisplayHandler::renderOutPage() {
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;
}
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() {
self = this;
@ -36,8 +24,7 @@ void EncoderHandler::setup() {
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_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
PIO pio = pio0;
uint offset = pio_add_program(pio, &quadrature_encoder_program);

View file

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

View file

@ -6,6 +6,7 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <hardware/gpio.h>
#include <stdio.h>
#include "DisplayHandler.h"
@ -22,6 +23,9 @@ volatile uint8_t BPM = 60;
volatile bool PLAY = true;
volatile uint32_t period_us = 0;
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;
@ -89,6 +93,11 @@ void full_save() {
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();
@ -110,16 +119,6 @@ void setup_outs() {
g->setLen(period_us);
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() {
@ -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() {
stdio_init_all();
sleep_ms(200);
@ -145,6 +178,11 @@ int main() {
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++) {
@ -159,6 +197,12 @@ int main() {
bool lastPlayState = false;
setup_ins();
if (RUN) {
PLAY = false;
}
while (true) {
encoder_handler.update();