feat(filter): Add modulation support and LFO integration

This commit is contained in:
Liam Kerr 2026-08-06 00:03:43 +01:00
parent 3c3fec9b90
commit fe594a5494
6 changed files with 131 additions and 20 deletions

View file

@ -1,21 +1,22 @@
#include "Filter.hpp" #include "Filter.hpp"
#include <cmath> #include <cmath>
#include <sys/types.h>
#ifndef M_PI #ifndef M_PI
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
#endif #endif
Filter::Filter(float initCutOff, float initQ, float initSampleRate) Filter::Filter(float initCutOff, float initQ, float initSampleRate)
: cutOff_(initCutOff), q_(initQ), sampleRate_(initSampleRate), : modulation_(0.0f), cutOff_(initCutOff), q_(initQ),
type_(kLowPass), g_(0.0f), lpL_(0.0f), bpL_(0.0f), lpR_(0.0f), sampleRate_(initSampleRate), type_(kLowPass), g_(0.0f), lpL_(0.0f),
bpR_(0.0f) { bpL_(0.0f), lpR_(0.0f), bpR_(0.0f) {
updateCoeffs(); updateCoeffs();
} }
Filter::FilterType Filter::getType() const { return type_; } Filter::FilterType Filter::getType() const { return type_; }
void Filter::updateCoeffs() { void Filter::updateCoeffs() {
float f = cutOff_ / std::fmax(sampleRate_, 1.0f); float f = (cutOff_ + modulation_) / std::fmax(sampleRate_, 1.0f);
if (f > kMaxNormalizedFrequency) if (f > kMaxNormalizedFrequency)
f = kMaxNormalizedFrequency; f = kMaxNormalizedFrequency;
g_ = 2.0f * std::sin(M_PI * f); 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 lp = lpL_;
float bp = bpL_; float bp = bpL_;
for (uint32_t i = 0; i < frames; ++i) { for (uint32_t i = 0; i < frames; ++i) {
if (modulation != nullptr)
setModulation(modulation[i]);
out[i] = processFrame(in[i], lp, bp); 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, 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 lpL = lpL_, bpL = bpL_;
float lpR = lpR_, bpR = bpR_; float lpR = lpR_, bpR = bpR_;
for (uint32_t i = 0; i < frames; ++i) { for (uint32_t i = 0; i < frames; ++i) {
if (modulation != nullptr)
setModulation(modulation[i]);
outL[i] = processFrame(inL[i], lpL, bpL); outL[i] = processFrame(inL[i], lpL, bpL);
outR[i] = processFrame(inR[i], lpR, bpR); outR[i] = processFrame(inR[i], lpR, bpR);
} }
@ -88,3 +95,11 @@ void Filter::setSampleRate(float sr) {
sampleRate_ = sr; sampleRate_ = sr;
updateCoeffs(); updateCoeffs();
} }
float Filter::getModulation() const { return modulation_; }
void Filter::setModulation(float mod) {
if (modulation_ == mod)
return;
modulation_ = mod;
updateCoeffs();
}

View file

@ -13,21 +13,26 @@ public:
enum FilterType { kLowPass, kHighPass, kBandPass, kNotch, kFilterTypeCount }; enum FilterType { kLowPass, kHighPass, kBandPass, kNotch, kFilterTypeCount };
void setCutOff(float freq); void setCutOff(float freq);
void setQ(float q);
void setSampleRate(float sr);
void setType(FilterType type);
float getCutOff() const; 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, 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: private:
void updateCoeffs(); void updateCoeffs();
float modulation_;
float processFrame(float input, float &lp, float &bp) const; float processFrame(float input, float &lp, float &bp) const;

44
core/Oscillator.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "Oscillator.hpp"
#include <cmath>
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_; }

39
core/Oscillator.hpp Normal file
View file

@ -0,0 +1,39 @@
#ifndef OSCILLATOR_HPP_INCLUDED
#define OSCILLATOR_HPP_INCLUDED
#include <cstdint>
#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

View file

@ -1,7 +1,7 @@
#!/usr/bin/make -f #!/usr/bin/make -f
NAME = SquidgeFilter NAME = SquidgeFilter
FILES_DSP = SquidgeFilter.cpp ../../core/Filter.cpp FILES_DSP = SquidgeFilter.cpp ../../core/Filter.cpp ../../core/Oscillator.cpp
TARGETS = vst3 clap TARGETS = vst3 clap

View file

@ -3,9 +3,12 @@
#include "DistrhoPluginInfo.h" #include "DistrhoPluginInfo.h"
#include "../../core/Filter.hpp" #include "../../core/Filter.hpp"
#include "../../core/Oscillator.hpp"
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
const int BUFFER_SIZE = 16384;
class SquidgeFilter : public Plugin { class SquidgeFilter : public Plugin {
public: public:
@ -17,8 +20,8 @@ public:
}; };
SquidgeFilter() // Note: Constructor name must match class name SquidgeFilter() // Note: Constructor name must match class name
: Plugin(kParameterCount, 0, 0), : Plugin(kParameterCount, 0, 0), filter_(1000.0f, 0.707f, 48000.0f),
filter_(1000.0f, 0.707f, 48000.0f) { // Default: 1kHz, Q=0.707 lfo_(4.0f) { // Default: 1kHz, Q=0.707
filter_.setType(Filter::kLowPass); filter_.setType(Filter::kLowPass);
} }
@ -94,7 +97,6 @@ protected:
filter_.setQ(value); filter_.setQ(value);
break; break;
case kParamType: case kParamType:
// Clamp value to valid enum range
int typeIdx = static_cast<int>(std::fmin(std::fmax(value, 0.0f), 3.0f)); int typeIdx = static_cast<int>(std::fmin(std::fmax(value, 0.0f), 3.0f));
filter_.setType(static_cast<Filter::FilterType>(typeIdx)); filter_.setType(static_cast<Filter::FilterType>(typeIdx));
break; break;
@ -102,15 +104,21 @@ protected:
} }
void run(const float **inputs, float **outputs, uint32_t frames) override { 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 { void sampleRateChanged(double newSr) override {
filter_.setSampleRate(static_cast<float>(newSr)); filter_.setSampleRate(static_cast<float>(newSr));
lfo_.setSampleRate(newSr);
} }
private: private:
Filter filter_; Filter filter_;
Oscillator lfo_;
float lfoBuffer_[BUFFER_SIZE];
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter);
}; };