From 467142603d1b98ec998ea7339a57567b38c16a2b Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 5 Aug 2026 18:06:44 +0100 Subject: [PATCH] feat: add resonance parameter to filter and update plugin info --- core/Filter.cpp | 50 +++++++++++++------ core/Filter.hpp | 20 ++++++-- plugins/SquidgeDistortion/DistrhoPluginInfo.h | 2 +- .../SquidgeDistortion/SquidgeDistortion.cpp | 20 ++++++-- 4 files changed, 70 insertions(+), 22 deletions(-) diff --git a/core/Filter.cpp b/core/Filter.cpp index e4f5551..65b0589 100644 --- a/core/Filter.cpp +++ b/core/Filter.cpp @@ -2,33 +2,49 @@ #include Filter::Filter(float initCutOff, float initSampleRate) - : cutOff_(initCutOff), feedback_(0.0f), alpha_(0.0f), - sampleRate_(initSampleRate), zL_(0.0f), zR_(0.0f) { + : cutOff_(initCutOff), feedback_(0.0f), resonance_(0.1f), a1_(0.0f), + a2_(0.0f), a3_(0.0f), g_(0.0f), k_(2.0f), sampleRate_(initSampleRate), + s1L_(0.0f), s2L_(0.0f), s1R_(0.0f), s2R_(0.0f), outZL_(0.0f), + outZR_(0.0f) { recompute(); }; void Filter::process(const float *in, float *out, uint32_t frames) { - filter(in, out, frames, &zL_); + filter(in, out, frames, &s1L_, &s2L_, &outZL_); }; 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_); + filter(inLeft, outLeft, frames, &s1L_, &s2L_, &outZL_); + filter(inRight, outRight, frames, &s1R_, &s2R_, &outZR_); }; -void Filter::filter(const float *in, float *out, uint32_t frames, float *z) { +void Filter::filter(const float *in, float *out, uint32_t frames, float *s1, + float *s2, float *outZ) { for (uint32_t i = 0; i < frames; ++i) { - float feedbackInput = in[i] + (feedback_ * (*z)); - *z = *z + alpha_ * (feedbackInput - *z); - out[i] = *z; + float x = in[i] + (feedback_ * (*outZ)); + float v3 = x - *s2; + if (resonance_ > 0.995f && std::fabs(v3) < 1.0e-9f) { + v3 = 1.0e-6f; + } + float v1 = (a1_ * (*s1)) + (a2_ * v3); + float v2 = (*s2) + (a2_ * (*s1)) + (a3_ * v3); + *s1 = (2.0f * v1) - *s1; + *s2 = (2.0f * v2) - *s2; + out[i] = v2; + *outZ = v2; } } 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); + float sr = std::fmax(sampleRate_, 1.0f); + float fc = std::fmin(std::fmax(cutOff_, 1.0f), 0.45f * sr); + g_ = std::tan(static_cast(M_PI) * (fc / sr)); + k_ = 2.0f * (1.0f - std::fmin(std::fmax(resonance_, 0.0f), 1.0f)); + + float denom = 1.0f + (g_ * (g_ + k_)); + a1_ = 1.0f / denom; + a2_ = g_ * a1_; + a3_ = g_ * a2_; }; void Filter::setCutOff(float newCutOff) { @@ -38,10 +54,16 @@ void Filter::setCutOff(float newCutOff) { float Filter::getCutOff() const { return cutOff_; } void Filter::setFeedback(float newFeedback) { - feedback_ = std::fmin(std::fmax(newFeedback, 0.0f), 0.99f); + feedback_ = std::fmin(std::fmax(newFeedback, 0.0f), 0.95f); } float Filter::getFeedback() const { return feedback_; } +void Filter::setResonance(float newResonance) { + resonance_ = std::fmin(std::fmax(newResonance, 0.0f), 1.0f); + recompute(); +} +float Filter::getResonance() const { return resonance_; } + void Filter::setSampleRate(float newSampleRate) { sampleRate_ = newSampleRate; recompute(); diff --git a/core/Filter.hpp b/core/Filter.hpp index 4b71f9c..d83f359 100644 --- a/core/Filter.hpp +++ b/core/Filter.hpp @@ -17,17 +17,29 @@ public: float getCutOff() const; void setFeedback(float newFeedback); float getFeedback() const; + void setResonance(float newResonance); + float getResonance() const; void setSampleRate(float newSampleRate); float getSampleRate() const; private: - void filter(const float *in, float *out, uint32_t frames, float *z); + void filter(const float *in, float *out, uint32_t frames, float *s1, + float *s2, float *outZ); float cutOff_; float feedback_; - float alpha_; + float resonance_; + float a1_; + float a2_; + float a3_; + float g_; + float k_; float sampleRate_; - float zL_; - float zR_; + float s1L_; + float s2L_; + float s1R_; + float s2R_; + float outZL_; + float outZR_; }; #endif // FILTER_HPP_INCLUDED \ No newline at end of file diff --git a/plugins/SquidgeDistortion/DistrhoPluginInfo.h b/plugins/SquidgeDistortion/DistrhoPluginInfo.h index 627f348..488bce6 100644 --- a/plugins/SquidgeDistortion/DistrhoPluginInfo.h +++ b/plugins/SquidgeDistortion/DistrhoPluginInfo.h @@ -9,7 +9,7 @@ #define DISTRHO_PLUGIN_NUM_INPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 -#define DISTRHO_PLUGIN_NUM_PARAMETERS 5 +#define DISTRHO_PLUGIN_NUM_PARAMETERS 6 #define DISTRHO_PLUGIN_WANT_PROGRAMS 0 #define DISTRHO_PLUGIN_WANT_STATE 0 diff --git a/plugins/SquidgeDistortion/SquidgeDistortion.cpp b/plugins/SquidgeDistortion/SquidgeDistortion.cpp index 4cf4ab5..8356f69 100644 --- a/plugins/SquidgeDistortion/SquidgeDistortion.cpp +++ b/plugins/SquidgeDistortion/SquidgeDistortion.cpp @@ -30,6 +30,7 @@ public: kParamFilter, kParamFilterFeedback, kParamMixVolume, + kParamResonance, kParameterCount // must be last }; SquidgeDistortion() @@ -83,11 +84,11 @@ protected: parameter.hints = kParameterIsAutomatable; break; case kParamFilterFeedback: - parameter.name = "Filter Feedback"; + parameter.name = "Feedback"; parameter.symbol = "filterfeedback"; parameter.ranges.min = 0.0f; - parameter.ranges.def = .1f; - parameter.ranges.max = 0.99f; + parameter.ranges.def = 0.0f; + parameter.ranges.max = 0.95f; parameter.hints = kParameterIsAutomatable; break; case kParamMixVolume: @@ -98,6 +99,14 @@ protected: parameter.ranges.max = 1.0f; parameter.hints = kParameterIsAutomatable; break; + case kParamResonance: + parameter.name = "Resonance"; + parameter.symbol = "resonance"; + parameter.ranges.min = 0.0f; + parameter.ranges.def = 0.1f; + parameter.ranges.max = 1.0f; + parameter.hints = kParameterIsAutomatable; + break; } } @@ -113,6 +122,8 @@ protected: return (float)filter_.getFeedback(); case kParamMixVolume: return mixer_.getInputGain(kChannelWet); + case kParamResonance: + return (float)filter_.getResonance(); } return 0.0f; } @@ -137,6 +148,9 @@ protected: mixer_.setInputGain(kChannelWet, value); mixer_.setInputGain(kChannelDry, 1.0f - value); break; + case kParamResonance: + filter_.setResonance(value); + break; } }