// Gate.cpp #include "Gate.h" #include "Mod.h" #include "Settings.h" #include "globals.h" #include "hardware/pwm.h" #include #include #include Gate::Gate(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2) : Output(pin, idx, slotIdx1, slotIdx2) { this->pin = pin; this->idx = idx; this->slotIdx1 = slotIdx1; this->slotIdx2 = slotIdx2; state = 0; editing = 0; modifierSelectionIndex = 8; divideMode = 0; // 1 divison | 0 multiplication modifier = 0; // divide mode modifier (4x, /32, etc) dur = 0; // how long pulse is on width = 50; // pulse width len = 0; // max len a pulse can be on, as determined by width p = 100; // probability of a pulse level = 100; modDest1 = (idx + 1) % 8; modDest2 = (idx + 2) % 8; } void Gate::pack(OutputConfig &cfg) { cfg.type = TYPE_GATE; GateSettings* s = (GateSettings*)cfg.data; s->modifierSelectionIndex = this->modifierSelectionIndex; s->divideMode = this->divideMode; s->modifier = this->modifier; s->width = this->width; s->p = this->p; s->level = this->level; s->shape = (uint8_t)this->shape; } void Gate::unpack(const OutputConfig &cfg) { if (cfg.type != TYPE_GATE) return; GateSettings s; memcpy(&s, cfg.data, sizeof(GateSettings)); this->modifierSelectionIndex = s.modifierSelectionIndex; this->divideMode = s.divideMode; this->modifier = s.modifier; this->width = s.width; this->p = s.p; this->level = s.level; this->shape = (WaveShape)s.shape; setDiv(this->modifierSelectionIndex); setWidth(this->width); } void Gate::setupPatches() { matrix.patch(this->slotIdx1, this->modDest1, this->idx, DEST_LEVEL, 100, false); matrix.patch(this->slotIdx2, this->modDest2, this->idx, DEST_LEVEL, 100, false); } void Gate::setLen(uint32_t currentPeriod) { len = (uint32_t)((double)currentPeriod * (width / 100.0) / 1000.0); } void Gate::setDiv(uint8_t modifier_selecton_index) { switch (modifier_selecton_index) { case 0: // x32 tickInterval = 3; isEnabled = true; divideMode = 0; modifier = 32; break; case 1: // x16 tickInterval = 6; isEnabled = true; divideMode = 0; modifier = 16; break; case 2: // x12 tickInterval = 8; isEnabled = true; divideMode = 0; modifier = 12; break; case 3: // x8 tickInterval = 12; isEnabled = true; divideMode = 0; modifier = 8; break; case 4: // x6 tickInterval = 16; isEnabled = true; divideMode = 0; modifier = 6; break; case 5: // x4 tickInterval = 24; isEnabled = true; divideMode = 0; modifier = 4; break; case 6: // x3 tickInterval = 32; isEnabled = true; divideMode = 0; modifier = 3; break; case 7: // x2 tickInterval = 48; isEnabled = true; divideMode = 0; modifier = 2; break; case 8: // 0 (OFF) tickInterval = 0; isEnabled = false; divideMode = 0; modifier = 0; break; case 9: // /1 tickInterval = 96; isEnabled = true; divideMode = 1; modifier = 1; break; case 10: // /2 tickInterval = 192; isEnabled = true; divideMode = 1; modifier = 2; break; case 11: // /3 tickInterval = 288; isEnabled = true; divideMode = 1; modifier = 3; break; case 12: // /4 tickInterval = 384; isEnabled = true; divideMode = 1; modifier = 4; break; case 13: // /6 tickInterval = 576; isEnabled = true; divideMode = 1; modifier = 4; break; case 14: // /8 tickInterval = 768; isEnabled = true; divideMode = 1; modifier = 8; break; case 15: // /16 tickInterval = 1536; isEnabled = true; divideMode = 1; modifier = 16; break; case 16: // /32 tickInterval = 3072; isEnabled = true; divideMode = 1; modifier = 16; break; default: tickInterval = 96; isEnabled = true; divideMode = 1; } this->triggerCount = 0; this->lastTriggerTick = 0xFFFFFFFF; setWidth(this->width); // this is called in width, check if needed still? calculatePulseWidth(); }; void Gate::setWidth(uint16_t newWidth) { this->width = newWidth; double ms_per_tick = (60000.0 / (double)BPM) / 96.0; double ms_between_triggers = ms_per_tick * (double)this->tickInterval; this->len = (uint32_t)(ms_between_triggers * (this->width / 100.0)); uint32_t max_allowed_len = (ms_between_triggers > 5) ? (uint32_t)ms_between_triggers - 2 : 1; if (this->len > max_allowed_len) { this->len = max_allowed_len; } if (this->len < 1) this->len = 1; double us_per_tick = 625000.0 / (double)BPM; calculatePulseWidth(); this->pulseDurationUs = (uint32_t)(us_per_tick * (double)this->pulseWidthTicks); if (this->pulseDurationUs < 100) this->pulseDurationUs = 100; } void Gate::calculatePulseWidth() { if (tickInterval == 0 || !isEnabled) { pulseWidthTicks = 0; return; } this->pulseWidthTicks = (uint32_t)((float)this->tickInterval * (this->width / 100.0f)); if (this->width > 0 && this->pulseWidthTicks == 0) { this->pulseWidthTicks = 1; } } void Gate::turnOn() { if (!isEnabled || tickInterval == 0) return; if (MASTER_TICK % tickInterval == 0) { if (MASTER_TICK != lastTriggerTick) { lastTriggerTick = MASTER_TICK; float baseP = (float)this->p; 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) { scheduledTick = 0xFFFFFFFF; return; } // swing triggerCount++; uint32_t swingDelayTicks = (uint32_t)((float)tickInterval * ((float)swing - 50.0f) / 100.0f); if (triggerCount % 2 == 0) { scheduledTick = MASTER_TICK + swingDelayTicks; } else { scheduledTick = MASTER_TICK; } } } if (MASTER_TICK >= scheduledTick && !state) { state = 1; startTick = MASTER_TICK; startTimeUs = time_us_64(); scheduledTick = 0xFFFFFFFF; currentRandomVal = (float)rand() / (float)RAND_MAX; } } void Gate::update() { if (!state && !sticky) { 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); return; } float phase = (float)elapsedUs / (float)pulseDurationUs; float outVal = 0; switch (shape) { case SINE: outVal = (sinf(phase * 2.0f * 3.14159f) * 0.5f) + 0.5f; break; case HALFSINE: // AKA HUMP outVal = sinf(phase * 3.14159f); break; case TRIANGLE: outVal = (phase < 0.5f) ? (phase * 2.0f) : (2.0f - (phase * 2.0f)); break; case SAW: outVal = 1.0f - phase; break; case RAMP: outVal = phase; break; case EXP: outVal = expf(-5.0f * phase); break; case REXP: outVal = expf(5.0f * (phase - 1.0f)); break; case LOG: outVal = 1.0f - expf(-5.0f * phase); break; case SQUARE: outVal = 1.0f; break; case BOUNCE: outVal = fabsf(sinf(phase * 3.14159f * 2.0f)); break; case SIGMO: outVal = phase * phase * (3.0f - 2.0f * phase); break; case WOBBLE: outVal = expf(-3.0f * phase) * cosf(phase * 3.14159f * 4.0f); if (outVal < 0) outVal = 0; break; case STEPDW: outVal = 1.0f - (floorf(phase * 4.0f) / 3.0f); break; case STEPUP: outVal = floorf(phase * 4.0f) / 3.0f; break; case SH: outVal = currentRandomVal; break; } this->lastOutVal = outVal; // handle width mod float effectiveWidth = (float)width + (widthMod * 100.0f); if (effectiveWidth > 100.0f) effectiveWidth = 100.0f; if (effectiveWidth < 1.0f) effectiveWidth = 1.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));} void Gate::writeAnalog(uint16_t val) { pwm_set_gpio_level(pin, val); } void Gate::turnOff() { writeAnalog(0); }