chore: land janitor round 1 and graft integration

Round 1 findings and fixes, all previously uncommitted:
- FXProcessor per-slot DSP history repair (HEAD does not compile without it)
- SynthVoice::getModulationValue, editor control-lock guard
- ExactCompare.h naming the deliberate exact float comparisons
- KnobLookAndFeel replacing the never-installed SerumLookAndFeel
- 55 first-party compiler warnings to 0
- AGENT.md renamed to AGENTS.md across Source/
- graft repository context graph and its hooks
- JANITOR_TASKS.md and UI_UX_AUDIT.md records
This commit is contained in:
biggy committed 2026-10-06 08:50:45 +02:00
1 parent 5c942cad0c
commit e4cea300db
63 files changed
+2360 -805

No files matched your search

+23 -32
View File
@@ -1,4 +1,5 @@
#include "Filter.h"
#include "ExactCompare.h"
namespace serum
{
@@ -18,14 +19,13 @@ namespace
}
}
void Filter::prepare (double sampleRate, int maxBlockSize)
void Filter::prepare (double sampleRate)
{
sr = sampleRate;
combLine.assign ((size_t) sampleRate, 0.0f); // 1 second of delay
combWrite = 0;
combDamp = 0.0;
reset();
(void) maxBlockSize;
}
void Filter::reset()
@@ -38,14 +38,22 @@ void Filter::reset()
a1 = a2 = a3 = 0.0;
stage.fill (0.0);
for (auto& s : formantState) s.fill (0.0);
std::fill (combLine.begin(), combLine.end(), 0.0f);
// combLine is one second of samples per filter, and a preset change now
// resets on the audio thread. It only needs clearing when the comb has
// actually written to it.
if (combDirty)
{
std::fill (combLine.begin(), combLine.end(), 0.0f);
combDirty = false;
}
combWrite = 0;
combDamp = 0.0;
}
void Filter::updateCoefficients (float cutoffHz, float res, int type) noexcept
{
if (cutoffHz == lastCutoffHz && res == lastRes && type == lastType)
// Cache key: rebuild coefficients only when a parameter actually changes.
if (! changed (cutoffHz, lastCutoffHz) && ! changed (res, lastRes) && type == lastType)
return;
lastCutoffHz = cutoffHz;
lastRes = res;
@@ -89,6 +97,8 @@ void Filter::updateCoefficients (float cutoffHz, float res, int type) noexcept
updateSvf (g, k);
break;
}
case FilterModel::Comb:
case FilterModel::Count:
default:
break;
}
@@ -96,7 +106,8 @@ void Filter::updateCoefficients (float cutoffHz, float res, int type) noexcept
void Filter::updateSvf (double g, double k) noexcept
{
if (g == lastG && k == lastK)
// Cache key: the SVF coefficients only change with g and k.
if (! changed (g, lastG) && ! changed (k, lastK))
return;
lastG = g;
lastK = k;
@@ -105,17 +116,6 @@ void Filter::updateSvf (double g, double k) noexcept
a3 = g * a2;
}
double Filter::svfLow (double in, double g, double k) noexcept
{
updateSvf (g, k);
const double v3 = in - ic2eq;
const double v1 = a1 * ic1eq + a2 * v3;
const double v2 = ic2eq + a2 * ic1eq + a3 * v3;
ic1eq = 2.0 * v1 - ic1eq;
ic2eq = 2.0 * v2 - ic2eq;
return v2;
}
double Filter::svfBand (double in, double g, double k) noexcept
{
updateSvf (g, k);
@@ -127,17 +127,6 @@ double Filter::svfBand (double in, double g, double k) noexcept
return v1;
}
double Filter::svfHigh (double in, double g, double k) noexcept
{
updateSvf (g, k);
const double v3 = in - ic2eq;
const double v1 = a1 * ic1eq + a2 * v3;
const double v2 = ic2eq + a2 * ic1eq + a3 * v3;
ic1eq = 2.0 * v1 - ic1eq;
ic2eq = 2.0 * v2 - ic2eq;
return in - k * v1 - v2;
}
double Filter::ladder (double in, double g, double res, double drive, int stages, bool diode) noexcept
{
stages = juce::jlimit (1, 4, stages);
@@ -193,6 +182,7 @@ double Filter::comb (double in, double freqHz, double res, double drive) noexcep
const float feedback = clampF ((float) res * 0.9f, 0.0f, 0.98f);
combLine[(size_t) combWrite] = (float) in + (float) combDamp * feedback;
combWrite = (combWrite + 1) % len;
combDirty = true;
return (double) y;
}
@@ -204,12 +194,12 @@ double Filter::formant (double in) noexcept
for (int i = 0; i < 3; ++i)
{
const auto& c = formantCoefficients[(size_t) i];
const double a1 = c[0];
const double a2 = c[1];
const double a3 = c[2];
const double c0 = c[0];
const double c1 = c[1];
const double c2 = c[2];
const double v3 = in - formantState[(size_t) i][1];
const double v1 = a1 * formantState[(size_t) i][0] + a2 * v3;
const double v2 = formantState[(size_t) i][1] + a2 * formantState[(size_t) i][0] + a3 * v3;
const double v1 = c0 * formantState[(size_t) i][0] + c1 * v3;
const double v2 = formantState[(size_t) i][1] + c1 * formantState[(size_t) i][0] + c2 * v3;
formantState[(size_t) i][0] = 2.0 * v1 - formantState[(size_t) i][0];
formantState[(size_t) i][1] = 2.0 * v2 - formantState[(size_t) i][1];
out += v1 * gains[i];
@@ -269,6 +259,7 @@ float Filter::processSample (float in, float cutoffHz, float res, float drive, i
case FilterModel::Screamer:
out = screamer (in, drive);
break;
case FilterModel::Count:
default:
break;
}