#pragma once #include #include enum class FilterType { LowPass12 = 0, LowPass24, BandPass, HighPass, Notch, NumTypes }; class Filter { public: void prepare(double sr) { sampleRate = sr; reset(); } void reset() { for (int i = 0; i < 4; ++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, 50.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; 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::NumTypes: return input; } return lp; } private: static constexpr float pi = 3.14159265358979323846f; double sampleRate = 44100.0; float stage[4] = {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; };