diff --git a/src/VCO.cpp b/src/VCO.cpp index bd5c7e1..cf0f2b7 100644 --- a/src/VCO.cpp +++ b/src/VCO.cpp @@ -213,8 +213,12 @@ static MinBlep<16, 16> minBlep; template struct VCOProcessor { T phase = 0.f; - T lastSync = 0.f; + /** 1 for forward, -1 for backward. */ T syncDirection = 1.f; + T lastSync = 0.f; + /** 1 or -1 */ + T lastSqrState = 1.f; + /** Leaky integrator result. */ T triFilterState = 0.f; OnePoleHighpass dcFilter; @@ -315,7 +319,14 @@ struct VCOProcessor { } }; - // Main logic + // Check if square value changed due to pulseWidth changing since last frame + if (frame.sqrEnabled || frame.triEnabled) { + T sqrState = simd::ifelse(prevPhase < pulseWidth, 1.f, -1.f); + T changed = (sqrState != lastSqrState); + T magnitude = (sqrState - lastSqrState) * syncDirection; + insertMinBlep(changed, 1e-6f, magnitude, sqrMinBlep); + } + if (!frame.syncEnabled) { // No sync. Process full frame T endPhase = prevPhase + deltaPhase; @@ -387,8 +398,8 @@ struct VCOProcessor { } if (frame.sqrEnabled || frame.triEnabled) { - T sqrHigh = (phase < pulseWidth) ^ syncDirection; - frame.sqr = simd::ifelse(sqrHigh, 1.f, -1.f); + frame.sqr = simd::ifelse(phase < pulseWidth, 1.f, -1.f); + lastSqrState = frame.sqr; frame.sqr += sqrMinBlep.shift(); T triSqr = frame.sqr; frame.sqr = dcFilter.process(dcFilterStateSqr, frame.sqr);