refactor(source): move every implementation into its module directory
- 36 files move into the module whose AGENTS.md already claimed them: Engine/, Synth/, Modulation/, Params/, Plugin/, GUI/, EffectUnits/, Resources/ - Source/engine, modulation, params, plugin and synth become Engine, Modulation, Params, Plugin and Synth, matching the directories that already hold code - ExactCompare.h stays at the Source/ root as the one shared header - SERUMALT_INCLUDE_DIRS adds one include directory per module, so the 31 parent-relative includes become plain names with no path prefix - the module AGENTS.md files list owned files instead of describing a move that had not happened, and README.md and AGENTS.md describe the real tree Verified: the harness passes CTest, the Linux VST3 and Standalone link, the Windows cross-build passes its module checks, each configure exposes only its own targets, no first-party warning line appears, and the generated Windows manifest is unchanged.
This commit is contained in:
1 parent
facd5c5b5e
commit
4b0db50271
70 files changed
+115
-111
No files matched your search
@@ -0,0 +1,152 @@
|
||||
#include "LFO.h"
|
||||
#include "ExactCompare.h"
|
||||
|
||||
namespace serum
|
||||
{
|
||||
|
||||
void LFO::reset()
|
||||
{
|
||||
phase = 0.0;
|
||||
value = 0.0f;
|
||||
delayCounter = 0.0;
|
||||
fadeCounter = 0.0;
|
||||
fadeVal = 1.0f;
|
||||
holdValue = rng.nextFloat() * 2.0f - 1.0f;
|
||||
prevDelayParam = -1.0f;
|
||||
prevFadeParam = -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,
|
||||
float fade, float delay)
|
||||
{
|
||||
sync = s;
|
||||
beat = b;
|
||||
shape = juce::jlimit (0, (int) LfoShape::Count - 1, shp);
|
||||
phaseOffset = juce::jlimit (0.0f, 1.0f, ph);
|
||||
|
||||
if (sync)
|
||||
rateHz = (tempo / 60.0) / maps::beatToMultiplier (beat);
|
||||
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 (changed (delay, prevDelayParam) || changed (fade, prevFadeParam))
|
||||
{
|
||||
delayCounter = delaySeconds * sr;
|
||||
fadeCounter = 0.0;
|
||||
fadeVal = (fadeSeconds <= 0.0) ? 1.0f : 0.0f;
|
||||
prevDelayParam = delay;
|
||||
prevFadeParam = fade;
|
||||
}
|
||||
value = delayCounter > 0.0 ? 0.0f : shapeValue() * fadeVal;
|
||||
}
|
||||
|
||||
void LFO::setShapeData (const std::vector<float>& data, int steps)
|
||||
{
|
||||
if (data.empty())
|
||||
return;
|
||||
|
||||
shapeSteps = juce::jlimit (2, kShapePoints, steps);
|
||||
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 lfoShapeValue (float phase, LfoShape shape, int steps,
|
||||
const float* data, int dataSize, float sampleHoldValue) noexcept
|
||||
{
|
||||
const int numSteps = juce::jlimit (2, LFO::kShapePoints, steps);
|
||||
const int numData = data != nullptr ? juce::jlimit (0, LFO::kShapePoints, dataSize) : 0;
|
||||
|
||||
auto point = [data, numData] (int index) noexcept
|
||||
{
|
||||
return numData > 0 ? data[(size_t) juce::jlimit (0, numData - 1, index)] : 0.0f;
|
||||
};
|
||||
|
||||
switch (shape)
|
||||
{
|
||||
case LfoShape::Sine:
|
||||
return std::sin (phase * juce::MathConstants<float>::twoPi);
|
||||
case LfoShape::Triangle:
|
||||
return 1.0f - 4.0f * std::abs (phase - 0.5f);
|
||||
case LfoShape::Saw:
|
||||
return 2.0f * phase - 1.0f;
|
||||
case LfoShape::Square:
|
||||
return (phase < 0.5f) ? 1.0f : -1.0f;
|
||||
case LfoShape::SampleHold:
|
||||
return sampleHoldValue;
|
||||
case LfoShape::StepSeq:
|
||||
return point (juce::jlimit (0, numSteps - 1, (int) (phase * (float) numSteps)));
|
||||
case LfoShape::Freehand:
|
||||
{
|
||||
const float pos = phase * (float) (numSteps - 1);
|
||||
const int i0 = (int) pos;
|
||||
const int i1 = juce::jmin (i0 + 1, numSteps - 1);
|
||||
const float frac = pos - (float) i0;
|
||||
return point (i0) * (1.0f - frac) + point (i1) * frac;
|
||||
}
|
||||
case LfoShape::Count:
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float LFO::shapeValue() noexcept
|
||||
{
|
||||
return lfoShapeValue (getPhase(), (LfoShape) shape, shapeSteps,
|
||||
shapeBuffer.data(), (int) shapeBuffer.size(), holdValue);
|
||||
}
|
||||
|
||||
float LFO::process() noexcept
|
||||
{
|
||||
return advance (1);
|
||||
}
|
||||
|
||||
float LFO::advance (int numSamples) noexcept
|
||||
{
|
||||
if (numSamples <= 0)
|
||||
return value;
|
||||
|
||||
// Start delay.
|
||||
if (delayCounter > 0.0)
|
||||
{
|
||||
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 += numSamples;
|
||||
if (fadeSeconds > 0.0)
|
||||
fadeVal = (float) juce::jlimit (0.0, 1.0, fadeCounter / (fadeSeconds * sr));
|
||||
else
|
||||
fadeVal = 1.0f;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace serum
|
||||
Reference in new issue
Block a user