wip: State Variable Filter (FILTER LFO WORKS NOW!!!)
This commit is contained in:
@@ -11,10 +11,3 @@ public:
|
|||||||
void SetFreq(float freq) { m_phase_dt = (this->*m_dt_function)(freq); }
|
void SetFreq(float freq) { m_phase_dt = (this->*m_dt_function)(freq); }
|
||||||
};
|
};
|
||||||
|
|
||||||
LFO::LFO(/* args */): Oscillator(Sine, 0.f, 0.5f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
LFO::~LFO()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
15
inc/LowPassStateVariableFilter.h
Normal file
15
inc/LowPassStateVariableFilter.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#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; };
|
||||||
|
};
|
||||||
35
inc/StateVariableFilter.h
Normal file
35
inc/StateVariableFilter.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#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,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ADSR.h"
|
#include "ADSR.h"
|
||||||
#include "Filter.h"
|
#include "StateVariableFilter.h"
|
||||||
#include "Adder.h"
|
#include "Adder.h"
|
||||||
#include "IEffect.h"
|
#include "IEffect.h"
|
||||||
#include "Note.h"
|
#include "Note.h"
|
||||||
#include "Oscillator.h"
|
#include "Oscillator.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "LFO.h"
|
||||||
|
|
||||||
class Synth {
|
class Synth {
|
||||||
private:
|
private:
|
||||||
@@ -15,7 +16,7 @@ class Synth {
|
|||||||
std::vector<Oscillator*> m_oscillators;
|
std::vector<Oscillator*> m_oscillators;
|
||||||
std::vector<IEffect*> m_effects;
|
std::vector<IEffect*> m_effects;
|
||||||
std::vector<float> m_out_signal;
|
std::vector<float> m_out_signal;
|
||||||
Oscillator* m_lfo;
|
LFO* m_lfo;
|
||||||
void ZeroSignal();
|
void ZeroSignal();
|
||||||
void GetNote();
|
void GetNote();
|
||||||
void TriggerNoteOnEffects();
|
void TriggerNoteOnEffects();
|
||||||
@@ -34,6 +35,6 @@ class Synth {
|
|||||||
const std::vector<Oscillator*>& GetOscillators() { return m_oscillators; }
|
const std::vector<Oscillator*>& GetOscillators() { return m_oscillators; }
|
||||||
const bool& GetIsNoteTriggered() { return is_note_triggered; }
|
const bool& GetIsNoteTriggered() { return is_note_triggered; }
|
||||||
ADSR* GetADSR() { return (ADSR*)m_effects[0]; }
|
ADSR* GetADSR() { return (ADSR*)m_effects[0]; }
|
||||||
Filter* GetFilter() { return (Filter*)m_effects[1]; }
|
StateVariableFilter* GetFilter() { return (StateVariableFilter*)m_effects[1]; }
|
||||||
void SetFilter(FilterType type);
|
void SetFilter(FilterType type);
|
||||||
};
|
};
|
||||||
10
src/LFO.cpp
Normal file
10
src/LFO.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include "LFO.h"
|
||||||
|
|
||||||
|
|
||||||
|
LFO::LFO(/* args */): Oscillator(Sine, 0.f, 0.5f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LFO::~LFO()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
LowPassFilter::LowPassFilter() {
|
LowPassFilter::LowPassFilter() {
|
||||||
// todo: defaults
|
// todo: defaults
|
||||||
m_freq = 200.f / SAMPLE_RATE;
|
m_freq = 200.f / SAMPLE_RATE;
|
||||||
m_q = 1.0f;//0.707f;
|
m_q = 0.707f;//0.707f;
|
||||||
m_order = 0;
|
m_order = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
src/LowPassStateVariableFilter.cpp
Normal file
21
src/LowPassStateVariableFilter.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#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,
|
float Renderer::DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter,
|
||||||
const Rectangle& panel_bounds) {
|
const Rectangle& panel_bounds) {
|
||||||
#define FILTER_TYPE_OPTIONS "LP;BP;HP"
|
#define FILTER_TYPE_OPTIONS "LP;BP;HP"
|
||||||
Filter* filter = synth.GetFilter();
|
StateVariableFilter* filter = synth.GetFilter();
|
||||||
float panel_y_offset = 0;
|
float panel_y_offset = 0;
|
||||||
|
|
||||||
// Draw Filter Panel
|
// Draw Filter Panel
|
||||||
@@ -257,6 +257,10 @@ float Renderer::DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter,
|
|||||||
// apply values to real one
|
// apply values to real one
|
||||||
// todo: thrid (order) parameter
|
// todo: thrid (order) parameter
|
||||||
// todo: why resonance changing does not work?
|
// todo: why resonance changing does not work?
|
||||||
|
// todo: limit statevariablefilter lowest frequency to ~40 hz
|
||||||
|
if (gui_filter.freq < 40.0) {
|
||||||
|
gui_filter.freq = 50.0;
|
||||||
|
}
|
||||||
filter->SetParameters(gui_filter.freq, filter->GetRes(),
|
filter->SetParameters(gui_filter.freq, filter->GetRes(),
|
||||||
filter->GetPeakGain());
|
filter->GetPeakGain());
|
||||||
|
|
||||||
|
|||||||
50
src/StateVariableFilter.cpp
Normal file
50
src/StateVariableFilter.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#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));
|
||||||
|
}
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
#include "Synth.h"
|
#include "Synth.h"
|
||||||
|
#include "FilterFactory.h"
|
||||||
#include "KeyBoard.h"
|
#include "KeyBoard.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "OscillatorType.h"
|
#include "OscillatorType.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "FilterFactory.h"
|
#include "LowPassStateVariableFilter.h"
|
||||||
#include "LFO.h"
|
|
||||||
|
|
||||||
Synth::Synth(/* args */) {
|
Synth::Synth(/* args */) {
|
||||||
m_lfo = new LFO();
|
m_lfo = new LFO();
|
||||||
|
m_lfo->SetFreq(5.0);
|
||||||
AddOscillator();
|
AddOscillator();
|
||||||
AddOscillator();
|
AddOscillator();
|
||||||
AddEffect(new ADSR());
|
AddEffect(new ADSR());
|
||||||
AddEffect(FilterFactory::GetDefaultFilter());
|
// todo: implement state-variable filters in a factory
|
||||||
|
AddEffect((StateVariableFilter*)new LowPassStateVariableFilter());
|
||||||
|
// AddEffect(FilterFactory::GetDefaultFilter());
|
||||||
for (size_t i = 0; i < STREAM_BUFFER_SIZE; i++) {
|
for (size_t i = 0; i < STREAM_BUFFER_SIZE; i++) {
|
||||||
float sample = 0.0f;
|
float sample = 0.0f;
|
||||||
m_out_signal.push_back(sample);
|
m_out_signal.push_back(sample);
|
||||||
@@ -38,8 +41,15 @@ void Synth::GetNote() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Synth::ApplyEffects() {
|
void Synth::ApplyEffects() {
|
||||||
for (IEffect* effect : m_effects) {
|
auto* adsr = m_effects[0];
|
||||||
effect->Process(m_out_signal);
|
adsr->Process(m_out_signal);
|
||||||
|
|
||||||
|
StateVariableFilter* filter = (StateVariableFilter*)m_effects[1];
|
||||||
|
// assert(filter);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < m_out_signal.size(); i++) {
|
||||||
|
ApplyFilterLfo();
|
||||||
|
m_out_signal[i] = filter->Process(m_out_signal[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,8 +66,7 @@ void Synth::UntriggerNoteOnEffects() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Synth::AddOscillator() {
|
void Synth::AddOscillator() {
|
||||||
m_oscillators.push_back(
|
m_oscillators.push_back(new Oscillator(OscillatorType::Sine, 0.0f, VOLUME));
|
||||||
new Oscillator(OscillatorType::Sine, 0.0f, VOLUME));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Trigger(Note input) {
|
void Synth::Trigger(Note input) {
|
||||||
@@ -73,15 +82,16 @@ void Synth::Trigger(Note input) {
|
|||||||
// todo: fix this
|
// todo: fix this
|
||||||
void Synth::ApplyFilterLfo() {
|
void Synth::ApplyFilterLfo() {
|
||||||
float dt = m_lfo->Process();
|
float dt = m_lfo->Process();
|
||||||
Filter* filter = (Filter*)m_effects[1];
|
StateVariableFilter* filter = (StateVariableFilter*)m_effects[1];
|
||||||
float freq = filter->GetFreq();
|
float freq = filter->GetFreq();
|
||||||
//todo: check formula
|
// todo: check formula
|
||||||
//filter->SetParameters(freq + dt * 0.2f, filter->GetRes(), filter->GetPeakGain());
|
// write_log("LFO DT: %f\n", dt);
|
||||||
|
filter->SetParameters(freq + dt, filter->GetRes(), filter->GetPeakGain());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Process() {
|
void Synth::Process() {
|
||||||
//todo: on each sample.
|
// todo: on each sample.
|
||||||
//in order to do that, we need to move to per-sample processing
|
// in order to do that, we need to move to per-sample processing
|
||||||
//ApplyFilterLfo();
|
//ApplyFilterLfo();
|
||||||
GetNote();
|
GetNote();
|
||||||
ApplyEffects();
|
ApplyEffects();
|
||||||
@@ -96,9 +106,10 @@ void Synth::Release() {
|
|||||||
void Synth::AddEffect(IEffect* fx) { m_effects.push_back(fx); }
|
void Synth::AddEffect(IEffect* fx) { m_effects.push_back(fx); }
|
||||||
|
|
||||||
void Synth::SetFilter(FilterType type) {
|
void Synth::SetFilter(FilterType type) {
|
||||||
Filter* old_filter = this->GetFilter();
|
StateVariableFilter* old_filter = this->GetFilter();
|
||||||
if (!old_filter->IsSameFilterType(type)) {
|
if (!old_filter->IsSameFilterType(type)) {
|
||||||
Filter* new_filter = FilterFactory::CreateFilter(old_filter, type);
|
// todo: implement other types of state variable filters;
|
||||||
|
StateVariableFilter* new_filter = (StateVariableFilter*)new LowPassStateVariableFilter(old_filter); // FilterFactory::CreateFilter(old_filter, type);
|
||||||
delete old_filter;
|
delete old_filter;
|
||||||
m_effects[1] = new_filter;
|
m_effects[1] = new_filter;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user