From 2cfc49bac3319707b2c84255c4231db62f5fef7f Mon Sep 17 00:00:00 2001 From: HiveBeats Date: Wed, 17 Jan 2024 08:56:59 +0700 Subject: [PATCH] feat: all filter types --- inc/BandPassFilter.h | 9 ++++-- inc/Filter.h | 41 ++++++++++++------------ inc/HighPassFilter.h | 4 +-- inc/LowPassFilter.h | 3 +- inc/LowPassStateVariableFilter.h | 15 --------- inc/StateVariableFilter.h | 35 --------------------- inc/Synth.h | 4 +-- src/BandPassFilter.cpp | 29 +++++++++-------- src/Filter.cpp | 50 +++++++++++++++++++----------- src/HighPassFilter.cpp | 29 +++++++++-------- src/LowPassFilter.cpp | 29 ++++++----------- src/LowPassStateVariableFilter.cpp | 21 ------------- src/Renderer.cpp | 4 +-- src/StateVariableFilter.cpp | 50 ------------------------------ src/Synth.cpp | 21 ++++--------- 15 files changed, 110 insertions(+), 234 deletions(-) delete mode 100644 inc/LowPassStateVariableFilter.h delete mode 100644 inc/StateVariableFilter.h delete mode 100644 src/LowPassStateVariableFilter.cpp delete mode 100644 src/StateVariableFilter.cpp diff --git a/inc/BandPassFilter.h b/inc/BandPassFilter.h index e83c929..e2c0fd0 100644 --- a/inc/BandPassFilter.h +++ b/inc/BandPassFilter.h @@ -1,14 +1,17 @@ #pragma once + #include "Filter.h" class BandPassFilter : public Filter { - private: - void CalculateCoefficients() override; + protected: + float GetSampleForFilterType() override; public: + BandPassFilter(); BandPassFilter(Filter* filter); BandPassFilter(float freq, float res, float q); - BandPassFilter(/* args */); ~BandPassFilter(); bool IsSameFilterType(FilterType type) override { return type == BandPass; }; }; + + diff --git a/inc/Filter.h b/inc/Filter.h index c4d8ba4..2307f1c 100644 --- a/inc/Filter.h +++ b/inc/Filter.h @@ -1,24 +1,25 @@ #pragma once #include "IEffect.h" +#include "Settings.h" -enum FilterType { - LowPass, - BandPass, - HighPass -}; +enum FilterType { LowPass, BandPass, HighPass }; class Filter : public IEffect { protected: - float m_freq; // cutoff frequency - float m_q; // filter quantity (resonance) - float m_order; // filter order (peakGain) - /* todo: filter adsr */ - float m_norm, m_v, m_k; - float m_a0, m_a1, m_a2, m_b1, m_b2; - float m_z1, m_z2; - - void CalculateNormals(); - virtual void CalculateCoefficients(){}; + // float* m_output; // output buffer + float m_fs = SAMPLE_RATE; // sampling frequency; + float m_fc; // cutoff frequency normally something like: 440.0*pow(2.0, + // (midi_note - 69.0)/12.0); + float m_res; // resonance 0 to 1; + float m_drive; // internal distortion 0 to 0.1 + float m_freq; + float m_damp; + float m_notcho; // notch output + float m_lowo; // low pass output + float m_higho; // high pass output + float m_bando; // band pass output + float m_peako; // peaking output = low - high + virtual float GetSampleForFilterType(){ return 0.0; }; public: Filter(/* args */); @@ -27,9 +28,9 @@ class Filter : public IEffect { void Release() override final; float Process(float in); void Process(std::vector& samples) override final; - void SetParameters(float freq, float res, float q); - float GetFreq() { return m_freq; } - float GetRes() { return m_q; } - float GetPeakGain() { return m_norm; } - virtual bool IsSameFilterType(FilterType type){ return false; }; + void SetParameters(float freq, float res, float drive); + float GetFreq() { return m_fc; } + float GetRes() { return m_res; } + float GetPeakGain() { return m_drive; } + virtual bool IsSameFilterType(FilterType type) { return false; }; }; diff --git a/inc/HighPassFilter.h b/inc/HighPassFilter.h index 69556a5..fa430ce 100644 --- a/inc/HighPassFilter.h +++ b/inc/HighPassFilter.h @@ -2,8 +2,8 @@ #include "Filter.h" class HighPassFilter : public Filter { - private: - void CalculateCoefficients() override; + protected: + float GetSampleForFilterType() override; public: HighPassFilter(); diff --git a/inc/LowPassFilter.h b/inc/LowPassFilter.h index 1ee043b..f447670 100644 --- a/inc/LowPassFilter.h +++ b/inc/LowPassFilter.h @@ -4,7 +4,7 @@ class LowPassFilter : public Filter { protected: - void CalculateCoefficients() override; + float GetSampleForFilterType() override; public: LowPassFilter(); @@ -13,3 +13,4 @@ class LowPassFilter : public Filter { ~LowPassFilter(); bool IsSameFilterType(FilterType type) override { return type == LowPass; }; }; + diff --git a/inc/LowPassStateVariableFilter.h b/inc/LowPassStateVariableFilter.h deleted file mode 100644 index 7357341..0000000 --- a/inc/LowPassStateVariableFilter.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "StateVariableFilter.h" - -class LowPassStateVariableFilter : StateVariableFilter { - protected: - float GetSampleForFilterType() override; - - public: - LowPassStateVariableFilter(); - LowPassStateVariableFilter(StateVariableFilter* filter); - LowPassStateVariableFilter(float freq, float res, float q); - ~LowPassStateVariableFilter(); - bool IsSameFilterType(FilterType type) override { return type == LowPass; }; -}; diff --git a/inc/StateVariableFilter.h b/inc/StateVariableFilter.h deleted file mode 100644 index 05db56a..0000000 --- a/inc/StateVariableFilter.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "Filter.h" -#include "IEffect.h" -#include "Settings.h" - -class StateVariableFilter : public IEffect { - protected: - // float* m_output; // output buffer - float m_fs = SAMPLE_RATE; // sampling frequency; - float m_fc; // cutoff frequency normally something like: 440.0*pow(2.0, - // (midi_note - 69.0)/12.0); - float m_res; // resonance 0 to 1; - float m_drive; // internal distortion 0 to 0.1 - float m_freq; - float m_damp; - float m_notcho; // notch output - float m_lowo; // low pass output - float m_higho; // high pass output - float m_bando; // band pass output - float m_peako; // peaking output = low - high - virtual float GetSampleForFilterType(){ return 0.0; }; - - public: - StateVariableFilter(/* args */); - virtual ~StateVariableFilter(); - void Trigger() override final; - void Release() override final; - float Process(float in); - void Process(std::vector& samples) override final; - void SetParameters(float freq, float res, float drive); - float GetFreq() { return m_fc; } - float GetRes() { return m_res; } - float GetPeakGain() { return m_drive; } - virtual bool IsSameFilterType(FilterType type) { return false; }; -}; diff --git a/inc/Synth.h b/inc/Synth.h index a7b2505..c150d11 100644 --- a/inc/Synth.h +++ b/inc/Synth.h @@ -1,7 +1,7 @@ #pragma once #include "ADSR.h" -#include "StateVariableFilter.h" +#include "Filter.h" #include "Adder.h" #include "IEffect.h" #include "Note.h" @@ -35,6 +35,6 @@ class Synth { const std::vector& GetOscillators() { return m_oscillators; } const bool& GetIsNoteTriggered() { return is_note_triggered; } ADSR* GetADSR() { return (ADSR*)m_effects[0]; } - StateVariableFilter* GetFilter() { return (StateVariableFilter*)m_effects[1]; } + Filter* GetFilter() { return (Filter*)m_effects[1]; } void SetFilter(FilterType type); }; \ No newline at end of file diff --git a/src/BandPassFilter.cpp b/src/BandPassFilter.cpp index c751e3d..2f1ee52 100644 --- a/src/BandPassFilter.cpp +++ b/src/BandPassFilter.cpp @@ -1,23 +1,22 @@ #include "BandPassFilter.h" +#include "Settings.h" -BandPassFilter::BandPassFilter(/* args */) {} - -BandPassFilter::BandPassFilter(Filter* filter) { - m_freq = filter->GetFreq(); - m_q = filter->GetRes(); - m_order = filter->GetPeakGain(); +BandPassFilter::BandPassFilter() { + SetParameters(200, 0.1, 0.001); } -BandPassFilter::BandPassFilter(float freq, float res, float q) {} +BandPassFilter::BandPassFilter( + Filter* filter) { + SetParameters(filter->GetFreq(), filter->GetRes(), filter->GetPeakGain()); +} + +BandPassFilter::BandPassFilter(float freq, float res, + float q) { + SetParameters(freq, res, q); +} BandPassFilter::~BandPassFilter() {} -void BandPassFilter::CalculateCoefficients() { - CalculateNormals(); - m_norm = 1 / (1 + m_k / m_q + m_k * m_k); - m_a0 = m_k / m_q * m_norm; - m_a1 = 0; - m_a2 = -m_a0; - m_b1 = 2 * (m_k * m_k - 1) * m_norm; - m_b2 = (1 - m_k / m_q + m_k * m_k) * m_norm; +float BandPassFilter::GetSampleForFilterType() { + return m_lowo; } \ No newline at end of file diff --git a/src/Filter.cpp b/src/Filter.cpp index 4beecce..a8fc39e 100644 --- a/src/Filter.cpp +++ b/src/Filter.cpp @@ -1,36 +1,50 @@ #include "Filter.h" #include "Settings.h" +#include +#include Filter::Filter(/* args */) {} Filter::~Filter() {} -void Filter::CalculateNormals() { - m_v = powf(10, fabs(m_order) / 20.0); - m_k = tanf(M_PI * m_freq); -} - void Filter::Trigger() {} void Filter::Release() {} -float Filter::Process(float in) { - // may move to a compile-time dictionary calculation, if needed - CalculateCoefficients(); - float out = in * m_a0 + m_z1; - m_z1 = in * m_a1 + m_z2 - m_b1 * out; - m_z2 = in * m_a2 - m_b2 * out; - return out; -} - void Filter::Process(std::vector& samples) { for (std::size_t i = 0; i < samples.size(); i++) { samples[i] = Process(samples[i]); } } -void Filter::SetParameters(float freq, float res, float q) { - m_freq = freq / SAMPLE_RATE; - m_q = res; - m_order = q; + +float Filter::Process(float in) { + m_notcho = in - m_damp * m_bando; + m_lowo = m_lowo + m_freq * m_bando; + m_higho = m_notcho - m_lowo; + m_bando = + m_freq * m_higho + m_bando - m_drive * m_bando * m_bando * m_bando; + // (m_notcho or m_lowo or m_higho or m_bando or m_peako) + float out = 0.5 * GetSampleForFilterType(); + m_notcho = in - m_damp * m_bando; + m_lowo = m_lowo + m_freq * m_bando; + m_higho = m_notcho - m_lowo; + m_bando = + m_freq * m_higho + m_bando - m_drive * m_bando * m_bando * m_bando; + out += 0.5 * GetSampleForFilterType(); + + return out; +} + +void Filter::SetParameters(float freq, float res, float drive) { + m_fc = freq; + m_res = res; + m_drive = drive; + + // the fs*2 is because it's double sampled + m_freq = + 2.0 * std::sinf(SYNTH_PI * std::min(0.25f, m_fc / (m_fs * 2))); + + m_damp = std::min(2.0f * (1.0f - std::powf(m_res, 0.25f)), + std::min(2.0f, 2.0f / m_freq - m_freq * 0.5f)); } diff --git a/src/HighPassFilter.cpp b/src/HighPassFilter.cpp index 587d520..90879d0 100644 --- a/src/HighPassFilter.cpp +++ b/src/HighPassFilter.cpp @@ -1,23 +1,22 @@ #include "HighPassFilter.h" +#include "Settings.h" -HighPassFilter::HighPassFilter(/* args */) {} - -HighPassFilter::HighPassFilter(Filter* filter) { - m_freq = filter->GetFreq(); - m_q = filter->GetRes(); - m_order = filter->GetPeakGain(); +HighPassFilter::HighPassFilter() { + SetParameters(200, 0.1, 0.001); } -HighPassFilter::HighPassFilter(float freq, float res, float q) {} +HighPassFilter::HighPassFilter( + Filter* filter) { + SetParameters(filter->GetFreq(), filter->GetRes(), filter->GetPeakGain()); +} + +HighPassFilter::HighPassFilter(float freq, float res, + float q) { + SetParameters(freq, res, q); +} HighPassFilter::~HighPassFilter() {} -void HighPassFilter::CalculateCoefficients() { - CalculateNormals(); - m_norm = 1 / (1 + m_k / m_q + m_k * m_k); - m_a0 = 1 * m_norm; - m_a1 = -2 * m_a0; - m_a2 = m_a0; - m_b1 = 2 * (m_k * m_k - 1) * m_norm; - m_b2 = (1 - m_k / m_q + m_k * m_k) * m_norm; +float HighPassFilter::GetSampleForFilterType() { + return m_higho; } \ No newline at end of file diff --git a/src/LowPassFilter.cpp b/src/LowPassFilter.cpp index 5d745b7..73cd8dc 100644 --- a/src/LowPassFilter.cpp +++ b/src/LowPassFilter.cpp @@ -2,32 +2,21 @@ #include "Settings.h" LowPassFilter::LowPassFilter() { - // todo: defaults - m_freq = 200.f / SAMPLE_RATE; - m_q = 0.707f;//0.707f; - m_order = 0; + SetParameters(200, 0.1, 0.001); } -LowPassFilter::LowPassFilter(float freq, float res, float q) { - m_freq = freq / SAMPLE_RATE; - m_q = res; - m_order = q; +LowPassFilter::LowPassFilter( + Filter* filter) { + SetParameters(filter->GetFreq(), filter->GetRes(), filter->GetPeakGain()); } -LowPassFilter::LowPassFilter(Filter* filter) { - m_freq = filter->GetFreq(); - m_q = filter->GetRes(); - m_order = filter->GetPeakGain(); +LowPassFilter::LowPassFilter(float freq, float res, + float q) { + SetParameters(freq, res, q); } LowPassFilter::~LowPassFilter() {} -void LowPassFilter::CalculateCoefficients() { - CalculateNormals(); - m_norm = 1 / (1 + m_k / m_q + m_k * m_k); - m_a0 = m_k * m_k * m_norm; - m_a1 = 2 * m_a0; - m_a2 = m_a0; - m_b1 = 2 * (m_k * m_k - 1) * m_norm; - m_b2 = (1 - m_k / m_q + m_k * m_k) * m_norm; +float LowPassFilter::GetSampleForFilterType() { + return m_lowo; } \ No newline at end of file diff --git a/src/LowPassStateVariableFilter.cpp b/src/LowPassStateVariableFilter.cpp deleted file mode 100644 index d3488b5..0000000 --- a/src/LowPassStateVariableFilter.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "LowPassStateVariableFilter.h" - -LowPassStateVariableFilter::LowPassStateVariableFilter() { - SetParameters(200, 0.1, 0.001); -} - -LowPassStateVariableFilter::LowPassStateVariableFilter( - StateVariableFilter* filter) { - SetParameters(filter->GetFreq(), filter->GetRes(), filter->GetPeakGain()); -} - -LowPassStateVariableFilter::LowPassStateVariableFilter(float freq, float res, - float q) { - SetParameters(freq, res, q); -} - -LowPassStateVariableFilter::~LowPassStateVariableFilter() {} - -float LowPassStateVariableFilter::GetSampleForFilterType() { - return m_lowo; -} \ No newline at end of file diff --git a/src/Renderer.cpp b/src/Renderer.cpp index ece9ead..8ce824d 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -203,7 +203,7 @@ void Renderer::draw_second_panel(Rectangle& bounds) { float Renderer::DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter, const Rectangle& panel_bounds) { #define FILTER_TYPE_OPTIONS "LP;BP;HP" - StateVariableFilter* filter = synth.GetFilter(); + Filter* filter = synth.GetFilter(); float panel_y_offset = 0; // Draw Filter Panel @@ -257,7 +257,7 @@ float Renderer::DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter, // apply values to real one // todo: thrid (order) parameter // todo: why resonance changing does not work? - // todo: limit statevariablefilter lowest frequency to ~40 hz + // todo: limit filter lowest frequency to ~40 hz if (gui_filter.freq < 40.0) { gui_filter.freq = 50.0; } diff --git a/src/StateVariableFilter.cpp b/src/StateVariableFilter.cpp deleted file mode 100644 index d9d4bf7..0000000 --- a/src/StateVariableFilter.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "StateVariableFilter.h" -#include "Settings.h" -#include -#include - -StateVariableFilter::StateVariableFilter(/* args */) {} - -StateVariableFilter::~StateVariableFilter() {} - -void StateVariableFilter::Trigger() {} - -void StateVariableFilter::Release() {} - -void StateVariableFilter::Process(std::vector& samples) { - for (std::size_t i = 0; i < samples.size(); i++) { - samples[i] = Process(samples[i]); - } -} - - -float StateVariableFilter::Process(float in) { - m_notcho = in - m_damp * m_bando; - m_lowo = m_lowo + m_freq * m_bando; - m_higho = m_notcho - m_lowo; - m_bando = - m_freq * m_higho + m_bando - m_drive * m_bando * m_bando * m_bando; - // (m_notcho or m_lowo or m_higho or m_bando or m_peako) - float out = 0.5 * GetSampleForFilterType(); - m_notcho = in - m_damp * m_bando; - m_lowo = m_lowo + m_freq * m_bando; - m_higho = m_notcho - m_lowo; - m_bando = - m_freq * m_higho + m_bando - m_drive * m_bando * m_bando * m_bando; - out += 0.5 * GetSampleForFilterType(); - - return out; -} - -void StateVariableFilter::SetParameters(float freq, float res, float drive) { - m_fc = freq; - m_res = res; - m_drive = drive; - - // the fs*2 is because it's double sampled - m_freq = - 2.0 * std::sinf(SYNTH_PI * std::min(0.25f, m_fc / (m_fs * 2))); - - m_damp = std::min(2.0f * (1.0f - std::powf(m_res, 0.25f)), - std::min(2.0f, 2.0f / m_freq - m_freq * 0.5f)); -} \ No newline at end of file diff --git a/src/Synth.cpp b/src/Synth.cpp index e99c98b..4165b8a 100644 --- a/src/Synth.cpp +++ b/src/Synth.cpp @@ -4,7 +4,7 @@ #include "Logger.h" #include "OscillatorType.h" #include "Settings.h" -#include "LowPassStateVariableFilter.h" +#include "LowPassFilter.h" Synth::Synth(/* args */) { m_lfo = new LFO(); @@ -12,9 +12,7 @@ Synth::Synth(/* args */) { AddOscillator(); AddOscillator(); AddEffect(new ADSR()); - // todo: implement state-variable filters in a factory - AddEffect((StateVariableFilter*)new LowPassStateVariableFilter()); - // AddEffect(FilterFactory::GetDefaultFilter()); + AddEffect(FilterFactory::GetDefaultFilter()); for (size_t i = 0; i < STREAM_BUFFER_SIZE; i++) { float sample = 0.0f; m_out_signal.push_back(sample); @@ -44,8 +42,7 @@ void Synth::ApplyEffects() { auto* adsr = m_effects[0]; adsr->Process(m_out_signal); - StateVariableFilter* filter = (StateVariableFilter*)m_effects[1]; - // assert(filter); + Filter* filter = (Filter*)m_effects[1]; for (std::size_t i = 0; i < m_out_signal.size(); i++) { ApplyFilterLfo(); @@ -79,20 +76,14 @@ void Synth::Trigger(Note input) { TriggerNoteOnEffects(); } -// todo: fix this void Synth::ApplyFilterLfo() { float dt = m_lfo->Process(); - StateVariableFilter* filter = (StateVariableFilter*)m_effects[1]; + Filter* filter = (Filter*)m_effects[1]; float freq = filter->GetFreq(); - // todo: check formula - // write_log("LFO DT: %f\n", dt); filter->SetParameters(freq + dt, filter->GetRes(), filter->GetPeakGain()); } void Synth::Process() { - // todo: on each sample. - // in order to do that, we need to move to per-sample processing - //ApplyFilterLfo(); GetNote(); ApplyEffects(); } @@ -106,10 +97,10 @@ void Synth::Release() { void Synth::AddEffect(IEffect* fx) { m_effects.push_back(fx); } void Synth::SetFilter(FilterType type) { - StateVariableFilter* old_filter = this->GetFilter(); + Filter* old_filter = this->GetFilter(); if (!old_filter->IsSameFilterType(type)) { // todo: implement other types of state variable filters; - StateVariableFilter* new_filter = (StateVariableFilter*)new LowPassStateVariableFilter(old_filter); // FilterFactory::CreateFilter(old_filter, type); + Filter* new_filter = FilterFactory::CreateFilter(old_filter, type); delete old_filter; m_effects[1] = new_filter; }