feat: all filter types
This commit is contained in:
@@ -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; };
|
||||
};
|
||||
|
||||
|
||||
|
||||
41
inc/Filter.h
41
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<float>& 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; };
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#include "Filter.h"
|
||||
|
||||
class HighPassFilter : public Filter {
|
||||
private:
|
||||
void CalculateCoefficients() override;
|
||||
protected:
|
||||
float GetSampleForFilterType() override;
|
||||
|
||||
public:
|
||||
HighPassFilter();
|
||||
|
||||
@@ -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; };
|
||||
};
|
||||
|
||||
|
||||
@@ -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; };
|
||||
};
|
||||
@@ -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<float>& 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; };
|
||||
};
|
||||
@@ -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<Oscillator*>& 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);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,36 +1,50 @@
|
||||
#include "Filter.h"
|
||||
#include "Settings.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
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<float>& 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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "StateVariableFilter.h"
|
||||
#include "Settings.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
StateVariableFilter::StateVariableFilter(/* args */) {}
|
||||
|
||||
StateVariableFilter::~StateVariableFilter() {}
|
||||
|
||||
void StateVariableFilter::Trigger() {}
|
||||
|
||||
void StateVariableFilter::Release() {}
|
||||
|
||||
void StateVariableFilter::Process(std::vector<float>& 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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user