From dec738ffde090bd4d912218c7551739040604dd1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 2 Jan 2026 01:59:46 -0500 Subject: [PATCH] VCO: Fix minBLEP cross detection for backwards phase and narrow pulse width. --- src/VCO.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/VCO.cpp b/src/VCO.cpp index cf0f2b7..e987b97 100644 --- a/src/VCO.cpp +++ b/src/VCO.cpp @@ -286,12 +286,13 @@ struct VCOProcessor { }; // Computes subsample time where phase crosses threshold. - auto getCrossing = [](T threshold, T startPhase, T endPhase, T startSubsample, T endSubsample) -> T { - // Wrap threshold mod 1 to be in (startPhase, startPhase + 1] - threshold -= simd::floor(threshold - startPhase); - // Map to (0, 1] - T p = (threshold - startPhase) / (endPhase - startPhase); - // Map to (startSubsample, endSubsample] + auto getCrossing = [](T thresholdPhase, T startPhase, T endPhase, T startSubsample, T endSubsample) -> T { + T delta = endPhase - startPhase; + T diff = thresholdPhase - startPhase; + // Forward: wrap thresholdPhase to (startPhase, startPhase+1] + // Backward: wrap thresholdPhase to [startPhase-1, startPhase) + thresholdPhase -= simd::ifelse(delta >= 0.f, simd::floor(diff), simd::ceil(diff)); + T p = (thresholdPhase - startPhase) / delta; return startSubsample + p * (endSubsample - startSubsample); }; @@ -300,19 +301,19 @@ struct VCOProcessor { // channelMask limits processing to specific channels. auto processCrossings = [&](T startPhase, T endPhase, T startSubsample, T endSubsample, T channelMask) { if (frame.sqrEnabled || frame.triEnabled) { - // Wrap crossing (phase crosses 0 mod 1) + // Insert minBLEP to square when phase crosses 0 (mod 1) T wrapSubsample = getCrossing(1.f, startPhase, endPhase, startSubsample, endSubsample); T wrapMask = channelMask & (startSubsample < wrapSubsample) & (wrapSubsample <= endSubsample); insertMinBlep(wrapMask, wrapSubsample, 2.f * syncDirection, sqrMinBlep); - // Pulse width crossing + // Insert minBLEP to square when phase crosses pulse width T pulseSubsample = getCrossing(pulseWidth, startPhase, endPhase, startSubsample, endSubsample); T pulseMask = channelMask & (startSubsample < pulseSubsample) & (pulseSubsample <= endSubsample); insertMinBlep(pulseMask, pulseSubsample, -2.f * syncDirection, sqrMinBlep); } if (frame.sawEnabled) { - // Saw crossing at 0.5 + // Insert minBLEP to saw when crossing 0.5 T sawSubsample = getCrossing(0.5f, startPhase, endPhase, startSubsample, endSubsample); T sawMask = channelMask & (startSubsample < sawSubsample) & (sawSubsample <= endSubsample); insertMinBlep(sawMask, sawSubsample, -2.f * syncDirection, sawMinBlep);