#pragma once #include #include enum class FilterType { LowPass12 = 0, LowPass24, BandPass, HighPass, Notch, BandPass24, HighPass24, Notch24, LowPass48, NumTypes }; class Filter { public: void prepare(double sr) { sampleRate = sr; reset(); } void reset() { for (int i = 0; i < 8; ++i) stage[i] = 0.0f; gCoeff = 0.0f; gTarget = 0.0f; kCoeff = 2.0f; kTarget = 2.0f; } void setCoefficients(float cutoff, float res, FilterType type) { cutoff = std::clamp(cutoff, 20.0f, static_cast(sampleRate * 0.49)); resonance = std::clamp(res, 0.0f, 1.0f); filterType = type; gTarget = std::tan(static_cast(pi) * cutoff / static_cast(sampleRate)); kTarget = 2.0f - 2.0f * resonance; } float process(float input) { // Per-sample coefficient smoothing (one-pole, ~0.002 = ~44 samples to ~63%) gCoeff += (gTarget - gCoeff) * 0.002f; kCoeff += (kTarget - kCoeff) * 0.002f; float g = gCoeff; float k = kCoeff; float denom = 1.0f + k * g + g * g; float hp = (input - (k + g) * stage[0] - stage[1]) / denom; float bp = g * hp + stage[0]; float lp = g * bp + stage[1]; stage[0] = g * hp + bp; stage[1] = g * bp + lp; float hp2 = (lp - (k + g) * stage[2] - stage[3]) / denom; float bp2 = g * hp2 + stage[2]; float lp2 = g * bp2 + stage[3]; stage[2] = g * hp2 + bp2; stage[3] = g * bp2 + lp2; // Stage 3 (fed by the stage-2 lowpass) — 36 dB float hp3 = (lp2 - (k + g) * stage[4] - stage[5]) / denom; float bp3 = g * hp3 + stage[4]; float lp3 = g * bp3 + stage[5]; stage[4] = g * hp3 + bp3; stage[5] = g * bp3 + lp3; // Stage 4 (fed by the stage-3 lowpass) — 48 dB float hp4 = (lp3 - (k + g) * stage[6] - stage[7]) / denom; float bp4 = g * hp4 + stage[6]; float lp4 = g * bp4 + stage[7]; stage[6] = g * hp4 + bp4; stage[7] = g * bp4 + lp4; switch (filterType) { case FilterType::LowPass12: return lp; case FilterType::LowPass24: return lp2; case FilterType::BandPass: return bp; case FilterType::HighPass: return hp; case FilterType::Notch: return input - bp; case FilterType::BandPass24: return bp2; case FilterType::HighPass24: return hp2; case FilterType::Notch24: return input - bp2; case FilterType::LowPass48: return lp4; case FilterType::NumTypes: return input; } return lp; } private: static constexpr float pi = 3.14159265358979323846f; double sampleRate = 44100.0; float stage[8] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; float gCoeff = 0.0f, gTarget = 0.0f; float kCoeff = 2.0f, kTarget = 2.0f; float resonance = 0.0f; FilterType filterType = FilterType::LowPass12; };