266 lines
5.6 KiB
C++
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;
|
|
}
|
|
}
|