feat: change to an SVF filter design

This commit is contained in:
Liam Kerr 2026-08-05 20:05:34 +01:00
parent 821f3d7d65
commit a80c6276a8
2 changed files with 106 additions and 53 deletions

View file

@ -1,49 +1,90 @@
#include "Filter.hpp" #include "Filter.hpp"
#include <cmath> #include <cmath>
Filter::Filter(float initCutOff, float initSampleRate) #ifndef M_PI
: cutOff_(initCutOff), feedback_(0.0f), alpha_(0.0f), #define M_PI 3.14159265358979323846
sampleRate_(initSampleRate), zL_(0.0f), zR_(0.0f) { #endif
recompute();
}; Filter::Filter(float initCutOff, float initQ, float initSampleRate)
: cutOff_(initCutOff), q_(initQ), sampleRate_(initSampleRate),
type_(kLowPass), g_(0.0f), lpL_(0.0f), bpL_(0.0f), lpR_(0.0f),
bpR_(0.0f) {
updateCoeffs();
}
Filter::FilterType Filter::getType() const { return type_; }
void Filter::updateCoeffs() {
float f = cutOff_ / std::fmax(sampleRate_, 1.0f);
if (f > kMaxNormalizedFrequency)
f = kMaxNormalizedFrequency;
g_ = 2.0f * std::sin(M_PI * f);
}
float Filter::processFrame(float input, float &lp, float &bp) const {
float hp = input - (bp / q_) - lp;
bp = bp + g_ * hp;
lp = lp + g_ * bp;
switch (type_) {
case kHighPass:
return hp;
case kBandPass:
return bp;
case kNotch:
return input - lp;
case kLowPass:
default:
return lp;
}
}
void Filter::process(const float *in, float *out, uint32_t frames) { void Filter::process(const float *in, float *out, uint32_t frames) {
filter(in, out, frames, &zL_); float lp = lpL_;
}; float bp = bpL_;
void Filter::processStereo(const float *inLeft, const float *inRight,
float *outLeft, float *outRight, uint32_t frames) {
filter(inLeft, outLeft, frames, &zL_);
filter(inRight, outRight, frames, &zR_);
};
void Filter::filter(const float *in, float *out, uint32_t frames, float *z) {
for (uint32_t i = 0; i < frames; ++i) { for (uint32_t i = 0; i < frames; ++i) {
float feedbackInput = in[i] + (feedback_ * (*z)); out[i] = processFrame(in[i], lp, bp);
*z = *z + alpha_ * (feedbackInput - *z);
out[i] = *z;
}
} }
void Filter::recompute() { lpL_ = lp;
float fc = std::fmax(cutOff_, 1.0f); bpL_ = bp;
float rc = 1.0f / (2.0f * M_PI * fc);
float val = 1.0f - std::exp(-1.0f / (rc * sampleRate_));
alpha_ = std::fmin(val, 0.999f);
};
void Filter::setCutOff(float newCutOff) {
cutOff_ = newCutOff;
recompute();
} }
void Filter::processStereo(const float *inL, const float *inR, float *outL,
float *outR, uint32_t frames) {
float lpL = lpL_, bpL = bpL_;
float lpR = lpR_, bpR = bpR_;
for (uint32_t i = 0; i < frames; ++i) {
outL[i] = processFrame(inL[i], lpL, bpL);
outR[i] = processFrame(inR[i], lpR, bpR);
}
lpL_ = lpL;
bpL_ = bpL;
lpR_ = lpR;
bpR_ = bpR;
}
float Filter::getCutOff() const { return cutOff_; } float Filter::getCutOff() const { return cutOff_; }
void Filter::setCutOff(float freq) {
void Filter::setFeedback(float newFeedback) { cutOff_ = std::fmax(freq, 1.0f);
feedback_ = std::fmin(std::fmax(newFeedback, 0.0f), 0.99f); updateCoeffs();
} }
float Filter::getFeedback() const { return feedback_; }
void Filter::setSampleRate(float newSampleRate) { float Filter::getQ() const { return q_; }
sampleRate_ = newSampleRate; void Filter::setQ(float q) {
recompute(); q_ = std::fmax(q, 0.1f);
updateCoeffs();
} }
void Filter::setType(FilterType type) { type_ = type; }
float Filter::getSampleRate() const { return sampleRate_; } float Filter::getSampleRate() const { return sampleRate_; }
void Filter::setSampleRate(float sr) {
sampleRate_ = sr;
updateCoeffs();
}

View file

@ -1,33 +1,45 @@
#ifndef FILTER_HPP_INCLUDED #ifndef FILTER_HPP_INCLUDED
#define FILTER_HPP_INCLUDED #define FILTER_HPP_INCLUDED
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include <cstdint> #include <cstdint>
constexpr float kMaxNormalizedFrequency = 0.16667f;
class Filter { class Filter {
public: public:
Filter(float initCutOff = 1.0f, float initSampleRate = 48000.0f); Filter(float initCutOff = 1000.0f, float initQ = 0.707f,
void process(const float *in, float *out, uint32_t frames); float initSampleRate = 48000.0f);
void processStereo(const float *inLeft, const float *inRight, float *outLeft,
float *outRight, uint32_t frames); enum FilterType { kLowPass, kHighPass, kBandPass, kNotch, kFilterTypeCount };
void setCutOff(float freq);
void setQ(float q);
void setSampleRate(float sr);
void setType(FilterType type);
void recompute();
void setCutOff(float newCutOff);
float getCutOff() const; float getCutOff() const;
void setFeedback(float newFeedback); float getQ() const;
float getFeedback() const;
void setSampleRate(float newSampleRate);
float getSampleRate() const; float getSampleRate() const;
FilterType getType() const;
void process(const float *in, float *out, uint32_t frames);
void processStereo(const float *inL, const float *inR, float *outL,
float *outR, uint32_t frames);
private: private:
void filter(const float *in, float *out, uint32_t frames, float *z); void updateCoeffs();
float processFrame(float input, float &lp, float &bp) const;
float cutOff_; float cutOff_;
float feedback_; float q_;
float alpha_;
float sampleRate_; float sampleRate_;
float zL_; FilterType type_;
float zR_;
float g_;
float lpL_, bpL_;
float lpR_, bpR_;
}; };
#endif // FILTER_HPP_INCLUDED #endif