From 75843a7309555f1e9af969ad34df513b98fc7ed2 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 5 Aug 2026 13:46:12 +0100 Subject: [PATCH] feat: add basic lp filter --- core/Filter.cpp | 49 +++++++++++++++ core/Filter.hpp | 33 +++++++++++ plugins/SquidgeDistortion/Makefile | 2 +- .../SquidgeDistortion/SquidgeDistortion.cpp | 59 ++++++++++++++++--- 4 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 core/Filter.cpp create mode 100644 core/Filter.hpp diff --git a/core/Filter.cpp b/core/Filter.cpp new file mode 100644 index 0000000..e4f5551 --- /dev/null +++ b/core/Filter.cpp @@ -0,0 +1,49 @@ +#include "Filter.hpp" +#include + +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_; } \ No newline at end of file diff --git a/core/Filter.hpp b/core/Filter.hpp new file mode 100644 index 0000000..4b71f9c --- /dev/null +++ b/core/Filter.hpp @@ -0,0 +1,33 @@ +#ifndef FILTER_HPP_INCLUDED +#define FILTER_HPP_INCLUDED +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#include + +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 \ No newline at end of file diff --git a/plugins/SquidgeDistortion/Makefile b/plugins/SquidgeDistortion/Makefile index 9b08a97..dab2d7e 100644 --- a/plugins/SquidgeDistortion/Makefile +++ b/plugins/SquidgeDistortion/Makefile @@ -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 diff --git a/plugins/SquidgeDistortion/SquidgeDistortion.cpp b/plugins/SquidgeDistortion/SquidgeDistortion.cpp index 6dfa417..117c878 100644 --- a/plugins/SquidgeDistortion/SquidgeDistortion.cpp +++ b/plugins/SquidgeDistortion/SquidgeDistortion.cpp @@ -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(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(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); };