diff --git a/Source/LFO.cpp b/Source/LFO.cpp index 67573fb..9530a24 100644 --- a/Source/LFO.cpp +++ b/Source/LFO.cpp @@ -10,14 +10,16 @@ void LFO::reset() delayCounter = 0.0; fadeCounter = 0.0; fadeVal = 1.0f; - holdValue = 0.0f; - prevPhase = 0.0; + holdValue = rng.nextFloat() * 2.0f - 1.0f; prevDelayParam = -1.0f; prevFadeParam = -1.0f; - shapeBuffer.assign ((size_t) kShapePoints, 0.0f); - // default step sequence - for (int i = 0; i < kShapePoints; ++i) - shapeBuffer[(size_t) i] = (i % 2 == 0) ? 1.0f : -1.0f; + if (shapeBuffer.empty()) + { + shapeBuffer.resize ((size_t) kShapePoints); + // default step sequence + for (int i = 0; i < kShapePoints; ++i) + shapeBuffer[(size_t) i] = (i % 2 == 0) ? 1.0f : -1.0f; + } } void LFO::setParams (float rateNorm, bool s, float b, int shp, float ph, @@ -26,10 +28,10 @@ void LFO::setParams (float rateNorm, bool s, float b, int shp, float ph, sync = s; beat = b; shape = juce::jlimit (0, (int) LfoShape::Count - 1, shp); - phase = juce::jlimit (0.0f, 1.0f, ph); + phaseOffset = juce::jlimit (0.0f, 1.0f, ph); if (sync) - rateHz = maps::beatToMultiplier (beat) * (tempo / 60.0); + rateHz = (tempo / 60.0) / maps::beatToMultiplier (beat); else rateHz = maps::rateToHz (rateNorm); @@ -46,6 +48,7 @@ void LFO::setParams (float rateNorm, bool s, float b, int shp, float ph, prevDelayParam = delay; prevFadeParam = fade; } + value = delayCounter > 0.0 ? 0.0f : shapeValue() * fadeVal; } void LFO::setShapeData (const std::vector& data, int steps) @@ -54,17 +57,18 @@ void LFO::setShapeData (const std::vector& data, int steps) return; shapeSteps = juce::jlimit (2, kShapePoints, steps); - shapeBuffer.assign (data.begin(), data.end()); - shapeBuffer.resize ((size_t) kShapePoints, 0.0f); + for (size_t i = 0; i < shapeBuffer.size(); ++i) + shapeBuffer[i] = i < data.size() && std::isfinite (data[i]) + ? juce::jlimit (-1.0f, 1.0f, data[i]) : 0.0f; } float LFO::shapeValue() noexcept { - const float p = (float) phase; + const float p = getPhase(); switch ((LfoShape) shape) { case LfoShape::Sine: - return std::sin (p * 6.28318530717958647692f); + return std::sin (p * juce::MathConstants::twoPi); case LfoShape::Triangle: return 1.0f - 4.0f * std::abs (p - 0.5f); case LfoShape::Saw: @@ -72,11 +76,7 @@ float LFO::shapeValue() noexcept case LfoShape::Square: return (p < 0.5f) ? 1.0f : -1.0f; case LfoShape::SampleHold: - { - if (phase < prevPhase) - holdValue = rng.nextFloat() * 2.0f - 1.0f; return holdValue; - } case LfoShape::StepSeq: { const int idx = juce::jlimit (0, shapeSteps - 1, (int) (p * shapeSteps)); @@ -97,27 +97,40 @@ float LFO::shapeValue() noexcept float LFO::process() noexcept { + return advance (1); +} + +float LFO::advance (int numSamples) noexcept +{ + if (numSamples <= 0) + return value; + // Start delay. if (delayCounter > 0.0) { - delayCounter -= 1.0; - value = 0.0f; - return 0.0f; + const int skipped = (int) juce::jmin ((double) numSamples, std::ceil (delayCounter)); + delayCounter = juce::jmax (0.0, delayCounter - skipped); + numSamples -= skipped; + if (numSamples == 0) + return value = 0.0f; } // Fade-in ramp. if (fadeVal < 1.0f) { - fadeCounter += 1.0; + fadeCounter += numSamples; if (fadeSeconds > 0.0) fadeVal = (float) juce::jlimit (0.0, 1.0, fadeCounter / (fadeSeconds * sr)); else fadeVal = 1.0f; } - prevPhase = phase; - phase += rateHz / sr; - phase -= std::floor (phase); + phase += rateHz * numSamples / sr; + const double cycles = std::floor (phase); + phase -= cycles; + if (shape == (int) LfoShape::SampleHold) + for (int i = 0; i < (int) cycles; ++i) + holdValue = rng.nextFloat() * 2.0f - 1.0f; value = shapeValue() * fadeVal; return value; diff --git a/Source/LFO.h b/Source/LFO.h index ad57a53..2974e9c 100644 --- a/Source/LFO.h +++ b/Source/LFO.h @@ -15,6 +15,7 @@ class LFO public: static constexpr int kShapePoints = 64; + LFO() { reset(); } void prepare (double sampleRate) { sr = sampleRate; reset(); } void reset(); @@ -24,7 +25,8 @@ public: void setShapeData (const std::vector& data, int steps); float process() noexcept; // advance and return current value - float getPhase() const noexcept { return (float) phase; } + float advance (int numSamples) noexcept; + float getPhase() const noexcept { return (float) (phase + phaseOffset - std::floor (phase + phaseOffset)); } float getValue() const noexcept { return value; } const std::vector& getShapeData() const noexcept { return shapeBuffer; } @@ -35,6 +37,7 @@ private: double tempo = 120.0; double phase = 0.0; + double phaseOffset = 0.0; double rateHz = 1.0; float value = 0.0f; @@ -55,7 +58,6 @@ private: juce::Random rng; float holdValue = 0.0f; - double prevPhase = 0.0; float shapeValue() noexcept; };