master_clock/src/main.cpp

266 lines
5.6 KiB
C++

#include "Settings.h"
#include "hardware/structs/rosc.h"
#include "pico/multicore.h"
#include "pico/stdlib.h"
#include "pico/time.h"
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <hardware/gpio.h>
#include <math.h>
#include <pico/types.h>
#include <stdio.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 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;
volatile uint64_t last_clk_us = 0;
volatile uint64_t last_valid_clk_us;
volatile bool EXTERNAL_CLOCK = false;
ModMatrix matrix;
// Initialize Outputs
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);
bool timer_callback(struct repeating_timer *t) {
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);
}
void update_period() {
period_us = (uint32_t)(MINUTE_US / (uint32_t)BPM / PPQN);
init_timer(period_us);
}
void update_BPM(bool up) {
if (up) {
BPM++;
} else {
BPM--;
}
update_period();
for (auto g : outputs) {
g->setWidth(g->width);
}
if (!EXTERNAL_CLOCK) {
init_timer(period_us);
} else {
cancel_repeating_timer(&bpm_timer);
}
}
void core1_entry() {
multicore_fifo_pop_blocking();
multicore_lockout_victim_init();
char buffer[32];
while (true) {
display_handler.render();
}
}
void full_save() {
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();
}
void setup_outs() {
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);
pwm_set_gpio_level(g->pin, 0);
pwm_set_enabled(slice_num, true);
g->setLen(period_us);
g->setupPatches();
}
}
void handle_outs() {
matrix.process(outputs, 8);
for (Gate *g : outputs) {
g->turnOn();
g->update();
}
}
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 (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;
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
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
}
int main() {
stdio_init_all();
sleep_ms(200);
bool loaded_defaults = load();
display_handler.setup();
encoder_handler.setup();
setup_outs();
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]);
}
multicore_launch_core1(core1_entry);
multicore_fifo_push_blocking(1);
update_period();
srand(rosc_hw->randombit);
bool lastPlayState = false;
setup_ins();
if (RUN) {
PLAY = false;
}
while (true) {
encoder_handler.update();
if (PLAY) {
handle_outs();
} else {
for (Gate *g : outputs) {
g->turnOff();
}
}
lastPlayState = PLAY;
}
}