Compare commits

..

9 commits

5 changed files with 245 additions and 160 deletions

View file

@ -60,6 +60,7 @@ public:
WaveShape shape = SQUARE; WaveShape shape = SQUARE;
uint32_t startTick = 0; uint32_t startTick = 0;
uint32_t stopTick = 0;
uint32_t pulseWidthTicks = 0; uint32_t pulseWidthTicks = 0;
bool sticky = false; bool sticky = false;
int8_t modifierSelectionIndex; int8_t modifierSelectionIndex;

View file

@ -41,10 +41,13 @@ 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 float BPM;
static constexpr uint32_t MINUTE_US = 60000000; 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 float filteredBPM;
extern volatile uint64_t last_external_pulse_us;
extern const uint64_t CLOCK_TIMEOUT_US;
extern volatile bool RUN; extern volatile bool RUN;
@ -55,6 +58,10 @@ extern volatile uint64_t last_clk_us;
extern volatile uint64_t last_valid_clk_us; extern volatile uint64_t last_valid_clk_us;
extern volatile bool EXTERNAL_CLOCK; extern volatile bool EXTERNAL_CLOCK;
#define AVG_SAMPLES 12
extern uint64_t pulse_intervals[AVG_SAMPLES];
extern uint8_t pulse_idx;
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};
inline const char* waveShapeToString(WaveShape shape) { inline const char* waveShapeToString(WaveShape shape) {

View file

@ -397,21 +397,21 @@ void DisplayHandler::render() {
} }
void DisplayHandler::renderMainPage() { void DisplayHandler::renderMainPage() {
std::string bpm_string = "BPM: " + std::to_string(BPM); char bpm_buffer[16];
snprintf(bpm_buffer, sizeof(bpm_buffer), "BPM %.1f", BPM);
if (cursorPosition == 0) { if (cursorPosition == 0) {
if (cursorClick == 1) { if (cursorClick == 1) {
pico_ssd1306::fillRect(display, 0, 0, 100, 18); pico_ssd1306::fillRect(display, 0, 0, 110, 18);
pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2, pico_ssd1306::drawText(display, font_12x16, bpm_buffer, 1, 2,
pico_ssd1306::WriteMode::SUBTRACT); pico_ssd1306::WriteMode::SUBTRACT);
} else { } else {
pico_ssd1306::drawRect(display, 0, 0, 100, 18, pico_ssd1306::drawRect(display, 0, 0, 110, 18,
pico_ssd1306::WriteMode::ADD); pico_ssd1306::WriteMode::ADD);
pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2); pico_ssd1306::drawText(display, font_12x16, bpm_buffer, 1, 2);
} }
} else { } else {
pico_ssd1306::drawText(display, font_12x16, bpm_string.c_str(), 1, 2); pico_ssd1306::drawText(display, font_12x16, bpm_buffer, 1, 2);
} }
uint8_t cursor_x = 2; uint8_t cursor_x = 2;

View file

@ -4,12 +4,18 @@
#include "Settings.h" #include "Settings.h"
#include "globals.h" #include "globals.h"
#include "hardware/pwm.h" #include "hardware/pwm.h"
#include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <math.h> #include <math.h>
#include <pico/types.h>
#ifndef max
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
Gate::Gate(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2) : Output(pin, idx, slotIdx1, slotIdx2) { Gate::Gate(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2)
: Output(pin, idx, slotIdx1, slotIdx2) {
this->pin = pin; this->pin = pin;
this->idx = idx; this->idx = idx;
this->slotIdx1 = slotIdx1; this->slotIdx1 = slotIdx1;
@ -34,11 +40,10 @@ Gate::Gate(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2) : Outpu
modDest2 = (idx + 2) % 8; modDest2 = (idx + 2) % 8;
} }
void Gate::pack(OutputConfig &cfg) { void Gate::pack(OutputConfig &cfg) {
cfg.type = TYPE_GATE; cfg.type = TYPE_GATE;
GateSettings* s = (GateSettings*)cfg.data; GateSettings *s = (GateSettings *)cfg.data;
s->modifierSelectionIndex = this->modifierSelectionIndex; s->modifierSelectionIndex = this->modifierSelectionIndex;
s->divideMode = this->divideMode; s->divideMode = this->divideMode;
@ -50,7 +55,8 @@ void Gate::pack(OutputConfig &cfg) {
} }
void Gate::unpack(const OutputConfig &cfg) { void Gate::unpack(const OutputConfig &cfg) {
if (cfg.type != TYPE_GATE) return; if (cfg.type != TYPE_GATE)
return;
GateSettings s; GateSettings s;
memcpy(&s, cfg.data, sizeof(GateSettings)); memcpy(&s, cfg.data, sizeof(GateSettings));
@ -68,10 +74,10 @@ void Gate::unpack(const OutputConfig &cfg) {
} }
void Gate::setupPatches() { void Gate::setupPatches() {
matrix.patch(this->slotIdx1, this->modDest1, this->idx, DEST_LEVEL, 100,
matrix.patch(this->slotIdx1, this->modDest1, this->idx, DEST_LEVEL, 100, false); false);
matrix.patch(this->slotIdx2, this->modDest2, this->idx, DEST_LEVEL, 100,
matrix.patch(this->slotIdx2, this->modDest2, this->idx, DEST_LEVEL, 100, false); false);
} }
void Gate::setLen(uint32_t currentPeriod) { void Gate::setLen(uint32_t currentPeriod) {
@ -192,8 +198,6 @@ void Gate::setDiv(uint8_t modifier_selecton_index) {
this->lastTriggerTick = 0xFFFFFFFF; this->lastTriggerTick = 0xFFFFFFFF;
setWidth(this->width); setWidth(this->width);
// this is called in width, check if needed still?
calculatePulseWidth();
}; };
void Gate::setWidth(uint16_t newWidth) { void Gate::setWidth(uint16_t newWidth) {
@ -231,8 +235,6 @@ void Gate::calculatePulseWidth() {
pulseWidthTicks = 0; pulseWidthTicks = 0;
return; return;
} }
this->pulseWidthTicks =
(uint32_t)((float)this->tickInterval * (this->width / 100.0f));
if (this->width > 0 && this->pulseWidthTicks == 0) { if (this->width > 0 && this->pulseWidthTicks == 0) {
this->pulseWidthTicks = 1; this->pulseWidthTicks = 1;
@ -240,43 +242,39 @@ void Gate::calculatePulseWidth() {
} }
void Gate::turnOn() { void Gate::turnOn() {
if (!isEnabled || tickInterval == 0) if (!isEnabled || tickInterval == 0) {
return; return;
}
if (MASTER_TICK % tickInterval == 0) { if (MASTER_TICK % tickInterval == 0 && MASTER_TICK != lastTriggerTick) {
if (MASTER_TICK != lastTriggerTick) {
lastTriggerTick = MASTER_TICK; lastTriggerTick = MASTER_TICK;
float baseP = (float)this->p; // Probability
float effectiveP = (float)this->p + (this->pMod * 100.0f);
float effectiveP = baseP + (this->pMod * 100.0f);
if (effectiveP > 100.0f) effectiveP = 100.0f;
if (effectiveP < 0.0f) effectiveP = 0.0f;
if ((rand() % 100) + 1 > (uint8_t)effectiveP) { if ((rand() % 100) + 1 > (uint8_t)effectiveP) {
scheduledTick = 0xFFFFFFFF; scheduledTick = 0xFFFFFFFF;
return; return;
} }
// swing // Swing
triggerCount++; triggerCount++;
int32_t swingOffset =
uint32_t swingDelayTicks = (int32_t)((float)tickInterval * ((float)swing - 50.0f) / 100.0f);
(uint32_t)((float)tickInterval * ((float)swing - 50.0f) / 100.0f);
if (triggerCount % 2 == 0) { if (triggerCount % 2 == 0) {
scheduledTick = MASTER_TICK + swingDelayTicks; scheduledTick = MASTER_TICK + (uint32_t)max(0, swingOffset);
} else { } else {
scheduledTick = MASTER_TICK; scheduledTick = MASTER_TICK;
} }
} }
}
if (MASTER_TICK >= scheduledTick && !state) { if (MASTER_TICK >= scheduledTick && !state) {
state = 1; state = 1;
startTick = MASTER_TICK; startTick = MASTER_TICK;
startTimeUs = time_us_64();
calculatePulseWidth();
stopTick = startTick + pulseWidthTicks;
scheduledTick = 0xFFFFFFFF; scheduledTick = 0xFFFFFFFF;
currentRandomVal = (float)rand() / (float)RAND_MAX; currentRandomVal = (float)rand() / (float)RAND_MAX;
} }
@ -285,32 +283,72 @@ void Gate::turnOn() {
void Gate::update() { void Gate::update() {
if (!state && !sticky) { if (!state && !sticky) {
lastOutVal = 0.0f; lastOutVal = 0.0f;
return;
}
uint64_t now = time_us_64();
uint32_t elapsedUs = (uint32_t)(now - startTimeUs);
if (elapsedUs >= pulseDurationUs) {
state = 0;
if (width < 100) {
scheduledTick = 0xFFFFFFFF;
lastTriggerTick = 0xFFFFFFFF;
}
if (!sticky)
writeAnalog(0); writeAnalog(0);
return; return;
} }
float phase = (float)elapsedUs / (float)pulseDurationUs; // width
float outVal = 0; float effectiveWidth = (float)width + (widthMod * 100.0f);
if (effectiveWidth > 100.0f)
effectiveWidth = 100.0f;
if (effectiveWidth < 0.0f)
effectiveWidth = 0.0f;
uint32_t modulatedTicks =
(uint32_t)((float)this->tickInterval * (effectiveWidth / 100.0f));
this->stopTick = startTick + modulatedTicks;
if (effectiveWidth < 100.0f) {
if (MASTER_TICK >= stopTick) {
state = 0;
if (!sticky) {
lastOutVal = 0.0f;
writeAnalog(0);
}
return;
}
}
uint64_t now = time_us_64();
uint64_t usSinceLastTick = (now > last_clk_us) ? (now - last_clk_us) : 0;
double current_BPM_for_math = (double)filteredBPM;
if (current_BPM_for_math < 1.0)
current_BPM_for_math = 1.0;
double us_per_tick = 60000000.0 / (current_BPM_for_math * (double)PPQN);
float subTick = (float)usSinceLastTick / (float)us_per_tick;
if (subTick > 0.99f)
subTick = 0.99f;
float elapsedTicks = (float)(MASTER_TICK - startTick) + subTick;
float totalDurationTicks = (effectiveWidth >= 100.0f)
? (float)tickInterval
: (float)(stopTick - startTick);
if (totalDurationTicks < 1.0f)
totalDurationTicks = 1.0f;
float phase = elapsedTicks / totalDurationTicks;
if (effectiveWidth >= 100.0f) {
while (phase >= 1.0f)
phase -= 1.0f;
} else {
if (phase > 1.0f)
phase = 1.0f;
}
if (phase < 0.0f)
phase = 0.0f;
float outVal = 0;
switch (shape) { switch (shape) {
case SINE: case SINE:
outVal = (sinf(phase * 2.0f * 3.14159f) * 0.5f) + 0.5f; outVal = (sinf(phase * 2.0f * 3.14159265f) * 0.5f) + 0.5f;
break; break;
case HALFSINE: // AKA HUMP case HALFSINE:
outVal = sinf(phase * 3.14159f); outVal = sinf(phase * 3.14159265f);
break; break;
case TRIANGLE: case TRIANGLE:
outVal = (phase < 0.5f) ? (phase * 2.0f) : (2.0f - (phase * 2.0f)); outVal = (phase < 0.5f) ? (phase * 2.0f) : (2.0f - (phase * 2.0f));
@ -334,13 +372,13 @@ void Gate::update() {
outVal = 1.0f; outVal = 1.0f;
break; break;
case BOUNCE: case BOUNCE:
outVal = fabsf(sinf(phase * 3.14159f * 2.0f)); outVal = fabsf(sinf(phase * 3.14159265f * 2.0f));
break; break;
case SIGMO: case SIGMO:
outVal = phase * phase * (3.0f - 2.0f * phase); outVal = phase * phase * (3.0f - 2.0f * phase);
break; break;
case WOBBLE: case WOBBLE:
outVal = expf(-3.0f * phase) * cosf(phase * 3.14159f * 4.0f); outVal = expf(-3.0f * phase) * cosf(phase * 3.14159265f * 4.0f);
if (outVal < 0) if (outVal < 0)
outVal = 0; outVal = 0;
break; break;
@ -353,33 +391,18 @@ void Gate::update() {
case SH: case SH:
outVal = currentRandomVal; outVal = currentRandomVal;
break; break;
default:
outVal = 1.0f;
break;
} }
this->lastOutVal = outVal; this->lastOutVal = outVal;
// handle width mod float finalLevel = ((float)this->level / 100.0f) + (this->levelMod);
float effectiveWidth = (float)width + (widthMod * 100.0f); if (finalLevel > 1.0f)
finalLevel = 1.0f;
if (effectiveWidth > 100.0f) effectiveWidth = 100.0f; if (finalLevel < 0.0f)
if (effectiveWidth < 1.0f) effectiveWidth = 1.0f; finalLevel = 0.0f;
double us_per_tick = 625000.0 / (double)BPM;
uint32_t modulatedTicks = (uint32_t)((float)this->tickInterval * (effectiveWidth / 100.0f));
if (modulatedTicks < 1) modulatedTicks = 1;
this->pulseDurationUs = (uint32_t)(us_per_tick * (double)modulatedTicks);
float baseLevel = (float)this->level / 100.0f;
float normalizedMod = this->levelMod;
if (normalizedMod > 1.0f || normalizedMod < -1.0f) {
normalizedMod /= 100.0f;
}
float finalLevel = baseLevel + normalizedMod;
if (finalLevel > 1.0f) finalLevel = 1.0f;
if (finalLevel < 0.0f) finalLevel = 0.0f;
writeAnalog((uint16_t)(outVal * 1023.0f * finalLevel)); writeAnalog((uint16_t)(outVal * 1023.0f * finalLevel));
} }

View file

@ -23,7 +23,7 @@
// Time based operations // Time based operations
struct repeating_timer bpm_timer = {0}; struct repeating_timer bpm_timer = {0};
volatile uint8_t BPM = 60; volatile float 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;
@ -33,7 +33,15 @@ volatile uint8_t EXTPPQNIdx = 0;
volatile uint64_t last_clk_us = 0; volatile uint64_t last_clk_us = 0;
volatile uint64_t last_valid_clk_us; volatile uint64_t last_valid_clk_us;
volatile bool EXTERNAL_CLOCK = false; volatile bool EXTERNAL_CLOCK = false;
volatile float filteredBPM = 60.0f;
volatile uint64_t last_external_pulse_us = 0;
const uint64_t CLOCK_TIMEOUT_US = 2000000;
uint64_t pulse_intervals[AVG_SAMPLES] = {0};
uint8_t pulse_idx = 0;
uint64_t last_external_clk_us = 0;
uint8_t BPM_UI_REFRESH = 0;
bool external_pulse_received = false;
ModMatrix matrix; ModMatrix matrix;
// Initialize Outputs // Initialize Outputs
@ -58,6 +66,7 @@ static EncoderHandler encoder_handler(&display_handler);
bool timer_callback(struct repeating_timer *t) { bool timer_callback(struct repeating_timer *t) {
if (PLAY == 1) { if (PLAY == 1) {
last_clk_us = to_us_since_boot(get_absolute_time());
MASTER_TICK += 1; MASTER_TICK += 1;
} }
return true; return true;
@ -69,23 +78,19 @@ void init_timer(uint32_t period_us) {
} }
void update_period() { void update_period() {
period_us = (uint32_t)(MINUTE_US / (uint32_t)BPM / PPQN); period_us = (uint32_t)(MINUTE_US / (float)BPM / PPQN);
init_timer(period_us); init_timer(period_us);
} }
void update_BPM(bool up) { void update_BPM(bool up) {
if (up) { if (up) {
BPM++; BPM += 0.5;
} else { } else {
BPM--; BPM -= 0.5;
} }
update_period(); update_period();
for (auto g : outputs) {
g->setWidth(g->width);
}
if (!EXTERNAL_CLOCK) { if (!EXTERNAL_CLOCK) {
init_timer(period_us); init_timer(period_us);
} else { } else {
@ -146,34 +151,66 @@ void handle_outs() {
} }
void gpio_callback(uint gpio, uint32_t events) { void gpio_callback(uint gpio, uint32_t events) {
// CLK LOGIC
if (gpio == IN_CLK_PIN && (events & GPIO_IRQ_EDGE_RISE)) { if (gpio == IN_CLK_PIN && (events & GPIO_IRQ_EDGE_RISE)) {
uint64_t now = to_us_since_boot(get_absolute_time()); uint64_t now = to_us_since_boot(get_absolute_time());
if (now - last_valid_clk_us < 5000) { if (now - last_valid_clk_us < 1000)
return; return;
}
last_valid_clk_us = now;
uint16_t incomingPPQN; if (last_external_clk_us > 0) {
if (last_clk_us > 0) { uint64_t latest_diff = now - last_external_clk_us;
uint64_t diff = now - last_clk_us;
incomingPPQN = PPQNOPTS[EXTPPQNIdx];
float calculatedBPM = 60000000.0f / (float)(diff * incomingPPQN);
if (calculatedBPM >= 30 && calculatedBPM <= 255) { pulse_intervals[pulse_idx] = latest_diff;
if (fabsf((float)BPM - calculatedBPM) > 0.5f) { pulse_idx = (pulse_idx + 1) % AVG_SAMPLES;
BPM = (uint8_t)(calculatedBPM + 0.5f);
update_period(); uint64_t sum = 0;
for (auto g : outputs) { uint8_t count = 0;
g->setWidth(g->width); for (int i = 0; i < AVG_SAMPLES; i++) {
if (pulse_intervals[i] > 0) {
sum += pulse_intervals[i];
count++;
}
}
if (count > 0) {
double avg_diff = (double)sum / (double)count;
uint16_t incomingPPQN = PPQNOPTS[EXTPPQNIdx];
double calculatedBPM = 60000000.0 / (avg_diff * (double)incomingPPQN);
if (calculatedBPM > 20.0 && calculatedBPM < 300.0) {
float diff = (float)calculatedBPM - filteredBPM;
if (fabsf(diff) > 5.0f || filteredBPM < 1.0f) {
filteredBPM = (float)calculatedBPM;
} else {
filteredBPM += (0.3f * diff);
} }
} }
} }
} }
MASTER_TICK += (PPQN / incomingPPQN);
uint32_t ticks_per_pulse = 96 / PPQNOPTS[EXTPPQNIdx];
MASTER_TICK = ((MASTER_TICK + (ticks_per_pulse / 2)) / ticks_per_pulse) *
ticks_per_pulse;
for (int i = 0; i < 8; i++) {
if (outputs[i]->lastTriggerTick > MASTER_TICK) {
outputs[i]->lastTriggerTick = 0xFFFFFFFF;
}
}
last_external_clk_us = now;
last_clk_us = now; last_clk_us = now;
last_valid_clk_us = now;
last_external_pulse_us = now;
external_pulse_received = true;
EXTERNAL_CLOCK = true;
BPM_UI_REFRESH += 1;
if (BPM_UI_REFRESH % 4 == 0) {
display_handler.updateScreen = 1;
}
} }
if (gpio == IN_RUN_PIN) { if (gpio == IN_RUN_PIN) {
@ -209,7 +246,6 @@ void setup_ins() {
gpio_init(IN_CLK_PIN); gpio_init(IN_CLK_PIN);
gpio_set_dir(IN_CLK_PIN, GPIO_IN); gpio_set_dir(IN_CLK_PIN, GPIO_IN);
gpio_pull_down(IN_CLK_PIN); gpio_pull_down(IN_CLK_PIN);
// Add to existing callback
gpio_set_irq_enabled(IN_CLK_PIN, GPIO_IRQ_EDGE_RISE, true); gpio_set_irq_enabled(IN_CLK_PIN, GPIO_IRQ_EDGE_RISE, true);
// SETUP CV INS // SETUP CV INS
@ -218,10 +254,8 @@ void setup_ins() {
adc_gpio_init(27); adc_gpio_init(27);
} }
// Helper to scale your current range to 0.0 - 1.0
float fmap(float x, float in_min, float in_max) { float fmap(float x, float in_min, float in_max) {
float result = (x - in_min) / (in_max - in_min); float result = (x - in_min) / (in_max - in_min);
// Constraints to keep it between 0.0 and 1.0
if (result < 0.0f) if (result < 0.0f)
return 0.0f; return 0.0f;
if (result > 1.0f) if (result > 1.0f)
@ -233,33 +267,24 @@ void update_cv() {
static uint64_t last_adc_read = 0; static uint64_t last_adc_read = 0;
uint64_t now = to_us_since_boot(get_absolute_time()); uint64_t now = to_us_since_boot(get_absolute_time());
if (now - last_adc_read < 2000) if (now - last_adc_read < 2000)
return; // 2ms is plenty fast return;
last_adc_read = now; last_adc_read = now;
// Calibration (Adjust these based on your earlier -0.19 to 0.15 range)
const float raw_min = -0.19f; const float raw_min = -0.19f;
const float raw_max = 0.15f; const float raw_max = 0.15f;
const float offset_zero = 0.404f; // Your calibrated offset const float offset_zero = 0.404f;
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
adc_select_input(i); adc_select_input(i);
// CROSSTALK FIX: Dummy read to clear the ADC capacitor
adc_read(); adc_read();
busy_wait_us(10); // Tiny pause to settle busy_wait_us(10);
// Actual read
float raw_val = (float)adc_read() * (1.0f / 4095.0f); float raw_val = (float)adc_read() * (1.0f / 4095.0f);
float centered = offset_zero - raw_val; float centered = offset_zero - raw_val;
// SCALING & FLIPPING:
// By using (max - centered), we flip the inversion.
float scaled = (centered - raw_min) / (raw_max - raw_min); float scaled = (centered - raw_min) / (raw_max - raw_min);
// Optional: If it's STILL upside down, use this instead:
// float scaled = 1.0f - ((centered - raw_min) / (raw_max - raw_min));
// Constrain 0.0 to 1.0
if (scaled < 0.01f) if (scaled < 0.01f)
scaled = 0.0f; scaled = 0.0f;
if (scaled > 1.0f) if (scaled > 1.0f)
@ -308,8 +333,39 @@ int main() {
if (RUN) { if (RUN) {
PLAY = false; PLAY = false;
} }
while (true) { while (true) {
uint64_t now = to_us_since_boot(get_absolute_time());
if (EXTERNAL_CLOCK && (now - last_external_pulse_us > CLOCK_TIMEOUT_US)) {
EXTERNAL_CLOCK = false;
BPM = globalSettings.bpm;
filteredBPM = (float)BPM;
update_period();
printf("Clock Lost. Internal BPM Resumed.\n");
}
if (external_pulse_received) {
external_pulse_received = false;
static uint8_t last_ppqn_idx = 0xFF;
if (EXTPPQNIdx != last_ppqn_idx) {
MASTER_TICK = 0;
for (Gate *g : outputs) {
g->lastTriggerTick = 0xFFFFFFFF;
g->state = 0;
}
last_ppqn_idx = EXTPPQNIdx;
}
BPM = filteredBPM;
if (PLAY) {
for (Gate *g : outputs) {
g->update();
}
}
}
update_cv(); update_cv();
encoder_handler.update(); encoder_handler.update();
@ -320,7 +376,5 @@ int main() {
g->turnOff(); g->turnOff();
} }
} }
lastPlayState = PLAY;
} }
} }