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);