| @@ -38,6 +38,7 @@ struct VoltageControlledOscillator { | |||||
| T freq = 0.f; | T freq = 0.f; | ||||
| T pulseWidth = 0.5f; | T pulseWidth = 0.5f; | ||||
| T syncDirection = 1.f; | T syncDirection = 1.f; | ||||
| T sqrState = 1.f; | |||||
| dsp::TRCFilter<T> sqrFilter; | dsp::TRCFilter<T> sqrFilter; | ||||
| @@ -68,37 +69,44 @@ struct VoltageControlledOscillator { | |||||
| syncDirection = 1.f; | syncDirection = 1.f; | ||||
| } | } | ||||
| phase += deltaPhase; | phase += deltaPhase; | ||||
| // Wrap phase | |||||
| phase -= simd::floor(phase); | |||||
| // Jump sqr when crossing 0, or 1 if backwards | |||||
| T wrapPhase = (syncDirection == -1.f) & 1.f; | |||||
| T wrapCrossing = (wrapPhase - (phase - deltaPhase)) / deltaPhase; | |||||
| int wrapMask = simd::movemask((0 < wrapCrossing) & (wrapCrossing <= 1.f)); | |||||
| if (wrapMask) { | |||||
| // Wrap phase | |||||
| T phaseFloor = simd::floor(phase); | |||||
| phase -= phaseFloor; | |||||
| // Jump sqr when phase crosses 1, or crosses 0 if running backwards | |||||
| T wrapMask = (phaseFloor != 0.f); | |||||
| int wrapM = simd::movemask(wrapMask); | |||||
| if (wrapM) { | |||||
| T wrapPhase = (syncDirection == -1.f) & 1.f; | |||||
| T wrapCrossing = (wrapPhase - (phase - deltaPhase)) / deltaPhase; | |||||
| for (int i = 0; i < channels; i++) { | for (int i = 0; i < channels; i++) { | ||||
| if (wrapMask & (1 << i)) { | |||||
| if (wrapM & (1 << i)) { | |||||
| T mask = simd::movemaskInverse<T>(1 << i); | T mask = simd::movemaskInverse<T>(1 << i); | ||||
| float p = wrapCrossing[i] - 1.f; | |||||
| // TODO: MinBlepGenerator::insertDiscontinuity() should handle subframes outside the range -1 < p <= 0 instead of failing silently. | |||||
| float p = clamp(wrapCrossing[i] - 1.f, -1.f, 0.f); | |||||
| T x = mask & (2.f * syncDirection); | T x = mask & (2.f * syncDirection); | ||||
| sqrMinBlep.insertDiscontinuity(p, x); | sqrMinBlep.insertDiscontinuity(p, x); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| sqrState = simd::ifelse(wrapMask, syncDirection, sqrState); | |||||
| // Jump sqr when crossing `pulseWidth` | // Jump sqr when crossing `pulseWidth` | ||||
| T pulseCrossing = (pulseWidth - (phase - deltaPhase)) / deltaPhase; | |||||
| int pulseMask = simd::movemask((0 < pulseCrossing) & (pulseCrossing <= 1.f)); | |||||
| if (pulseMask) { | |||||
| T pwMask = (syncDirection == sqrState) & ((syncDirection == 1.f) ^ (phase < pulseWidth)); | |||||
| int pw = simd::movemask(pwMask); | |||||
| if (pw) { | |||||
| T pulseCrossing = (pulseWidth - (phase - deltaPhase)) / deltaPhase; | |||||
| for (int i = 0; i < channels; i++) { | for (int i = 0; i < channels; i++) { | ||||
| if (pulseMask & (1 << i)) { | |||||
| if (pw & (1 << i)) { | |||||
| T mask = simd::movemaskInverse<T>(1 << i); | T mask = simd::movemaskInverse<T>(1 << i); | ||||
| float p = pulseCrossing[i] - 1.f; | |||||
| float p = clamp(pulseCrossing[i] - 1.f, -1.f, 0.f); | |||||
| T x = mask & (-2.f * syncDirection); | T x = mask & (-2.f * syncDirection); | ||||
| sqrMinBlep.insertDiscontinuity(p, x); | sqrMinBlep.insertDiscontinuity(p, x); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| sqrState = simd::ifelse(pwMask, -syncDirection, sqrState); | |||||
| // Jump saw when crossing 0.5 | // Jump saw when crossing 0.5 | ||||
| T halfCrossing = (0.5f - (phase - deltaPhase)) / deltaPhase; | T halfCrossing = (0.5f - (phase - deltaPhase)) / deltaPhase; | ||||
| @@ -134,7 +142,9 @@ struct VoltageControlledOscillator { | |||||
| T mask = simd::movemaskInverse<T>(1 << i); | T mask = simd::movemaskInverse<T>(1 << i); | ||||
| float p = syncCrossing[i] - 1.f; | float p = syncCrossing[i] - 1.f; | ||||
| T x; | T x; | ||||
| x = mask & (sqr(newPhase) - sqr(phase)); | |||||
| // Assume that hard-syncing a square always resets it to HIGH | |||||
| x = mask & (1.f - sqrState); | |||||
| sqrState = simd::ifelse(mask, 1.f, sqrState); | |||||
| sqrMinBlep.insertDiscontinuity(p, x); | sqrMinBlep.insertDiscontinuity(p, x); | ||||
| x = mask & (saw(newPhase) - saw(phase)); | x = mask & (saw(newPhase) - saw(phase)); | ||||
| sawMinBlep.insertDiscontinuity(p, x); | sawMinBlep.insertDiscontinuity(p, x); | ||||
| @@ -150,7 +160,7 @@ struct VoltageControlledOscillator { | |||||
| } | } | ||||
| // Square | // Square | ||||
| sqrValue = sqr(phase); | |||||
| sqrValue = sqrState; | |||||
| sqrValue += sqrMinBlep.process(); | sqrValue += sqrMinBlep.process(); | ||||
| if (analog) { | if (analog) { | ||||
| @@ -227,10 +237,6 @@ struct VoltageControlledOscillator { | |||||
| return sawValue; | return sawValue; | ||||
| } | } | ||||
| T sqr(T phase) { | |||||
| T v = simd::ifelse(phase < pulseWidth, 1.f, -1.f); | |||||
| return v; | |||||
| } | |||||
| T sqr() { | T sqr() { | ||||
| return sqrValue; | return sqrValue; | ||||
| } | } | ||||