|
@@ -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; |
|
|
} |
|
|
} |
|
|