127 lines
3.3 KiB
C++
127 lines
3.3 KiB
C++
#include "LFO.h"
|
|
|
|
namespace serum
|
|
{
|
|
|
|
void LFO::reset()
|
|
{
|
|
phase = 0.0;
|
|
value = 0.0f;
|
|
delayCounter = 0.0;
|
|
fadeCounter = 0.0;
|
|
fadeVal = 1.0f;
|
|
holdValue = 0.0f;
|
|
prevPhase = 0.0;
|
|
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;
|
|
}
|
|
|
|
void LFO::setParams (float rateNorm, bool s, float b, int shp, float ph,
|
|
float fade, float delay)
|
|
{
|
|
sync = s;
|
|
beat = b;
|
|
shape = juce::jlimit (0, (int) LfoShape::Count - 1, shp);
|
|
phase = juce::jlimit (0.0f, 1.0f, ph);
|
|
|
|
if (sync)
|
|
rateHz = maps::beatToMultiplier (beat) * (tempo / 60.0);
|
|
else
|
|
rateHz = maps::rateToHz (rateNorm);
|
|
|
|
delaySeconds = juce::jlimit (0.0f, 1.0f, delay) * 4.0;
|
|
fadeSeconds = juce::jlimit (0.0f, 1.0f, fade) * 8.0;
|
|
|
|
// Only restart the delay/fade timing when those knobs actually change,
|
|
// so repeated block-rate setParams calls don't keep resetting the LFO.
|
|
if (delay != prevDelayParam || fade != prevFadeParam)
|
|
{
|
|
delayCounter = delaySeconds * sr;
|
|
fadeCounter = 0.0;
|
|
fadeVal = (fadeSeconds <= 0.0) ? 1.0f : 0.0f;
|
|
prevDelayParam = delay;
|
|
prevFadeParam = fade;
|
|
}
|
|
}
|
|
|
|
void LFO::setShapeData (const std::vector<float>& data, int steps)
|
|
{
|
|
if (data.empty())
|
|
return;
|
|
|
|
shapeSteps = juce::jlimit (2, kShapePoints, steps);
|
|
shapeBuffer.assign (data.begin(), data.end());
|
|
shapeBuffer.resize ((size_t) kShapePoints, 0.0f);
|
|
}
|
|
|
|
float LFO::shapeValue() noexcept
|
|
{
|
|
const float p = (float) phase;
|
|
switch ((LfoShape) shape)
|
|
{
|
|
case LfoShape::Sine:
|
|
return std::sin (p * 6.28318530717958647692f);
|
|
case LfoShape::Triangle:
|
|
return 1.0f - 4.0f * std::abs (p - 0.5f);
|
|
case LfoShape::Saw:
|
|
return 2.0f * p - 1.0f;
|
|
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));
|
|
return shapeBuffer[(size_t) idx];
|
|
}
|
|
case LfoShape::Freehand:
|
|
{
|
|
const float pos = p * (float) (shapeSteps - 1);
|
|
const int i0 = (int) pos;
|
|
const int i1 = juce::jmin (i0 + 1, shapeSteps - 1);
|
|
const float frac = pos - (float) i0;
|
|
return shapeBuffer[(size_t) i0] * (1.0f - frac) + shapeBuffer[(size_t) i1] * frac;
|
|
}
|
|
default:
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
float LFO::process() noexcept
|
|
{
|
|
// Start delay.
|
|
if (delayCounter > 0.0)
|
|
{
|
|
delayCounter -= 1.0;
|
|
value = 0.0f;
|
|
return 0.0f;
|
|
}
|
|
|
|
// Fade-in ramp.
|
|
if (fadeVal < 1.0f)
|
|
{
|
|
fadeCounter += 1.0;
|
|
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);
|
|
|
|
value = shapeValue() * fadeVal;
|
|
return value;
|
|
}
|
|
|
|
} // namespace serum
|