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:
biggy committed 2026-10-06 09:17:49 +02:00
1 parent facd5c5b5e
commit 4b0db50271
70 files changed
+115 -111

No files matched your search

+83
View File
@@ -0,0 +1,83 @@
#include "FilterBank.h"
#include "ExactCompare.h"
namespace serum
{
void FilterBank::prepare (double sampleRate)
{
f1L.prepare (sampleRate);
f1R.prepare (sampleRate);
f2L.prepare (sampleRate);
f2R.prepare (sampleRate);
}
void FilterBank::reset()
{
f1L.reset(); f1R.reset(); f2L.reset(); f2R.reset();
}
void FilterBank::applySlot (Filter& f, float* buf, int n, bool on, int type, float cutoff,
float res, float drive, float key, int slope, float noteHz) noexcept
{
if (on)
f.process (buf, n, cutoff, res, drive, key, noteHz, type, slope);
}
void FilterBank::process (float* l, float* r, int numSamples, const FilterBankParams& p, float noteHz) noexcept
{
const bool anyFilter = p.f1On || p.f2On;
if (! anyFilter)
return;
if (p.route == (int) FilterRoute::Serial)
{
applySlot (f1L, l, numSamples, p.f1On, p.f1Type, p.f1Cutoff, p.f1Res, p.f1Drive, p.f1Key, p.f1Slope, noteHz);
applySlot (f1R, r, numSamples, p.f1On, p.f1Type, p.f1Cutoff, p.f1Res, p.f1Drive, p.f1Key, p.f1Slope, noteHz);
applySlot (f2L, l, numSamples, p.f2On, p.f2Type, p.f2Cutoff, p.f2Res, p.f2Drive, p.f2Key, p.f2Slope, noteHz);
applySlot (f2R, r, numSamples, p.f2On, p.f2Type, p.f2Cutoff, p.f2Res, p.f2Drive, p.f2Key, p.f2Slope, noteHz);
}
else if (p.route == (int) FilterRoute::Parallel)
{
// Run both filters on copies and crossfade.
const float cutoff1 = p.f1On ? Filter::getCutoffHz (p.f1Cutoff, p.f1Key, noteHz) : 0.0f;
const float cutoff2 = p.f2On ? Filter::getCutoffHz (p.f2Cutoff, p.f2Key, noteHz) : 0.0f;
float f1l = 0.0f, f1r = 0.0f, f2l = 0.0f, f2r = 0.0f;
for (int i = 0; i < numSamples; ++i)
{
f1l = l[i]; f1r = r[i];
f2l = l[i]; f2r = r[i];
if (p.f1On)
{
f1l = f1L.processSample (f1l, cutoff1, p.f1Res, p.f1Drive, p.f1Type, p.f1Slope);
f1r = f1R.processSample (f1r, cutoff1, p.f1Res, p.f1Drive, p.f1Type, p.f1Slope);
}
if (p.f2On)
{
f2l = f2L.processSample (f2l, cutoff2, p.f2Res, p.f2Drive, p.f2Type, p.f2Slope);
f2r = f2R.processSample (f2r, cutoff2, p.f2Res, p.f2Drive, p.f2Type, p.f2Slope);
}
const float m = p.mix;
l[i] = f1l * (1.0f - m) + f2l * m;
r[i] = f1r * (1.0f - m) + f2r * m;
}
}
else // Split: filter 1 -> left, filter 2 -> right
{
applySlot (f1L, l, numSamples, p.f1On, p.f1Type, p.f1Cutoff, p.f1Res, p.f1Drive, p.f1Key, p.f1Slope, noteHz);
applySlot (f2R, r, numSamples, p.f2On, p.f2Type, p.f2Cutoff, p.f2Res, p.f2Drive, p.f2Key, p.f2Slope, noteHz);
}
// Post-filter output level. Skip the multiply only at exact unity so the
// bypass never changes the rendered samples.
if (! exactly (p.out, 1.0f))
{
for (int i = 0; i < numSamples; ++i)
{
l[i] *= p.out;
r[i] *= p.out;
}
}
}
} // namespace serum