#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 #include #include #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; } }