From 467142603d1b98ec998ea7339a57567b38c16a2b Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 5 Aug 2026 18:06:44 +0100 Subject: [PATCH 1/8] 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; } } From 821f3d7d657536d364ca9399b28f5ed4cbde39a6 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 5 Aug 2026 18:54:51 +0100 Subject: [PATCH 2/8] feat: remove filter from distortion (filter will be it's own plugin) --- plugins/SquidgeDistortion/Makefile | 2 +- .../SquidgeDistortion/SquidgeDistortion.cpp | 42 ++----------------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/plugins/SquidgeDistortion/Makefile b/plugins/SquidgeDistortion/Makefile index dab2d7e..9b08a97 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 ../../core/Filter.cpp +FILES_DSP = SquidgeDistortion.cpp ../../core/Gain.cpp ../../core/Clipper.cpp ../../core/Mixer.cpp TARGETS = vst3 clap diff --git a/plugins/SquidgeDistortion/SquidgeDistortion.cpp b/plugins/SquidgeDistortion/SquidgeDistortion.cpp index 4cf4ab5..44d4ecb 100644 --- a/plugins/SquidgeDistortion/SquidgeDistortion.cpp +++ b/plugins/SquidgeDistortion/SquidgeDistortion.cpp @@ -5,7 +5,6 @@ #include #include "../../core/Clipper.hpp" -#include "../../core/Filter.hpp" #include "../../core/Gain.hpp" #include "../../core/Mixer.hpp" @@ -27,13 +26,11 @@ public: enum Parameters { kParamGain, kParamClipMode, - kParamFilter, - kParamFilterFeedback, kParamMixVolume, kParameterCount // must be last }; SquidgeDistortion() - : Plugin(kParameterCount, 0, 0), gain_(2.0f), mixer_(1.0f), filter_(440) { + : Plugin(kParameterCount, 0, 0), gain_(2.0f), mixer_(1.0f) { mixer_.setInputGain(kChannelDry, 0.0f); mixer_.setInputGain(kChannelWet, 1.0f); } @@ -74,22 +71,6 @@ 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"; @@ -107,10 +88,6 @@ 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); } @@ -127,12 +104,6 @@ protected: static_cast(Clipper::kClipperModeCount - 1)); clipper_.setMode(static_cast(std::lround(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); @@ -152,24 +123,17 @@ protected: 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], buffers_.lpL}; - const float *inputsR[2] = {inputs[1], buffers_.lpR}; + const float *inputsL[2] = {inputs[0], buffers_.clippedL}; + const float *inputsR[2] = {inputs[1], buffers_.clippedR}; 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_; - Filter filter_; AudioBuffers buffers_; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeDistortion); From a80c6276a8f7549c31c4c40787555518e461c5bf Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 5 Aug 2026 20:05:34 +0100 Subject: [PATCH 3/8] feat: change to an SVF filter design --- core/Filter.cpp | 111 +++++++++++++++++++++++++++++++++--------------- core/Filter.hpp | 48 +++++++++++++-------- 2 files changed, 106 insertions(+), 53 deletions(-) diff --git a/core/Filter.cpp b/core/Filter.cpp index e4f5551..86efe2d 100644 --- a/core/Filter.cpp +++ b/core/Filter.cpp @@ -1,49 +1,90 @@ #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(); -}; +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif -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_); -}; +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(); +} -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; +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::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::process(const float *in, float *out, uint32_t frames) { + float lp = lpL_; + float bp = bpL_; -void Filter::setCutOff(float newCutOff) { - cutOff_ = newCutOff; - recompute(); + for (uint32_t i = 0; i < frames; ++i) { + out[i] = processFrame(in[i], lp, bp); + } + + lpL_ = lp; + bpL_ = bp; } + +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_; } - -void Filter::setFeedback(float newFeedback) { - feedback_ = std::fmin(std::fmax(newFeedback, 0.0f), 0.99f); +void Filter::setCutOff(float freq) { + cutOff_ = std::fmax(freq, 1.0f); + updateCoeffs(); } -float Filter::getFeedback() const { return feedback_; } -void Filter::setSampleRate(float newSampleRate) { - sampleRate_ = newSampleRate; - recompute(); +float Filter::getQ() const { return q_; } +void Filter::setQ(float q) { + q_ = std::fmax(q, 0.1f); + updateCoeffs(); +} + +void Filter::setType(FilterType type) { type_ = type; } + +float Filter::getSampleRate() const { return sampleRate_; } +void Filter::setSampleRate(float sr) { + sampleRate_ = sr; + updateCoeffs(); } -float Filter::getSampleRate() const { return sampleRate_; } \ No newline at end of file diff --git a/core/Filter.hpp b/core/Filter.hpp index 4b71f9c..63420e0 100644 --- a/core/Filter.hpp +++ b/core/Filter.hpp @@ -1,33 +1,45 @@ #ifndef FILTER_HPP_INCLUDED #define FILTER_HPP_INCLUDED -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif + #include +constexpr float kMaxNormalizedFrequency = 0.16667f; + 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); + Filter(float initCutOff = 1000.0f, float initQ = 0.707f, + float initSampleRate = 48000.0f); + + 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; - void setFeedback(float newFeedback); - float getFeedback() const; - void setSampleRate(float newSampleRate); + float getQ() 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: - 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 feedback_; - float alpha_; + float q_; float sampleRate_; - float zL_; - float zR_; + FilterType type_; + + float g_; + + float lpL_, bpL_; + float lpR_, bpR_; }; -#endif // FILTER_HPP_INCLUDED \ No newline at end of file +#endif \ No newline at end of file From 3c3fec9b903740272bd5404a15c7f90a63f06b43 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 5 Aug 2026 21:02:23 +0100 Subject: [PATCH 4/8] feat: adding filter plugin --- Makefile | 2 +- plugins/SquidgeFilter/DistrhoPluginInfo.h | 25 +++++ plugins/SquidgeFilter/Makefile | 11 ++ plugins/SquidgeFilter/SquidgeFilter.cpp | 120 ++++++++++++++++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 plugins/SquidgeFilter/DistrhoPluginInfo.h create mode 100644 plugins/SquidgeFilter/Makefile create mode 100644 plugins/SquidgeFilter/SquidgeFilter.cpp diff --git a/Makefile b/Makefile index 06e9521..ed9a92c 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # ------------------------------ # # Define your plugins here as space-separated names -PLUGINS = SquidgeDistortion +PLUGINS = SquidgeDistortion SquidgeFilter # -------------------------------------------------------------- diff --git a/plugins/SquidgeFilter/DistrhoPluginInfo.h b/plugins/SquidgeFilter/DistrhoPluginInfo.h new file mode 100644 index 0000000..4e6a319 --- /dev/null +++ b/plugins/SquidgeFilter/DistrhoPluginInfo.h @@ -0,0 +1,25 @@ +#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED +#define DISTRHO_PLUGIN_INFO_H_INCLUDED + +#define DISTRHO_PLUGIN_NAME "SquidgeFilter" +#define DISTRHO_PLUGIN_AUTHOR "SquidgeSoft" +#define DISTRHO_PLUGIN_URI "moe.liam.squidgesoft.SquidgeFilter" +#define DISTRHO_PLUGIN_CATEGORY "fx" + +#define DISTRHO_PLUGIN_NUM_INPUTS 2 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 + +#define DISTRHO_PLUGIN_NUM_PARAMETERS 3 + +#define DISTRHO_PLUGIN_WANT_PROGRAMS 0 +#define DISTRHO_PLUGIN_WANT_STATE 0 + +#define DISTRHO_PLUGIN_HAS_UI 0 +#define DISTRHO_PLUGIN_IS_SYNTH 0 + +#define DISTRHO_PLUGIN_PRIV_ID "moe.liam.squidgesoft.SquidgeFilter" +#define DISTRHO_PLUGIN_BRAND "SquidgeSoft" + +#define DISTRHO_PLUGIN_CLAP_ID "moe.liam.squidgesoft.SquidgeFilter" + +#endif \ No newline at end of file diff --git a/plugins/SquidgeFilter/Makefile b/plugins/SquidgeFilter/Makefile new file mode 100644 index 0000000..4d6640f --- /dev/null +++ b/plugins/SquidgeFilter/Makefile @@ -0,0 +1,11 @@ +#!/usr/bin/make -f +NAME = SquidgeFilter + +FILES_DSP = SquidgeFilter.cpp ../../core/Filter.cpp + +TARGETS = vst3 clap + +include ../../dpf/Makefile.plugins.mk + +all: $(TARGETS) + diff --git a/plugins/SquidgeFilter/SquidgeFilter.cpp b/plugins/SquidgeFilter/SquidgeFilter.cpp new file mode 100644 index 0000000..1339b3f --- /dev/null +++ b/plugins/SquidgeFilter/SquidgeFilter.cpp @@ -0,0 +1,120 @@ +#include "DistrhoDetails.hpp" +#include "DistrhoPlugin.hpp" +#include "DistrhoPluginInfo.h" + +#include "../../core/Filter.hpp" + +START_NAMESPACE_DISTRHO + +class SquidgeFilter : public Plugin { + +public: + enum Parameters { + kParamFreq, + kParamResonance, + kParamType, + kParameterCount // must be last + }; + + SquidgeFilter() // Note: Constructor name must match class name + : Plugin(kParameterCount, 0, 0), + filter_(1000.0f, 0.707f, 48000.0f) { // Default: 1kHz, Q=0.707 + filter_.setType(Filter::kLowPass); + } + +protected: + const char *getLabel() const override { return "SquidgeFilter"; } + const char *getDescription() const override { + return "Simple State Variable Filter."; + } + const char *getMaker() const override { return "SquidgeSoft"; } + const char *getLicense() const override { return "GPL3"; } + uint32_t getVersion() const override { return d_version(0, 1, 0); } + int64_t getUniqueId() const override { return d_cconst('S', 'Q', 'F', 'T'); } + + void initParameter(uint32_t index, Parameter ¶meter) override { + switch (index) { + case kParamFreq: + parameter.name = "Frequency"; + parameter.symbol = "frequency"; + parameter.ranges.min = 20.0f; + parameter.ranges.def = 1000.0f; + parameter.ranges.max = 20000.0f; + parameter.hints = kParameterIsAutomatable | kParameterIsLogarithmic; + break; + + case kParamResonance: + parameter.name = "Resonance"; + parameter.symbol = "resonance"; + parameter.ranges.min = 0.1f; + parameter.ranges.def = 0.707f; + parameter.ranges.max = 20.0f; + parameter.hints = kParameterIsAutomatable; + break; + + case kParamType: + parameter.name = "Filter Type"; + parameter.symbol = "type"; + parameter.ranges.min = 0.0f; + parameter.ranges.def = 0.0f; + parameter.ranges.max = Filter::kFilterTypeCount - 1; + parameter.hints = kParameterIsAutomatable | kParameterIsInteger; + parameter.enumValues.count = Filter::kFilterTypeCount; + parameter.enumValues.restrictedMode = true; + + static const ParameterEnumerationValue sTypes[Filter::kFilterTypeCount] = + {{0.0f, "Low Pass"}, + {1.0f, "High Pass"}, + {2.0f, "Band Pass"}, + {3.0f, "Notch"}}; + parameter.enumValues.values = + const_cast(sTypes); + break; + } + } + + float getParameterValue(uint32_t index) const override { + switch (index) { + case kParamFreq: + return filter_.getCutOff(); + case kParamResonance: + return filter_.getQ(); + case kParamType: + return static_cast(filter_.getType()); + } + return 0.0f; + } + + void setParameterValue(uint32_t index, float value) override { + switch (index) { + case kParamFreq: + filter_.setCutOff(value); + break; + case kParamResonance: + filter_.setQ(value); + break; + case kParamType: + // Clamp value to valid enum range + int typeIdx = static_cast(std::fmin(std::fmax(value, 0.0f), 3.0f)); + filter_.setType(static_cast(typeIdx)); + break; + } + } + + void run(const float **inputs, float **outputs, uint32_t frames) override { + filter_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames); + } + + void sampleRateChanged(double newSr) override { + filter_.setSampleRate(static_cast(newSr)); + } + +private: + Filter filter_; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter); +}; + +Plugin *createPlugin() { return new SquidgeFilter(); } + +END_NAMESPACE_DISTRHO \ No newline at end of file From fe594a5494a1ae84dddaf341792a1d618a6394d5 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Thu, 6 Aug 2026 00:03:43 +0100 Subject: [PATCH 5/8] feat(filter): Add modulation support and LFO integration --- core/Filter.cpp | 27 +++++++++++---- core/Filter.hpp | 23 ++++++++----- core/Oscillator.cpp | 44 +++++++++++++++++++++++++ core/Oscillator.hpp | 39 ++++++++++++++++++++++ plugins/SquidgeFilter/Makefile | 2 +- plugins/SquidgeFilter/SquidgeFilter.cpp | 16 ++++++--- 6 files changed, 131 insertions(+), 20 deletions(-) create mode 100644 core/Oscillator.cpp create mode 100644 core/Oscillator.hpp diff --git a/core/Filter.cpp b/core/Filter.cpp index 86efe2d..b325d4b 100644 --- a/core/Filter.cpp +++ b/core/Filter.cpp @@ -1,21 +1,22 @@ #include "Filter.hpp" #include +#include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif 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) { + : modulation_(0.0f), 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); + float f = (cutOff_ + modulation_) / std::fmax(sampleRate_, 1.0f); if (f > kMaxNormalizedFrequency) f = kMaxNormalizedFrequency; g_ = 2.0f * std::sin(M_PI * f); @@ -41,11 +42,14 @@ float Filter::processFrame(float input, float &lp, float &bp) const { } } -void Filter::process(const float *in, float *out, uint32_t frames) { +void Filter::process(const float *in, float *out, uint32_t frames, + const float *modulation) { float lp = lpL_; float bp = bpL_; for (uint32_t i = 0; i < frames; ++i) { + if (modulation != nullptr) + setModulation(modulation[i]); out[i] = processFrame(in[i], lp, bp); } @@ -54,11 +58,14 @@ void Filter::process(const float *in, float *out, uint32_t frames) { } void Filter::processStereo(const float *inL, const float *inR, float *outL, - float *outR, uint32_t frames) { + float *outR, uint32_t frames, + const float *modulation) { float lpL = lpL_, bpL = bpL_; float lpR = lpR_, bpR = bpR_; for (uint32_t i = 0; i < frames; ++i) { + if (modulation != nullptr) + setModulation(modulation[i]); outL[i] = processFrame(inL[i], lpL, bpL); outR[i] = processFrame(inR[i], lpR, bpR); } @@ -88,3 +95,11 @@ void Filter::setSampleRate(float sr) { sampleRate_ = sr; updateCoeffs(); } + +float Filter::getModulation() const { return modulation_; } +void Filter::setModulation(float mod) { + if (modulation_ == mod) + return; + modulation_ = mod; + updateCoeffs(); +} \ No newline at end of file diff --git a/core/Filter.hpp b/core/Filter.hpp index 63420e0..ca0a9ac 100644 --- a/core/Filter.hpp +++ b/core/Filter.hpp @@ -13,21 +13,26 @@ public: enum FilterType { kLowPass, kHighPass, kBandPass, kNotch, kFilterTypeCount }; void setCutOff(float freq); - void setQ(float q); - void setSampleRate(float sr); - void setType(FilterType type); - float getCutOff() const; - float getQ() const; - float getSampleRate() const; - FilterType getType() const; - void process(const float *in, float *out, uint32_t frames); + void setQ(float q); + float getQ() const; + void setSampleRate(float sr); + float getSampleRate() const; + void setType(FilterType type); + FilterType getType() const; + void setModulation(float mod); + float getModulation() const; + + void process(const float *in, float *out, uint32_t frames, + const float *modulation = nullptr); void processStereo(const float *inL, const float *inR, float *outL, - float *outR, uint32_t frames); + float *outR, uint32_t frames, + const float *modulation = nullptr); private: void updateCoeffs(); + float modulation_; float processFrame(float input, float &lp, float &bp) const; diff --git a/core/Oscillator.cpp b/core/Oscillator.cpp new file mode 100644 index 0000000..523616d --- /dev/null +++ b/core/Oscillator.cpp @@ -0,0 +1,44 @@ +#include "Oscillator.hpp" + +#include + +Oscillator::Oscillator(float initFreq) + : freq_(initFreq), gain_(100.0f), waveShape_(kWaveShapeSine), + sampleRate_(48000.f), phase_(0.0f), inc_(0.0f) { + recalc_(); +}; + +void Oscillator::process(float *out, uint32_t frames) { + for (uint32_t i = 0; i < frames; ++i) { + switch (waveShape_) { + default: + case kWaveShapeSine: + out[i] = std::sinf(phase_ * 2.0f * M_PI) * gain_; + break; + } + phase_ += inc_; + if (phase_ >= 1.0f) + phase_ -= 1.0f; + } +} + +void Oscillator::recalc_() { inc_ = freq_ / sampleRate_; } + +void Oscillator::setFrequency(float newFreq) { + freq_ = std::fmax(newFreq, 0.0f); +} +float Oscillator::getFrequency() const { return freq_; }; + +void Oscillator::setGain(float newGain) { + gain_ = newGain > 0 ? newGain : 1.0f; +} +float Oscillator::getGain() const { return gain_; }; + +void Oscillator::setSampleRate(float newSampleRate) { + sampleRate_ = newSampleRate > 0 ? newSampleRate : 48000.0f; + recalc_(); +} +float Oscillator::getSampleRate() const { return sampleRate_; } + +void Oscillator::setWaveShape(WaveShape shape) { waveShape_ = shape; } +Oscillator::WaveShape Oscillator::getWaveShape() const { return waveShape_; } \ No newline at end of file diff --git a/core/Oscillator.hpp b/core/Oscillator.hpp new file mode 100644 index 0000000..e246ee9 --- /dev/null +++ b/core/Oscillator.hpp @@ -0,0 +1,39 @@ +#ifndef OSCILLATOR_HPP_INCLUDED +#define OSCILLATOR_HPP_INCLUDED + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +class Oscillator { + enum WaveShape { kWaveShapeSine, kWaveShapeTriangle, kWaveShapeSquare }; + +public: + Oscillator(float initFreq); + void process(float *out, uint32_t frames); + + void setFrequency(float newFreq); + float getFrequency() const; + + void setGain(float newGain); + float getGain() const; + + void setSampleRate(float newSampleRate); + float getSampleRate() const; + + void setWaveShape(WaveShape shape); + WaveShape getWaveShape() const; + +private: + float freq_; + float gain_; + WaveShape waveShape_; + float sampleRate_; + float phase_; + float inc_; + void recalc_(); +}; + +#endif // OSCILLATOR_HPP_INCLUDED \ No newline at end of file diff --git a/plugins/SquidgeFilter/Makefile b/plugins/SquidgeFilter/Makefile index 4d6640f..9728c49 100644 --- a/plugins/SquidgeFilter/Makefile +++ b/plugins/SquidgeFilter/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/make -f NAME = SquidgeFilter -FILES_DSP = SquidgeFilter.cpp ../../core/Filter.cpp +FILES_DSP = SquidgeFilter.cpp ../../core/Filter.cpp ../../core/Oscillator.cpp TARGETS = vst3 clap diff --git a/plugins/SquidgeFilter/SquidgeFilter.cpp b/plugins/SquidgeFilter/SquidgeFilter.cpp index 1339b3f..2d06bac 100644 --- a/plugins/SquidgeFilter/SquidgeFilter.cpp +++ b/plugins/SquidgeFilter/SquidgeFilter.cpp @@ -3,9 +3,12 @@ #include "DistrhoPluginInfo.h" #include "../../core/Filter.hpp" +#include "../../core/Oscillator.hpp" START_NAMESPACE_DISTRHO +const int BUFFER_SIZE = 16384; + class SquidgeFilter : public Plugin { public: @@ -17,8 +20,8 @@ public: }; SquidgeFilter() // Note: Constructor name must match class name - : Plugin(kParameterCount, 0, 0), - filter_(1000.0f, 0.707f, 48000.0f) { // Default: 1kHz, Q=0.707 + : Plugin(kParameterCount, 0, 0), filter_(1000.0f, 0.707f, 48000.0f), + lfo_(4.0f) { // Default: 1kHz, Q=0.707 filter_.setType(Filter::kLowPass); } @@ -94,7 +97,6 @@ protected: filter_.setQ(value); break; case kParamType: - // Clamp value to valid enum range int typeIdx = static_cast(std::fmin(std::fmax(value, 0.0f), 3.0f)); filter_.setType(static_cast(typeIdx)); break; @@ -102,15 +104,21 @@ protected: } void run(const float **inputs, float **outputs, uint32_t frames) override { - filter_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames); + lfo_.process(lfoBuffer_, frames); + + filter_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames, + lfoBuffer_); } void sampleRateChanged(double newSr) override { filter_.setSampleRate(static_cast(newSr)); + lfo_.setSampleRate(newSr); } private: Filter filter_; + Oscillator lfo_; + float lfoBuffer_[BUFFER_SIZE]; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter); }; From d8c1c90f15b1840fa373c8848a63bd944b919ef2 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 12 Aug 2026 00:05:50 +0100 Subject: [PATCH 6/8] feat: adding lfo to filter --- core/Filter.cpp | 58 +++++++++++----- core/Filter.hpp | 5 ++ core/Oscillator.cpp | 2 + core/Oscillator.hpp | 2 + plugins/SquidgeFilter/DistrhoPluginInfo.h | 2 + plugins/SquidgeFilter/SquidgeFilter.cpp | 81 ++++++++++++++++++++++- 6 files changed, 130 insertions(+), 20 deletions(-) diff --git a/core/Filter.cpp b/core/Filter.cpp index b325d4b..328301a 100644 --- a/core/Filter.cpp +++ b/core/Filter.cpp @@ -1,6 +1,5 @@ #include "Filter.hpp" #include -#include #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -15,15 +14,24 @@ Filter::Filter(float initCutOff, float initQ, float initSampleRate) Filter::FilterType Filter::getType() const { return type_; } +float Filter::clampFreq(float freq) { + return std::fmin(std::fmax(freq, kFilterMin), kFilterMax); +} + void Filter::updateCoeffs() { - float f = (cutOff_ + modulation_) / std::fmax(sampleRate_, 1.0f); + float totalFreq = clampFreq(cutOff_ + modulation_); + + float f = totalFreq / std::fmax(sampleRate_, 1.0f); + if (f > kMaxNormalizedFrequency) f = kMaxNormalizedFrequency; + if (f < 0.0001f) + f = 0.0001f; + 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; @@ -48,8 +56,11 @@ void Filter::process(const float *in, float *out, uint32_t frames, float bp = bpL_; for (uint32_t i = 0; i < frames; ++i) { - if (modulation != nullptr) - setModulation(modulation[i]); + if (modulation != nullptr) { + if (std::abs(modulation[i] - modulation_) > 0.001f) { + setModulation(modulation[i]); + } + } out[i] = processFrame(in[i], lp, bp); } @@ -64,10 +75,17 @@ void Filter::processStereo(const float *inL, const float *inR, float *outL, float lpR = lpR_, bpR = bpR_; for (uint32_t i = 0; i < frames; ++i) { - if (modulation != nullptr) - setModulation(modulation[i]); - outL[i] = processFrame(inL[i], lpL, bpL); - outR[i] = processFrame(inR[i], lpR, bpR); + if (modulation != nullptr) { + if (std::abs(modulation[i] - modulation_) > 0.001f) { + setModulation(modulation[i]); + } + } + + float inValL = (inL != nullptr) ? inL[i] : 0.0f; + float inValR = (inR != nullptr) ? inR[i] : 0.0f; + + outL[i] = processFrame(inValL, lpL, bpL); + outR[i] = processFrame(inValR, lpR, bpR); } lpL_ = lpL; @@ -77,29 +95,35 @@ void Filter::processStereo(const float *inL, const float *inR, float *outL, } float Filter::getCutOff() const { return cutOff_; } + void Filter::setCutOff(float freq) { - cutOff_ = std::fmax(freq, 1.0f); + cutOff_ = clampFreq(freq); updateCoeffs(); } float Filter::getQ() const { return q_; } + void Filter::setQ(float q) { - q_ = std::fmax(q, 0.1f); + q_ = std::fmin(std::fmax(q, kQMin), kQMax); updateCoeffs(); } void Filter::setType(FilterType type) { type_ = type; } float Filter::getSampleRate() const { return sampleRate_; } + void Filter::setSampleRate(float sr) { - sampleRate_ = sr; - updateCoeffs(); + if (sr > 0.0f) { + sampleRate_ = sr; + updateCoeffs(); + } } float Filter::getModulation() const { return modulation_; } + void Filter::setModulation(float mod) { - if (modulation_ == mod) - return; - modulation_ = mod; - updateCoeffs(); + if (std::abs(modulation_ - mod) > 0.0001f) { + modulation_ = mod; + updateCoeffs(); + } } \ No newline at end of file diff --git a/core/Filter.hpp b/core/Filter.hpp index ca0a9ac..ca8186c 100644 --- a/core/Filter.hpp +++ b/core/Filter.hpp @@ -4,6 +4,10 @@ #include constexpr float kMaxNormalizedFrequency = 0.16667f; +constexpr float kFilterMin = 20.0f; +constexpr float kFilterMax = 10000.0f; +constexpr float kQMin = 0.1f; +constexpr float kQMax = 20.0f; class Filter { public: @@ -34,6 +38,7 @@ private: void updateCoeffs(); float modulation_; + float clampFreq(float freq); float processFrame(float input, float &lp, float &bp) const; float cutOff_; diff --git a/core/Oscillator.cpp b/core/Oscillator.cpp index 523616d..7cee116 100644 --- a/core/Oscillator.cpp +++ b/core/Oscillator.cpp @@ -22,6 +22,8 @@ void Oscillator::process(float *out, uint32_t frames) { } } +void Oscillator::reset() { phase_ = 0; } + void Oscillator::recalc_() { inc_ = freq_ / sampleRate_; } void Oscillator::setFrequency(float newFreq) { diff --git a/core/Oscillator.hpp b/core/Oscillator.hpp index e246ee9..8b37067 100644 --- a/core/Oscillator.hpp +++ b/core/Oscillator.hpp @@ -26,6 +26,8 @@ public: void setWaveShape(WaveShape shape); WaveShape getWaveShape() const; + void reset(); + private: float freq_; float gain_; diff --git a/plugins/SquidgeFilter/DistrhoPluginInfo.h b/plugins/SquidgeFilter/DistrhoPluginInfo.h index 4e6a319..1e69a67 100644 --- a/plugins/SquidgeFilter/DistrhoPluginInfo.h +++ b/plugins/SquidgeFilter/DistrhoPluginInfo.h @@ -11,6 +11,8 @@ #define DISTRHO_PLUGIN_NUM_PARAMETERS 3 +#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 + #define DISTRHO_PLUGIN_WANT_PROGRAMS 0 #define DISTRHO_PLUGIN_WANT_STATE 0 diff --git a/plugins/SquidgeFilter/SquidgeFilter.cpp b/plugins/SquidgeFilter/SquidgeFilter.cpp index 2d06bac..9397f8f 100644 --- a/plugins/SquidgeFilter/SquidgeFilter.cpp +++ b/plugins/SquidgeFilter/SquidgeFilter.cpp @@ -16,12 +16,14 @@ public: kParamFreq, kParamResonance, kParamType, + kParamLFOSyncMode, + kParamLFOAmount, kParameterCount // must be last }; - SquidgeFilter() // Note: Constructor name must match class name + SquidgeFilter() : Plugin(kParameterCount, 0, 0), filter_(1000.0f, 0.707f, 48000.0f), - lfo_(4.0f) { // Default: 1kHz, Q=0.707 + lfo_(4.0f) { filter_.setType(Filter::kLowPass); } @@ -73,6 +75,34 @@ protected: parameter.enumValues.values = const_cast(sTypes); break; + case kParamLFOAmount: + parameter.name = "LFO Amount"; + parameter.symbol = "lfoamount"; + parameter.ranges.min = 0.0f; + parameter.ranges.def = 0.0f; + parameter.ranges.max = 1000.0f; + + parameter.hints = kParameterIsAutomatable | kParameterIsInteger; + break; + case kParamLFOSyncMode: + parameter.name = "LFO Sync"; + parameter.symbol = "lfosync"; + parameter.ranges.min = 0.0f; + parameter.ranges.def = 3.0f; + parameter.ranges.max = 6.0f; + + parameter.hints = kParameterIsAutomatable | kParameterIsInteger; + + static const ParameterEnumerationValue sModes[] = { + {0.0f, "1/1 Bar"}, {1.0f, "1/2 Bar"}, {2.0f, "1/4 Bar"}, + {3.0f, "1/8 Bar"}, {4.0f, "1/16 Bar"}, {5.0f, "1/32 Bar"}, + {6.0f, "1/64 Bar"}}; + + parameter.enumValues.count = 7; + parameter.enumValues.restrictedMode = true; + parameter.enumValues.values = + const_cast(sModes); + break; } } @@ -84,7 +114,12 @@ protected: return filter_.getQ(); case kParamType: return static_cast(filter_.getType()); - } + case kParamLFOAmount: + return lfo_.getGain(); + break; + case kParamLFOSyncMode: + return lfoSync_; + }; return 0.0f; } @@ -96,6 +131,12 @@ protected: case kParamResonance: filter_.setQ(value); break; + case kParamLFOAmount: + lfo_.setGain(value); + break; + case kParamLFOSyncMode: + lfoSync_ = value; + break; case kParamType: int typeIdx = static_cast(std::fmin(std::fmax(value, 0.0f), 3.0f)); filter_.setType(static_cast(typeIdx)); @@ -104,6 +145,39 @@ protected: } void run(const float **inputs, float **outputs, uint32_t frames) override { + + const TimePosition &timePos = getTimePosition(); + + float targetFreq = 4.0f; + int syncIndex = static_cast(lfoSync_); + + if (syncIndex < 0) + syncIndex = 0; + if (syncIndex > 6) + syncIndex = 6; + + if (timePos.bbt.valid && timePos.playing && + timePos.bbt.beatsPerMinute > 0.0) { + float bpm = static_cast(timePos.bbt.beatsPerMinute); + + float baseFreq = (bpm / 60.0f) * 4.0f; + + static const float multipliers[] = {1.0f, 2.0f, 4.0f, 8.0f, + 16.0f, 32.0f, 64.0f}; + + targetFreq = baseFreq * multipliers[syncIndex]; + + static int lastBar = -1; + if (timePos.bbt.bar != lastBar) { + lfo_.reset(); + lastBar = timePos.bbt.bar; + } + } else { + + targetFreq = 1.0f; + } + + lfo_.setFrequency(targetFreq); lfo_.process(lfoBuffer_, frames); filter_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames, @@ -118,6 +192,7 @@ protected: private: Filter filter_; Oscillator lfo_; + float lfoSync_; float lfoBuffer_[BUFFER_SIZE]; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter); From 062d1e074c2e98c107bdf24aaaf2f02c87bcea10 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 12 Aug 2026 01:27:00 +0100 Subject: [PATCH 7/8] feat: improve oscillator initialization values and LFO sync implementation with bar tracking --- core/Oscillator.cpp | 22 +++++++++----- plugins/SquidgeFilter/DistrhoPluginInfo.h | 1 + plugins/SquidgeFilter/SquidgeFilter.cpp | 37 ++++++++++------------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/core/Oscillator.cpp b/core/Oscillator.cpp index 7cee116..0f4b8a6 100644 --- a/core/Oscillator.cpp +++ b/core/Oscillator.cpp @@ -4,13 +4,17 @@ Oscillator::Oscillator(float initFreq) : freq_(initFreq), gain_(100.0f), waveShape_(kWaveShapeSine), - sampleRate_(48000.f), phase_(0.0f), inc_(0.0f) { + sampleRate_(48000.f), phase_(0.0f), inc_(0.0f) +{ recalc_(); }; -void Oscillator::process(float *out, uint32_t frames) { - for (uint32_t i = 0; i < frames; ++i) { - switch (waveShape_) { +void Oscillator::process(float *out, uint32_t frames) +{ + for (uint32_t i = 0; i < frames; ++i) + { + switch (waveShape_) + { default: case kWaveShapeSine: out[i] = std::sinf(phase_ * 2.0f * M_PI) * gain_; @@ -26,17 +30,21 @@ void Oscillator::reset() { phase_ = 0; } void Oscillator::recalc_() { inc_ = freq_ / sampleRate_; } -void Oscillator::setFrequency(float newFreq) { +void Oscillator::setFrequency(float newFreq) +{ freq_ = std::fmax(newFreq, 0.0f); + recalc_(); } float Oscillator::getFrequency() const { return freq_; }; -void Oscillator::setGain(float newGain) { +void Oscillator::setGain(float newGain) +{ gain_ = newGain > 0 ? newGain : 1.0f; } float Oscillator::getGain() const { return gain_; }; -void Oscillator::setSampleRate(float newSampleRate) { +void Oscillator::setSampleRate(float newSampleRate) +{ sampleRate_ = newSampleRate > 0 ? newSampleRate : 48000.0f; recalc_(); } diff --git a/plugins/SquidgeFilter/DistrhoPluginInfo.h b/plugins/SquidgeFilter/DistrhoPluginInfo.h index 1e69a67..c9adf22 100644 --- a/plugins/SquidgeFilter/DistrhoPluginInfo.h +++ b/plugins/SquidgeFilter/DistrhoPluginInfo.h @@ -15,6 +15,7 @@ #define DISTRHO_PLUGIN_WANT_PROGRAMS 0 #define DISTRHO_PLUGIN_WANT_STATE 0 +#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 #define DISTRHO_PLUGIN_HAS_UI 0 #define DISTRHO_PLUGIN_IS_SYNTH 0 diff --git a/plugins/SquidgeFilter/SquidgeFilter.cpp b/plugins/SquidgeFilter/SquidgeFilter.cpp index 9397f8f..028f04e 100644 --- a/plugins/SquidgeFilter/SquidgeFilter.cpp +++ b/plugins/SquidgeFilter/SquidgeFilter.cpp @@ -22,8 +22,8 @@ public: }; SquidgeFilter() - : Plugin(kParameterCount, 0, 0), filter_(1000.0f, 0.707f, 48000.0f), - lfo_(4.0f) { + : Plugin(kParameterCount, 0, 0), filter_(440.0f, 2.0f, 48000.0f), + lfo_(20.0f), lfoSync_(0.0f) { filter_.setType(Filter::kLowPass); } @@ -43,16 +43,16 @@ protected: parameter.name = "Frequency"; parameter.symbol = "frequency"; parameter.ranges.min = 20.0f; - parameter.ranges.def = 1000.0f; - parameter.ranges.max = 20000.0f; + parameter.ranges.def = 440.0f; + parameter.ranges.max = 10000.0f; parameter.hints = kParameterIsAutomatable | kParameterIsLogarithmic; break; case kParamResonance: parameter.name = "Resonance"; parameter.symbol = "resonance"; - parameter.ranges.min = 0.1f; - parameter.ranges.def = 0.707f; + parameter.ranges.min = 0.9f; + parameter.ranges.def = 2.0f; parameter.ranges.max = 20.0f; parameter.hints = kParameterIsAutomatable; break; @@ -149,31 +149,25 @@ protected: const TimePosition &timePos = getTimePosition(); float targetFreq = 4.0f; - int syncIndex = static_cast(lfoSync_); - - if (syncIndex < 0) - syncIndex = 0; - if (syncIndex > 6) - syncIndex = 6; if (timePos.bbt.valid && timePos.playing && timePos.bbt.beatsPerMinute > 0.0) { + float bpb = static_cast(timePos.bbt.beatsPerBar); float bpm = static_cast(timePos.bbt.beatsPerMinute); + float bar = static_cast(timePos.bbt.bar); - float baseFreq = (bpm / 60.0f) * 4.0f; + if (bar != currentBar_) { + lfo_.reset(); + currentBar_ = bar; + } + + float baseFreq = (bpm / 60.0f) / bpb; static const float multipliers[] = {1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f}; - targetFreq = baseFreq * multipliers[syncIndex]; - - static int lastBar = -1; - if (timePos.bbt.bar != lastBar) { - lfo_.reset(); - lastBar = timePos.bbt.bar; - } + targetFreq = baseFreq * multipliers[static_cast(lfoSync_)]; } else { - targetFreq = 1.0f; } @@ -193,6 +187,7 @@ private: Filter filter_; Oscillator lfo_; float lfoSync_; + uint32_t currentBar_ = 0; float lfoBuffer_[BUFFER_SIZE]; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter); From 55ec7f7c196f928455783c796b2b27cd0ab558e6 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Wed, 12 Aug 2026 01:55:27 +0100 Subject: [PATCH 8/8] feat: add initial implementation of squidgeTemplate plugin with basic gain parameter --- plugins/SquidgeTemplate/DistrhoPluginInfo.h | 26 +++++++ plugins/SquidgeTemplate/Makefile | 10 +++ plugins/SquidgeTemplate/SquidgeTemplate.cpp | 79 +++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 plugins/SquidgeTemplate/DistrhoPluginInfo.h create mode 100644 plugins/SquidgeTemplate/Makefile create mode 100644 plugins/SquidgeTemplate/SquidgeTemplate.cpp diff --git a/plugins/SquidgeTemplate/DistrhoPluginInfo.h b/plugins/SquidgeTemplate/DistrhoPluginInfo.h new file mode 100644 index 0000000..c38773a --- /dev/null +++ b/plugins/SquidgeTemplate/DistrhoPluginInfo.h @@ -0,0 +1,26 @@ +#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED +#define DISTRHO_PLUGIN_INFO_H_INCLUDED + +#define DISTRHO_PLUGIN_NAME "SquidgeTemplate" +#define DISTRHO_PLUGIN_AUTHOR "SquidgeSoft" +#define DISTRHO_PLUGIN_URI "moe.liam.squidgesoft.SquidgeTemplate" +#define DISTRHO_PLUGIN_CATEGORY "fx" + +#define DISTRHO_PLUGIN_NUM_INPUTS 2 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 + +#define DISTRHO_PLUGIN_NUM_PARAMETERS 1 + +#define DISTRHO_PLUGIN_WANT_TIMEPOS 0 + +#define DISTRHO_PLUGIN_WANT_PROGRAMS 0 +#define DISTRHO_PLUGIN_WANT_STATE 0 +#define DISTRHO_PLUGIN_HAS_UI 0 +#define DISTRHO_PLUGIN_IS_SYNTH 0 + +#define DISTRHO_PLUGIN_PRIV_ID "moe.liam.squidgesoft.SquidgeTemplate" +#define DISTRHO_PLUGIN_BRAND "SquidgeSoft" + +#define DISTRHO_PLUGIN_CLAP_ID "moe.liam.squidgesoft.SquidgeTemplate" + +#endif diff --git a/plugins/SquidgeTemplate/Makefile b/plugins/SquidgeTemplate/Makefile new file mode 100644 index 0000000..a8247ff --- /dev/null +++ b/plugins/SquidgeTemplate/Makefile @@ -0,0 +1,10 @@ +#!/usr/bin/make -f +NAME = squidgeTemplate + +FILES_DSP = squidgeTemplate.cpp ../../core/Gain.cpp + +TARGETS = vst3 clap + +include ../../dpf/Makefile.plugins.mk + +all: $(TARGETS) diff --git a/plugins/SquidgeTemplate/SquidgeTemplate.cpp b/plugins/SquidgeTemplate/SquidgeTemplate.cpp new file mode 100644 index 0000000..188a0b4 --- /dev/null +++ b/plugins/SquidgeTemplate/SquidgeTemplate.cpp @@ -0,0 +1,79 @@ +#include "DistrhoDetails.hpp" +#include "DistrhoPlugin.hpp" +#include "DistrhoPluginInfo.h" + +#include "../../core/Gain.hpp" + +START_NAMESPACE_DISTRHO + +class squidgeTemplate : public Plugin +{ +public: + enum Parameters + { + kParamGain, + kParameterCount + }; + + squidgeTemplate() + : Plugin(kParameterCount, 0, 0), gain_(1.0f) + { + } + +protected: + const char *getLabel() const override { return "squidgeTemplate"; } + const char *getDescription() const override { return "Generic squidge plugin template."; } + const char *getMaker() const override { return "SquidgeSoft"; } + const char *getLicense() const override { return "GPL3"; } + uint32_t getVersion() const override { return d_version(0, 1, 0); } + int64_t getUniqueId() const override { return d_cconst('S', 'Q', 'T', 'P'); } + + void initParameter(uint32_t index, Parameter ¶meter) override + { + switch (index) + { + case kParamGain: + parameter.name = "Gain"; + parameter.symbol = "gain"; + parameter.ranges.min = 0.0f; + parameter.ranges.def = 1.0f; + parameter.ranges.max = 2.0f; + parameter.hints = kParameterIsAutomatable; + break; + } + } + + float getParameterValue(uint32_t index) const override + { + if (index == kParamGain) + return gain_.getGain(); + return 0.0f; + } + + void setParameterValue(uint32_t index, float value) override + { + if (index == kParamGain) + gain_.setGain(value); + } + + void run(const float **inputs, float **outputs, uint32_t frames) override + { + gain_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames); + } + + void sampleRateChanged(double /*newSr*/) override + { + } + +private: + Gain gain_; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(squidgeTemplate) +}; + +Plugin *createPlugin() +{ + return new squidgeTemplate(); +} + +END_NAMESPACE_DISTRHO