feat: add basic lp filter

This commit is contained in:
Liam Kerr 2026-08-05 13:46:12 +01:00
parent 2bd91c7d8e
commit 75843a7309
4 changed files with 134 additions and 9 deletions

49
core/Filter.cpp Normal file
View file

@ -0,0 +1,49 @@
#include "Filter.hpp"
#include <cmath>
Filter::Filter(float initCutOff, float initSampleRate)
: cutOff_(initCutOff), feedback_(0.0f), alpha_(0.0f),
sampleRate_(initSampleRate), zL_(0.0f), zR_(0.0f) {
recompute();
};
void Filter::process(const float *in, float *out, uint32_t frames) {
filter(in, out, frames, &zL_);
};
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) {
float feedbackInput = in[i] + (feedback_ * (*z));
*z = *z + alpha_ * (feedbackInput - *z);
out[i] = *z;
}
}
void Filter::recompute() {
float fc = std::fmax(cutOff_, 1.0f);
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();
}
float Filter::getCutOff() const { return cutOff_; }
void Filter::setFeedback(float newFeedback) {
feedback_ = std::fmin(std::fmax(newFeedback, 0.0f), 0.99f);
}
float Filter::getFeedback() const { return feedback_; }
void Filter::setSampleRate(float newSampleRate) {
sampleRate_ = newSampleRate;
recompute();
}
float Filter::getSampleRate() const { return sampleRate_; }

33
core/Filter.hpp Normal file
View file

@ -0,0 +1,33 @@
#ifndef FILTER_HPP_INCLUDED
#define FILTER_HPP_INCLUDED
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include <cstdint>
class Filter {
public:
Filter(float initCutOff = 1.0f, float initSampleRate = 48000.0f);
void process(const float *in, float *out, uint32_t frames);
void processStereo(const float *inLeft, const float *inRight, float *outLeft,
float *outRight, uint32_t frames);
void recompute();
void setCutOff(float newCutOff);
float getCutOff() const;
void setFeedback(float newFeedback);
float getFeedback() const;
void setSampleRate(float newSampleRate);
float getSampleRate() const;
private:
void filter(const float *in, float *out, uint32_t frames, float *z);
float cutOff_;
float feedback_;
float alpha_;
float sampleRate_;
float zL_;
float zR_;
};
#endif // FILTER_HPP_INCLUDED

View file

@ -1,7 +1,7 @@
#!/usr/bin/make -f
NAME = SquidgeDistortion
FILES_DSP = SquidgeDistortion.cpp ../../core/Gain.cpp ../../core/Clipper.cpp ../../core/Mixer.cpp
FILES_DSP = SquidgeDistortion.cpp ../../core/Gain.cpp ../../core/Clipper.cpp ../../core/Mixer.cpp ../../core/Filter.cpp
TARGETS = vst3 clap

View file

@ -3,6 +3,7 @@
#include "DistrhoPluginInfo.h"
#include "../../core/Clipper.hpp"
#include "../../core/Filter.hpp"
#include "../../core/Gain.hpp"
#include "../../core/Mixer.hpp"
@ -10,6 +11,13 @@ START_NAMESPACE_DISTRHO
const int BUFFER_SIZE = 16384;
struct AudioBuffers {
float clippedL[BUFFER_SIZE];
float clippedR[BUFFER_SIZE];
float lpL[BUFFER_SIZE];
float lpR[BUFFER_SIZE];
};
class SquidgeDistortion : public Plugin {
public:
@ -17,11 +25,13 @@ public:
enum Parameters {
kParamGain,
kParamClipMode,
kParamFilter,
kParamFilterFeedback,
kParamMixVolume,
kParameterCount // must be last
};
SquidgeDistortion()
: Plugin(kParameterCount, 0, 0), gain_(2.0f), mixer_(1.0f) {
: Plugin(kParameterCount, 0, 0), gain_(2.0f), mixer_(1.0f), filter_(440) {
mixer_.setInputGain(kChannelDry, 0.0f);
mixer_.setInputGain(kChannelWet, 1.0f);
}
@ -62,6 +72,22 @@ protected:
parameter.enumValues.values =
const_cast<ParameterEnumerationValue *>(sClipModes);
break;
case kParamFilter:
parameter.name = "Filter";
parameter.symbol = "filter";
parameter.ranges.min = 20.0f;
parameter.ranges.def = 1000.0f;
parameter.ranges.max = 10000.0f;
parameter.hints = kParameterIsAutomatable;
break;
case kParamFilterFeedback:
parameter.name = "Filter Feedback";
parameter.symbol = "filterfeedback";
parameter.ranges.min = 0.0f;
parameter.ranges.def = .1f;
parameter.ranges.max = 0.99f;
parameter.hints = kParameterIsAutomatable;
break;
case kParamMixVolume:
parameter.name = "Dry/Wet";
parameter.symbol = "drywetvolume";
@ -79,6 +105,10 @@ protected:
return gain_.getGain();
case kParamClipMode:
return (float)clipper_.getMode();
case kParamFilter:
return (float)filter_.getCutOff();
case kParamFilterFeedback:
return (float)filter_.getFeedback();
case kParamMixVolume:
return mixer_.getInputGain(kChannelWet);
}
@ -93,6 +123,12 @@ protected:
case kParamClipMode:
clipper_.setMode((Clipper::ClipperMode)value);
break;
case kParamFilter:
filter_.setCutOff(value);
break;
case kParamFilterFeedback:
filter_.setFeedback(value);
break;
case kParamMixVolume:
mixer_.setInputGain(kChannelWet, value);
mixer_.setInputGain(kChannelDry, 1.0f - value);
@ -108,22 +144,29 @@ protected:
}
return;
}
gain_.processStereo(inputs[0], inputs[1], wetBufferL_, wetBufferR_, frames);
clipper_.processStereo(wetBufferL_, wetBufferR_, wetBufferL_, wetBufferR_,
frames);
gain_.processStereo(inputs[0], inputs[1], buffers_.clippedL,
buffers_.clippedR, frames);
clipper_.processStereo(buffers_.clippedL, buffers_.clippedR,
buffers_.clippedL, buffers_.clippedR, frames);
filter_.processStereo(buffers_.clippedL, buffers_.clippedR, buffers_.lpL,
buffers_.lpR, frames);
const float *inputsL[2] = {inputs[0], wetBufferL_};
const float *inputsR[2] = {inputs[1], wetBufferR_};
const float *inputsL[2] = {inputs[0], buffers_.lpL};
const float *inputsR[2] = {inputs[1], buffers_.lpR};
mixer_.processStereo(inputsL, inputsR, outputs[0], outputs[1], frames);
}
void sampleRateChanged(double newSr) override {
filter_.setSampleRate(static_cast<float>(newSr));
}
private:
Gain gain_;
Clipper clipper_;
Mixer<2> mixer_;
float wetBufferL_[BUFFER_SIZE];
float wetBufferR_[BUFFER_SIZE];
Filter filter_;
AudioBuffers buffers_;
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeDistortion);
};