feat: adding lfo to filter
This commit is contained in:
parent
fe594a5494
commit
d8c1c90f15
6 changed files with 130 additions and 20 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
#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
|
||||||
|
|
@ -15,15 +14,24 @@ Filter::Filter(float initCutOff, float initQ, float initSampleRate)
|
||||||
|
|
||||||
Filter::FilterType Filter::getType() const { return type_; }
|
Filter::FilterType Filter::getType() const { return type_; }
|
||||||
|
|
||||||
|
float Filter::clampFreq(float freq) {
|
||||||
|
return std::fmin(std::fmax(freq, kFilterMin), kFilterMax);
|
||||||
|
}
|
||||||
|
|
||||||
void Filter::updateCoeffs() {
|
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)
|
if (f > kMaxNormalizedFrequency)
|
||||||
f = kMaxNormalizedFrequency;
|
f = kMaxNormalizedFrequency;
|
||||||
|
if (f < 0.0001f)
|
||||||
|
f = 0.0001f;
|
||||||
|
|
||||||
g_ = 2.0f * std::sin(M_PI * f);
|
g_ = 2.0f * std::sin(M_PI * f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Filter::processFrame(float input, float &lp, float &bp) const {
|
float Filter::processFrame(float input, float &lp, float &bp) const {
|
||||||
|
|
||||||
float hp = input - (bp / q_) - lp;
|
float hp = input - (bp / q_) - lp;
|
||||||
|
|
||||||
bp = bp + g_ * hp;
|
bp = bp + g_ * hp;
|
||||||
|
|
@ -48,8 +56,11 @@ void Filter::process(const float *in, float *out, uint32_t frames,
|
||||||
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)
|
if (modulation != nullptr) {
|
||||||
setModulation(modulation[i]);
|
if (std::abs(modulation[i] - modulation_) > 0.001f) {
|
||||||
|
setModulation(modulation[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
out[i] = processFrame(in[i], lp, bp);
|
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_;
|
float lpR = lpR_, bpR = bpR_;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < frames; ++i) {
|
for (uint32_t i = 0; i < frames; ++i) {
|
||||||
if (modulation != nullptr)
|
if (modulation != nullptr) {
|
||||||
setModulation(modulation[i]);
|
if (std::abs(modulation[i] - modulation_) > 0.001f) {
|
||||||
outL[i] = processFrame(inL[i], lpL, bpL);
|
setModulation(modulation[i]);
|
||||||
outR[i] = processFrame(inR[i], lpR, bpR);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
lpL_ = lpL;
|
||||||
|
|
@ -77,29 +95,35 @@ void Filter::processStereo(const float *inL, const float *inR, float *outL,
|
||||||
}
|
}
|
||||||
|
|
||||||
float Filter::getCutOff() const { return cutOff_; }
|
float Filter::getCutOff() const { return cutOff_; }
|
||||||
|
|
||||||
void Filter::setCutOff(float freq) {
|
void Filter::setCutOff(float freq) {
|
||||||
cutOff_ = std::fmax(freq, 1.0f);
|
cutOff_ = clampFreq(freq);
|
||||||
updateCoeffs();
|
updateCoeffs();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Filter::getQ() const { return q_; }
|
float Filter::getQ() const { return q_; }
|
||||||
|
|
||||||
void Filter::setQ(float q) {
|
void Filter::setQ(float q) {
|
||||||
q_ = std::fmax(q, 0.1f);
|
q_ = std::fmin(std::fmax(q, kQMin), kQMax);
|
||||||
updateCoeffs();
|
updateCoeffs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filter::setType(FilterType type) { type_ = type; }
|
void Filter::setType(FilterType type) { type_ = type; }
|
||||||
|
|
||||||
float Filter::getSampleRate() const { return sampleRate_; }
|
float Filter::getSampleRate() const { return sampleRate_; }
|
||||||
|
|
||||||
void Filter::setSampleRate(float sr) {
|
void Filter::setSampleRate(float sr) {
|
||||||
sampleRate_ = sr;
|
if (sr > 0.0f) {
|
||||||
updateCoeffs();
|
sampleRate_ = sr;
|
||||||
|
updateCoeffs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float Filter::getModulation() const { return modulation_; }
|
float Filter::getModulation() const { return modulation_; }
|
||||||
|
|
||||||
void Filter::setModulation(float mod) {
|
void Filter::setModulation(float mod) {
|
||||||
if (modulation_ == mod)
|
if (std::abs(modulation_ - mod) > 0.0001f) {
|
||||||
return;
|
modulation_ = mod;
|
||||||
modulation_ = mod;
|
updateCoeffs();
|
||||||
updateCoeffs();
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
constexpr float kMaxNormalizedFrequency = 0.16667f;
|
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 {
|
class Filter {
|
||||||
public:
|
public:
|
||||||
|
|
@ -34,6 +38,7 @@ private:
|
||||||
void updateCoeffs();
|
void updateCoeffs();
|
||||||
float modulation_;
|
float modulation_;
|
||||||
|
|
||||||
|
float clampFreq(float freq);
|
||||||
float processFrame(float input, float &lp, float &bp) const;
|
float processFrame(float input, float &lp, float &bp) const;
|
||||||
|
|
||||||
float cutOff_;
|
float cutOff_;
|
||||||
|
|
|
||||||
|
|
@ -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::recalc_() { inc_ = freq_ / sampleRate_; }
|
||||||
|
|
||||||
void Oscillator::setFrequency(float newFreq) {
|
void Oscillator::setFrequency(float newFreq) {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ public:
|
||||||
void setWaveShape(WaveShape shape);
|
void setWaveShape(WaveShape shape);
|
||||||
WaveShape getWaveShape() const;
|
WaveShape getWaveShape() const;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float freq_;
|
float freq_;
|
||||||
float gain_;
|
float gain_;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#define DISTRHO_PLUGIN_NUM_PARAMETERS 3
|
#define DISTRHO_PLUGIN_NUM_PARAMETERS 3
|
||||||
|
|
||||||
|
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1
|
||||||
|
|
||||||
#define DISTRHO_PLUGIN_WANT_PROGRAMS 0
|
#define DISTRHO_PLUGIN_WANT_PROGRAMS 0
|
||||||
#define DISTRHO_PLUGIN_WANT_STATE 0
|
#define DISTRHO_PLUGIN_WANT_STATE 0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,14 @@ public:
|
||||||
kParamFreq,
|
kParamFreq,
|
||||||
kParamResonance,
|
kParamResonance,
|
||||||
kParamType,
|
kParamType,
|
||||||
|
kParamLFOSyncMode,
|
||||||
|
kParamLFOAmount,
|
||||||
kParameterCount // must be last
|
kParameterCount // must be last
|
||||||
};
|
};
|
||||||
|
|
||||||
SquidgeFilter() // Note: Constructor name must match class name
|
SquidgeFilter()
|
||||||
: Plugin(kParameterCount, 0, 0), filter_(1000.0f, 0.707f, 48000.0f),
|
: 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);
|
filter_.setType(Filter::kLowPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,6 +75,34 @@ protected:
|
||||||
parameter.enumValues.values =
|
parameter.enumValues.values =
|
||||||
const_cast<ParameterEnumerationValue *>(sTypes);
|
const_cast<ParameterEnumerationValue *>(sTypes);
|
||||||
break;
|
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<ParameterEnumerationValue *>(sModes);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +114,12 @@ protected:
|
||||||
return filter_.getQ();
|
return filter_.getQ();
|
||||||
case kParamType:
|
case kParamType:
|
||||||
return static_cast<float>(filter_.getType());
|
return static_cast<float>(filter_.getType());
|
||||||
}
|
case kParamLFOAmount:
|
||||||
|
return lfo_.getGain();
|
||||||
|
break;
|
||||||
|
case kParamLFOSyncMode:
|
||||||
|
return lfoSync_;
|
||||||
|
};
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,6 +131,12 @@ protected:
|
||||||
case kParamResonance:
|
case kParamResonance:
|
||||||
filter_.setQ(value);
|
filter_.setQ(value);
|
||||||
break;
|
break;
|
||||||
|
case kParamLFOAmount:
|
||||||
|
lfo_.setGain(value);
|
||||||
|
break;
|
||||||
|
case kParamLFOSyncMode:
|
||||||
|
lfoSync_ = value;
|
||||||
|
break;
|
||||||
case kParamType:
|
case kParamType:
|
||||||
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));
|
||||||
|
|
@ -104,6 +145,39 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(const float **inputs, float **outputs, uint32_t frames) override {
|
void run(const float **inputs, float **outputs, uint32_t frames) override {
|
||||||
|
|
||||||
|
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);
|
||||||
lfo_.process(lfoBuffer_, frames);
|
lfo_.process(lfoBuffer_, frames);
|
||||||
|
|
||||||
filter_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames,
|
filter_.processStereo(inputs[0], inputs[1], outputs[0], outputs[1], frames,
|
||||||
|
|
@ -118,6 +192,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
Filter filter_;
|
Filter filter_;
|
||||||
Oscillator lfo_;
|
Oscillator lfo_;
|
||||||
|
float lfoSync_;
|
||||||
float lfoBuffer_[BUFFER_SIZE];
|
float lfoBuffer_[BUFFER_SIZE];
|
||||||
|
|
||||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter);
|
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SquidgeFilter);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue