Add advance(numSamples) so the engine can step LFOs per sub-block instead of calling process() in a sample loop. process() now delegates to advance(1). Split phaseOffset from phase so setParams doesn't overwrite the running phase; getPhase() adds the offset and wraps. Fix sync mode to divide by beat multiplier instead of multiplying. Move SampleHold randomization into advance() based on cycle count. Validate shape data in setShapeData with isfinite and jlimit.
66 lines
1.9 KiB
C++
66 lines
1.9 KiB
C++
#pragma once
|
|
|
|
#include <JuceHeader.h>
|
|
#include "Params.h"
|
|
|
|
namespace serum
|
|
{
|
|
|
|
// ===========================================================================
|
|
// Free-running LFO with tempo sync, seven shapes (sine/tri/saw/square/S&H/
|
|
// step-sequencer/freehand), adjustable phase, fade-in and start delay.
|
|
// ===========================================================================
|
|
class LFO
|
|
{
|
|
public:
|
|
static constexpr int kShapePoints = 64;
|
|
|
|
LFO() { reset(); }
|
|
void prepare (double sampleRate) { sr = sampleRate; reset(); }
|
|
void reset();
|
|
|
|
void setParams (float rateNorm, bool sync, float beat, int shape, float phase,
|
|
float fade, float delay);
|
|
void setTempo (double bpm) noexcept { tempo = bpm; }
|
|
void setShapeData (const std::vector<float>& data, int steps);
|
|
|
|
float process() noexcept; // advance and return current value
|
|
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<float>& getShapeData() const noexcept { return shapeBuffer; }
|
|
int getShapeSteps() const noexcept { return shapeSteps; }
|
|
|
|
private:
|
|
double sr = 44100.0;
|
|
double tempo = 120.0;
|
|
|
|
double phase = 0.0;
|
|
double phaseOffset = 0.0;
|
|
double rateHz = 1.0;
|
|
float value = 0.0f;
|
|
|
|
int shape = 0;
|
|
bool sync = false;
|
|
float beat = 0.25f;
|
|
|
|
double delaySeconds = 0.0;
|
|
double fadeSeconds = 0.0;
|
|
double delayCounter = 0.0; // samples remaining before start
|
|
double fadeCounter = 0.0; // samples elapsed in fade
|
|
float fadeVal = 1.0f;
|
|
float prevDelayParam = -1.0f;
|
|
float prevFadeParam = -1.0f;
|
|
|
|
std::vector<float> shapeBuffer;
|
|
int shapeSteps = 16;
|
|
|
|
juce::Random rng;
|
|
float holdValue = 0.0f;
|
|
|
|
float shapeValue() noexcept;
|
|
};
|
|
|
|
} // namespace serum
|