diff --git a/src/Gate.cpp b/src/Gate.cpp index 1f8c19c..97f7720 100644 --- a/src/Gate.cpp +++ b/src/Gate.cpp @@ -294,59 +294,58 @@ void Gate::update() { // 1. EXIT EARLY IF OFF if (!state && !sticky) { lastOutVal = 0.0f; - // Make sure we actually write 0 if we aren't sticky writeAnalog(0); return; } // 2. LIVE WIDTH MODULATION - // We calculate the 'stopTick' every frame. - // IMPORTANT: Cap at 98% to ensure the gate has a "low" period before next beat. float effectiveWidth = (float)width + (widthMod * 100.0f); - if (effectiveWidth > 98.0f) effectiveWidth = 98.0f; - if (effectiveWidth < 1.0f) effectiveWidth = 1.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)); - if (modulatedTicks < 1) modulatedTicks = 1; - - // This is our "Target" end point this->stopTick = startTick + modulatedTicks; - // 3. THE HARD SYNC (THE FIX) - // If the Master Tick reached stopTick, kill the gate. - if (MASTER_TICK >= stopTick) { - state = 0; - // Don't reset lastTriggerTick here, otherwise turnOn() might re-fire - // on the same tick that we just finished. - if (!sticky) { - lastOutVal = 0.0f; - writeAnalog(0); + // 3. THE HARD SYNC + // Only kill the gate if width is strictly less than 100% + if (effectiveWidth < 100.0f) { + if (MASTER_TICK >= stopTick) { + state = 0; + if (!sticky) { + lastOutVal = 0.0f; + writeAnalog(0); + } + return; } - return; } // 4. HYBRID SMOOTHNESS MATH uint64_t now = time_us_64(); - // Ensure we don't underflow if now < last_clk_us (jitter) 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.98f) subTick = 0.98f; + if (subTick > 0.99f) subTick = 0.99f; - // Calculate phase (0.0 to 1.0) + // --- THE SINE WAVE FIX --- + // If width is 100%, we calculate phase based on the WHOLE interval. + // If width < 100%, we calculate phase based on the PULSE duration. float elapsedTicks = (float)(MASTER_TICK - startTick) + subTick; - float totalDurationTicks = (float)(stopTick - startTick); + float totalDurationTicks = (effectiveWidth >= 100.0f) ? (float)tickInterval : (float)(stopTick - startTick); - // Safety check for division by zero if (totalDurationTicks < 1.0f) totalDurationTicks = 1.0f; float phase = elapsedTicks / totalDurationTicks; - if (phase > 1.0f) phase = 1.0f; + + // Keep phase looping if we are at 100% width (so LFOs/Sines keep moving) + 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; // 5. WAVEFORM GENERATION @@ -360,7 +359,7 @@ void Gate::update() { 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; // Square is simple ON + case SQUARE: outVal = 1.0f; break; case BOUNCE: outVal = fabsf(sinf(phase * 3.14159265f * 2.0f)); break; case SIGMO: outVal = phase * phase * (3.0f - 2.0f * phase); break; case WOBBLE: outVal = expf(-3.0f * phase) * cosf(phase * 3.14159265f * 4.0f); @@ -378,7 +377,6 @@ void Gate::update() { if (finalLevel > 1.0f) finalLevel = 1.0f; if (finalLevel < 0.0f) finalLevel = 0.0f; - // Use (uint16_t) cast to ensure the PWM driver gets a clean integer writeAnalog((uint16_t)(outVal * 1023.0f * finalLevel)); }