diff --git a/include/slime/dsp/MinBLEPSquare.hpp b/include/slime/dsp/MinBLEPSquare.hpp index 3c7ac62..9fc1e80 100644 --- a/include/slime/dsp/MinBLEPSquare.hpp +++ b/include/slime/dsp/MinBLEPSquare.hpp @@ -28,6 +28,7 @@ public: void reset() { value = static_cast(0.0f); _phase = static_cast(0.0f); + _target = static_cast(0.0f); _filter.reset(); } @@ -49,7 +50,7 @@ public: // Calculate waveform value = rack::simd::ifelse(_phase < _width, 1.0f, -1.0f); - insertDiscontinuities(delta_phase); + insertDiscontinuities(value, delta_phase); value += _minblep_generator.process(); // Filter highpass @@ -61,6 +62,7 @@ public: } private: + T _target = 0.0f; T _phase = 0.0f; T _frequency; T _width = 0.5f; @@ -68,31 +70,45 @@ private: rack::dsp::TRCFilter _filter; rack::dsp::MinBlepGenerator<16, 16, T> _minblep_generator; - inline void insertDiscontinuities(T delta_phase); + inline void insertDiscontinuities(T target, T delta_phase); }; template <> -inline void MinBLEPSquareOscillator::insertDiscontinuities(float delta_phase) { +inline void MinBLEPSquareOscillator::insertDiscontinuities(float target, float delta_phase) { + // Only calculate if a discontinuity has actually occurred. + if (target == _target) { + _target = target; + return; + } + _target = target; + // Insert discontinuity where phase crosses 0 float zero_cross = (delta_phase - _phase) / delta_phase; - if ((0.0f < zero_cross) & (zero_cross <= 1.0f)) { + if ((0.0f < zero_cross) && (zero_cross <= 1.0f)) { _minblep_generator.insertDiscontinuity(zero_cross - 1.0f, 2.0f); } // Insert discontinuity where phase crosses pulse width float pulse_cross = (_width - (_phase - delta_phase)) / delta_phase; - if ((0.0f < pulse_cross) & (pulse_cross <= 1.0f)) { + if ((0.0f < pulse_cross) && (pulse_cross <= 1.0f)) { _minblep_generator.insertDiscontinuity(pulse_cross - 1.0f, -2.0f); } } template -inline void MinBLEPSquareOscillator::insertDiscontinuities(T delta_phase) { - // SIMD code adapted from VCV's Fundamental VCO +inline void MinBLEPSquareOscillator::insertDiscontinuities(T target, T delta_phase) { + // Only calculate if a discontinuity has actually occurred + int change_mask = rack::simd::movemask(target != _target); + _target = target; + if (change_mask == 0) { + return; + } + + // SIMD code adapted from VCV's Fundamental VCO, with some extra work to prevent duplicates // Insert discontinuity where phase crosses 0 T zero_cross = (delta_phase - _phase) / delta_phase; - int zero_mask = rack::simd::movemask((0.0f < zero_cross) & (zero_cross <= 1.0f)); + int zero_mask = change_mask & rack::simd::movemask((0.0f < zero_cross) & (zero_cross <= 1.0f)); if (zero_mask) { for (int i = 0; i < channels; i++) { if (zero_mask & (1 << i)) { @@ -106,7 +122,8 @@ inline void MinBLEPSquareOscillator::insertDiscontinuities(T delta_phase) { // Insert discontinuity where phase crosses pulse width T pulse_cross = (_width - (_phase - delta_phase)) / delta_phase; - int pulse_mask = rack::simd::movemask((0.0f < pulse_cross) & (pulse_cross <= 1.0f)); + int pulse_mask = change_mask & rack::simd::movemask((0.0f < pulse_cross) & (pulse_cross <= 1.0f)); + pulse_mask = pulse_mask & ~zero_mask; // Don't double-insert! This is probably overkill but it's cheap if (pulse_mask) { for (int i = 0; i < channels; i++) { if (pulse_mask & (1 << i)) {