2026-08-05 21:02:23 +01:00
|
|
|
#include "DistrhoDetails.hpp"
|
|
|
|
|
#include "DistrhoPlugin.hpp"
|
|
|
|
|
#include "DistrhoPluginInfo.h"
|
|
|
|
|
|
|
|
|
|
#include "../../core/Filter.hpp"
|
2026-08-06 00:03:43 +01:00
|
|
|
#include "../../core/Oscillator.hpp"
|
2026-08-05 21:02:23 +01:00
|
|
|
|
|
|
|
|
START_NAMESPACE_DISTRHO
|
|
|
|
|
|
2026-08-06 00:03:43 +01:00
|
|
|
const int BUFFER_SIZE = 16384;
|
|
|
|
|
|
2026-08-05 21:02:23 +01:00
|
|
|
class SquidgeFilter : public Plugin {
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
enum Parameters {
|
|
|
|
|
kParamFreq,
|
|
|
|
|
kParamResonance,
|
|
|
|
|
kParamType,
|
2026-08-12 00:05:50 +01:00
|
|
|
kParamLFOSyncMode,
|
|
|
|
|
kParamLFOAmount,
|
2026-08-05 21:02:23 +01:00
|
|
|
kParameterCount // must be last
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-12 00:05:50 +01:00
|
|
|
SquidgeFilter()
|
2026-08-06 00:03:43 +01:00
|
|
|
: Plugin(kParameterCount, 0, 0), filter_(1000.0f, 0.707f, 48000.0f),
|
2026-08-12 00:05:50 +01:00
|
|
|
lfo_(4.0f) {
|
2026-08-05 21:02:23 +01:00
|
|
|
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<ParameterEnumerationValue *>(sTypes);
|
|
|
|
|
break;
|
2026-08-12 00:05:50 +01:00
|
|
|
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<ParameterEnumerationValue *>(sModes);
|
|
|
|
|
break;
|
2026-08-05 21:02:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float getParameterValue(uint32_t index) const override {
|
|
|
|
|
switch (index) {
|
|
|
|
|
case kParamFreq:
|
|
|
|
|
return filter_.getCutOff();
|
|
|
|
|
case kParamResonance:
|
|
|
|
|
return filter_.getQ();
|
|
|
|
|
case kParamType:
|
|
|
|
|
return static_cast<float>(filter_.getType());
|
2026-08-12 00:05:50 +01:00
|
|
|
case kParamLFOAmount:
|
|
|
|
|
return lfo_.getGain();
|
|
|
|
|
break;
|
|
|
|
|
case kParamLFOSyncMode:
|
|
|
|
|
return lfoSync_;
|
|
|
|
|
};
|
2026-08-05 21:02:23 +01:00
|
|
|
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;
|
2026-08-12 00:05:50 +01:00
|
|
|
case kParamLFOAmount:
|
|
|
|
|
lfo_.setGain(value);
|
|
|
|
|
break;
|
|
|
|
|
case kParamLFOSyncMode:
|
|
|
|
|
lfoSync_ = value;
|
|
|
|
|
break;
|
2026-08-05 21:02:23 +01:00
|
|
|
case kParamType:
|
|
|
|
|
int typeIdx = static_cast<int>(std::fmin(std::fmax(value, 0.0f), 3.0f));
|
|
|
|
|
filter_.setType(static_cast<Filter::FilterType>(typeIdx));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void run(const float **inputs, float **outputs, uint32_t frames) override {
|
2026-08-12 00:05:50 +01:00
|
|
|
|
|
|
|
|
const TimePosition &timePos = getTimePosition();
|
|
|
|
|
|
|
|
|
|
float targetFreq = 4.0f;
|
|
|
|
|
int syncIndex = static_cast<int>(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<float>(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);
|
2026-08-06 00:03:43 +01:00
|
|
|
lfo_.process(lfoBuffer_, frames);
|
|
|
|
|
|
|
|
|
|
filter_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames,
|
|
|
|
|
lfoBuffer_);
|
2026-08-05 21:02:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void sampleRateChanged(double newSr) override {
|
|
|
|
|
filter_.setSampleRate(static_cast<float>(newSr));
|
2026-08-06 00:03:43 +01:00
|
|
|
lfo_.setSampleRate(newSr);
|
2026-08-05 21:02:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Filter filter_;
|
2026-08-06 00:03:43 +01:00
|
|
|
Oscillator lfo_;
|
2026-08-12 00:05:50 +01:00
|
|
|
float lfoSync_;
|
2026-08-06 00:03:43 +01:00
|
|
|
float lfoBuffer_[BUFFER_SIZE];
|
2026-08-05 21:02:23 +01:00
|
|
|
|
|
|
|
|
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Plugin *createPlugin() { return new SquidgeFilter(); }
|
|
|
|
|
|
|
|
|
|
END_NAMESPACE_DISTRHO
|