[feat]: Filter (#18)

closes #16

Reviewed-on: #18
This commit is contained in:
2023-09-10 00:56:47 +03:00
parent 868a59da0e
commit bb3ccc296a
22 changed files with 372 additions and 48 deletions

View File

@@ -3,18 +3,14 @@
#include "Ramp.h" #include "Ramp.h"
#include <cstddef> #include <cstddef>
struct ADSRParameters { class ADSR : public Effect {
float attack_time; // Attack time in seconds
float decay_time; // Decay time in seconds
float sustain_level; // Sustain level (0 to 1)
float release_time;
};
enum ADSRState { sOff, sAttack, sDecay, sSustain, sRelease }; enum ADSRState { sOff, sAttack, sDecay, sSustain, sRelease };
class ADSR : public Effect {
private: private:
ADSRParameters m_parameters; float m_attack_time;
float m_decay_time;
float m_sustain_level;
float m_release_time;
ADSRState m_state; ADSRState m_state;
Ramp* m_ramp; Ramp* m_ramp;
@@ -26,7 +22,6 @@ class ADSR : public Effect {
public: public:
ADSR(/* args */); ADSR(/* args */);
ADSR(ADSRParameters param);
~ADSR(); ~ADSR();
void Trigger() override; void Trigger() override;
void Release() override; void Release() override;

View File

@@ -13,7 +13,7 @@ struct Adder {
for (size_t i = 0; i < sample_count; i++) { for (size_t i = 0; i < sample_count; i++) {
float sample = 0.0f; float sample = 0.0f;
for (Oscillator* osc : oscillators) { for (Oscillator* osc : oscillators) {
sample += osc->GenerateSample(1.f); sample += osc->Process();
} }
signal[i] = sample; signal[i] = sample;

View File

@@ -13,11 +13,11 @@ class Application {
int m_sound_played_count; int m_sound_played_count;
Note* m_current_note; Note* m_current_note;
Renderer m_renderer; Renderer m_renderer;
bool detect_note_pressed(Note* note); bool DetectNotePressed(Note* note);
void init_synth(); void InitSynth();
void init_audio(); void InitAudio();
void update_on_note_input(); void UpdateOnNoteInput();
void play_buffered_audio(); void PlayBufferedAudio();
public: public:
Application(/* args */); Application(/* args */);

14
inc/BandPassFilter.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "Filter.h"
class BandPassFilter : public Filter {
private:
void CalculateCoefficients() override;
public:
BandPassFilter(Filter* filter);
BandPassFilter(float freq, float res, float q);
BandPassFilter(/* args */);
~BandPassFilter();
bool IsSameFilterType(FilterType type) override { return type == BandPass; };
};

35
inc/Filter.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include "Effect.h"
enum FilterType {
LowPass,
BandPass,
HighPass
};
class Filter : public Effect {
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(){};
public:
Filter(/* args */);
virtual ~Filter();
void Trigger() override;
void Release() override;
float Process(float in);
void Process(std::vector<float>& samples) override;
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; };
};

29
inc/FilterFactory.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include "Filter.h"
#include "LowPassFilter.h"
#include "BandPassFilter.h"
#include "HighPassFilter.h"
struct FilterFactory {
static Filter* CreateFilter(Filter* old_filter, FilterType new_type) {
Filter* new_filter;
switch (new_type) {
case LowPass:
new_filter = new LowPassFilter(old_filter);
break;
case BandPass:
new_filter = new BandPassFilter(old_filter);
break;
case HighPass:
new_filter = new HighPassFilter(old_filter);
break;
default:
break;
}
return new_filter;
}
static Filter* GetDefaultFilter() {
return new LowPassFilter();
}
};

14
inc/HighPassFilter.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "Filter.h"
class HighPassFilter : public Filter {
private:
void CalculateCoefficients() override;
public:
HighPassFilter();
HighPassFilter(Filter* filter);
HighPassFilter(float freq, float res, float q);
~HighPassFilter();
bool IsSameFilterType(FilterType type) override { return type == HighPass; };
};

View File

@@ -7,7 +7,7 @@
class KeyBoard { class KeyBoard {
private: private:
static int get_semitone_shift_internal(const char* root_note, static int GetSemitoneShiftInternal(const char* root_note,
char* target_note) { char* target_note) {
const char* pitch_classes[12] = {"C", "C#", "D", "D#", "E", "F", const char* pitch_classes[12] = {"C", "C#", "D", "D#", "E", "F",
"F#", "G", "G#", "A", "A#", "B"}; "F#", "G", "G#", "A", "A#", "B"};
@@ -65,7 +65,7 @@ class KeyBoard {
char* target_note_cstr = new char[target_note.length() + 1]; char* target_note_cstr = new char[target_note.length() + 1];
strcpy(target_note_cstr, target_note.c_str()); strcpy(target_note_cstr, target_note.c_str());
int result = get_semitone_shift_internal("A4", target_note_cstr); int result = GetSemitoneShiftInternal("A4", target_note_cstr);
delete[] target_note_cstr; delete[] target_note_cstr;
return result; return result;

15
inc/LowPassFilter.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include "Filter.h"
class LowPassFilter : public Filter {
protected:
void CalculateCoefficients() override;
public:
LowPassFilter();
LowPassFilter(Filter* filter);
LowPassFilter(float freq, float res, float q);
~LowPassFilter();
bool IsSameFilterType(FilterType type) override { return type == LowPass; };
};

View File

@@ -31,5 +31,5 @@ class Oscillator {
float GetFreq() { return m_freq; } float GetFreq() { return m_freq; }
void SetFreq(float freq); void SetFreq(float freq);
void Reset(); void Reset();
float GenerateSample(float duration); float Process();
}; };

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "ADSR.h" #include "ADSR.h"
#include "Filter.h"
#include "Synth.h" #include "Synth.h"
#include "SynthGuiState.h" #include "SynthGuiState.h"
#include "raylib.h" #include "raylib.h"
@@ -19,6 +20,9 @@ class Renderer {
void draw_signal(Synth& synth, SynthGuiState& synth_gui); void draw_signal(Synth& synth, SynthGuiState& synth_gui);
void draw_adsr_panel(ADSR* adsr, ADSRGuiState& gui_adsr, void draw_adsr_panel(ADSR* adsr, ADSRGuiState& gui_adsr,
const Rectangle& panel_bounds, float panel_y_offset); const Rectangle& panel_bounds, float panel_y_offset);
void draw_second_panel(Rectangle& bounds);
float DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter,
const Rectangle& panel_bounds);
public: public:
Renderer(/* args */); Renderer(/* args */);

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "ADSR.h" #include "ADSR.h"
#include "Filter.h"
#include "Adder.h" #include "Adder.h"
#include "Effect.h" #include "Effect.h"
#include "Note.h" #include "Note.h"
@@ -14,13 +15,14 @@ class Synth {
std::vector<Oscillator*> m_oscillators; std::vector<Oscillator*> m_oscillators;
std::vector<Effect*> m_effects; std::vector<Effect*> m_effects;
std::vector<float> m_out_signal; std::vector<float> m_out_signal;
Oscillator* m_lfo;
void zero_signal(); void zero_signal();
void get_note(); void get_note();
void trigger_note_on_effects(); void trigger_note_on_effects();
void untrigger_note_on_effects(); void untrigger_note_on_effects();
void apply_effects(); void apply_effects();
void add_oscillator(); void add_oscillator();
void apply_filter_lfo();
public: public:
Synth(/* args */); Synth(/* args */);
~Synth(); ~Synth();
@@ -32,4 +34,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]; }
void SetFilter(FilterType type);
}; };

View File

@@ -2,6 +2,7 @@
#include "OscillatorType.h" #include "OscillatorType.h"
#include "raygui.h" #include "raygui.h"
#include <vector> #include <vector>
#include "Filter.h"
struct OscillatorGuiState { struct OscillatorGuiState {
float volume; float volume;
@@ -18,7 +19,15 @@ struct ADSRGuiState {
float release; float release;
}; };
struct FilterGuiState {
float freq;
float res; //todo: res
FilterType type;
bool is_dropdown_open;
};
struct SynthGuiState { struct SynthGuiState {
std::vector<OscillatorGuiState*> oscillators; std::vector<OscillatorGuiState*> oscillators;
ADSRGuiState adsr; ADSRGuiState adsr;
FilterGuiState filter;
}; };

View File

@@ -3,15 +3,13 @@
#include "Settings.h" #include "Settings.h"
ADSR::ADSR(/* args */) { ADSR::ADSR(/* args */) {
m_parameters.attack_time = 1.f; m_attack_time = 1.f;
m_parameters.decay_time = 0.4f; m_decay_time = 0.4f;
m_parameters.sustain_level = 0.6f; m_sustain_level = 0.6f;
m_parameters.release_time = 0.8f; m_release_time = 0.8f;
m_ramp = new Ramp(0, SAMPLE_RATE); m_ramp = new Ramp(0, SAMPLE_RATE);
} }
ADSR::ADSR(ADSRParameters param) { m_parameters = param; }
ADSR::~ADSR() { delete m_ramp; } ADSR::~ADSR() { delete m_ramp; }
bool ADSR::is_attack_elapsed() { bool ADSR::is_attack_elapsed() {
@@ -31,8 +29,7 @@ void ADSR::recheck_state() {
case sAttack: case sAttack:
if (is_attack_elapsed()) { if (is_attack_elapsed()) {
m_state = sDecay; m_state = sDecay;
m_ramp->RampTo(m_parameters.sustain_level, m_ramp->RampTo(m_sustain_level, m_decay_time);
m_parameters.decay_time);
} }
break; break;
case sDecay: case sDecay:
@@ -58,7 +55,7 @@ void ADSR::process_sample(float* sample) {
} else if (m_state == sDecay) { } else if (m_state == sDecay) {
(*sample) = (*sample) * m_ramp->Process(); (*sample) = (*sample) * m_ramp->Process();
} else if (m_state == sSustain) { } else if (m_state == sSustain) {
(*sample) = (*sample) * m_parameters.sustain_level; (*sample) = (*sample) * m_sustain_level;
} else if (m_state == sRelease) { } else if (m_state == sRelease) {
(*sample) = (*sample) * m_ramp->Process(); (*sample) = (*sample) * m_ramp->Process();
} }
@@ -72,13 +69,13 @@ void ADSR::Trigger() {
m_state = sAttack; m_state = sAttack;
}; };
m_ramp->RampTo(1, m_parameters.attack_time); m_ramp->RampTo(1, m_attack_time);
} }
void ADSR::Release() { void ADSR::Release() {
write_log("Unset ADSR\n"); write_log("Unset ADSR\n");
m_state = sRelease; m_state = sRelease;
m_ramp->RampTo(0, m_parameters.release_time); m_ramp->RampTo(0, m_release_time);
} }
void ADSR::Process(std::vector<float>& samples) { void ADSR::Process(std::vector<float>& samples) {
@@ -90,8 +87,8 @@ void ADSR::Process(std::vector<float>& samples) {
void ADSR::SetParameters(float attack, float decay, float sustain, void ADSR::SetParameters(float attack, float decay, float sustain,
float release) { float release) {
m_parameters.attack_time = attack; m_attack_time = attack;
m_parameters.decay_time = decay; m_decay_time = decay;
m_parameters.sustain_level = sustain; m_sustain_level = sustain;
m_parameters.release_time = release; m_release_time = release;
} }

View File

@@ -4,8 +4,8 @@
#include <string> #include <string>
Application::Application(/* args */) { Application::Application(/* args */) {
init_synth(); InitSynth();
init_audio(); InitAudio();
} }
Application::~Application() { Application::~Application() {
@@ -19,7 +19,7 @@ Application::~Application() {
} }
} }
void Application::init_audio() { void Application::InitAudio() {
m_sound_played_count = 0; m_sound_played_count = 0;
InitAudioDevice(); InitAudioDevice();
@@ -31,7 +31,7 @@ void Application::init_audio() {
PlayAudioStream(m_synth_stream); PlayAudioStream(m_synth_stream);
} }
void Application::init_synth() { void Application::InitSynth() {
std::string* nameString = new std::string(std::string(new char[3])); std::string* nameString = new std::string(std::string(new char[3]));
m_current_note = new Note{.length = 1, .name = (*nameString)}; m_current_note = new Note{.length = 1, .name = (*nameString)};
m_current_note->name.assign("G4"); m_current_note->name.assign("G4");
@@ -50,7 +50,7 @@ void Application::init_synth() {
} }
} }
bool Application::detect_note_pressed(Note* note) { bool Application::DetectNotePressed(Note* note) {
std::size_t is_pressed = 0; std::size_t is_pressed = 0;
note->length = 8; note->length = 8;
if (IsKeyDown(KEY_A)) { if (IsKeyDown(KEY_A)) {
@@ -89,8 +89,8 @@ bool is_note_up() {
IsKeyUp(KEY_D) || IsKeyUp(KEY_E) || IsKeyUp(KEY_F) || IsKeyUp(KEY_G); IsKeyUp(KEY_D) || IsKeyUp(KEY_E) || IsKeyUp(KEY_F) || IsKeyUp(KEY_G);
} }
void Application::update_on_note_input() { void Application::UpdateOnNoteInput() {
if (detect_note_pressed(m_current_note)) { if (DetectNotePressed(m_current_note)) {
if (!m_synth.GetIsNoteTriggered()) { if (!m_synth.GetIsNoteTriggered()) {
m_synth.Trigger((*m_current_note)); m_synth.Trigger((*m_current_note));
} }
@@ -103,9 +103,9 @@ void Application::update_on_note_input() {
} }
// Play ring-buffered audio // Play ring-buffered audio
void Application::play_buffered_audio() { void Application::PlayBufferedAudio() {
if (IsAudioStreamProcessed(m_synth_stream)) { if (IsAudioStreamProcessed(m_synth_stream)) {
update_on_note_input(); UpdateOnNoteInput();
UpdateAudioStream(m_synth_stream, m_synth.GetOutSignal().data(), UpdateAudioStream(m_synth_stream, m_synth.GetOutSignal().data(),
STREAM_BUFFER_SIZE); STREAM_BUFFER_SIZE);
} }
@@ -114,7 +114,7 @@ void Application::play_buffered_audio() {
void Application::Run() { void Application::Run() {
// Main game loop // Main game loop
while (!WindowShouldClose()) { while (!WindowShouldClose()) {
play_buffered_audio(); PlayBufferedAudio();
m_renderer.Draw(m_synth, m_synth_gui_state); m_renderer.Draw(m_synth, m_synth_gui_state);
} }
} }

23
src/BandPassFilter.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "BandPassFilter.h"
BandPassFilter::BandPassFilter(/* args */) {}
BandPassFilter::BandPassFilter(Filter* filter) {
m_freq = filter->GetFreq();
m_q = filter->GetRes();
m_order = filter->GetPeakGain();
}
BandPassFilter::BandPassFilter(float freq, float res, float 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;
}

36
src/Filter.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include "Filter.h"
#include "Settings.h"
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;
}

23
src/HighPassFilter.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "HighPassFilter.h"
HighPassFilter::HighPassFilter(/* args */) {}
HighPassFilter::HighPassFilter(Filter* filter) {
m_freq = filter->GetFreq();
m_q = filter->GetRes();
m_order = filter->GetPeakGain();
}
HighPassFilter::HighPassFilter(float freq, float res, float 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;
}

33
src/LowPassFilter.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include "LowPassFilter.h"
#include "Settings.h"
LowPassFilter::LowPassFilter() {
// todo: defaults
m_freq = 200.f / SAMPLE_RATE;
m_q = 1.0f;//0.707f;
m_order = 0;
}
LowPassFilter::LowPassFilter(float freq, float res, float q) {
m_freq = freq / SAMPLE_RATE;
m_q = res;
m_order = q;
}
LowPassFilter::LowPassFilter(Filter* filter) {
m_freq = filter->GetFreq();
m_q = filter->GetRes();
m_order = filter->GetPeakGain();
}
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;
}

View File

@@ -45,7 +45,7 @@ void Oscillator::SetFreq(float freq) {
m_phase_dt = (this->*m_dt_function)(freq); m_phase_dt = (this->*m_dt_function)(freq);
} }
float Oscillator::GenerateSample(float duration) { float Oscillator::Process() {
return (this->*m_osc_function)() * m_volume; return (this->*m_osc_function)() * m_volume;
} }

View File

@@ -185,6 +185,73 @@ void Renderer::draw_adsr_panel(ADSR* adsr, ADSRGuiState& gui_adsr,
gui_adsr.release); gui_adsr.release);
} }
void Renderer::draw_second_panel(Rectangle& bounds) {
bounds.x += bounds.width;
GuiPanel(bounds, "");
}
float Renderer::DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter,
const Rectangle& panel_bounds) {
#define FILTER_TYPE_OPTIONS "LP;BP;HP"
Filter* filter = synth.GetFilter();
float panel_y_offset = 0;
// Draw Filter Panel
const int osc_panel_width = panel_bounds.width - 20;
const int osc_panel_height = 100;
const int osc_panel_x = panel_bounds.x + 10;
const int osc_panel_y = panel_bounds.y + 50;
panel_y_offset += osc_panel_height + 5;
GuiPanel((Rectangle){(float)osc_panel_x, (float)osc_panel_y,
(float)osc_panel_width, (float)osc_panel_height},
"Filter");
const float slider_padding = 50.f;
const float el_spacing = 5.f;
Rectangle el_rect = {.x = (float)osc_panel_x + slider_padding + 30,
.y = (float)osc_panel_y + 10,
.width = (float)osc_panel_width - (slider_padding * 2),
.height = 25.f};
// Frequency slider
float freq = log10f(gui_filter.freq);
char freq_slider_label[32];
snprintf(freq_slider_label, 10, "%.1f hz", powf(10.f, freq));
freq = GuiSlider(el_rect, freq_slider_label, "", freq, 0.f, 4.f);
gui_filter.freq = powf(10.f, freq);
el_rect.y += el_rect.height + el_spacing;
//todo: implement that when Res will be fixed
// Resonance slider
// float res = gui_filter.res;
// char res_slider_label[32];
// snprintf(res_slider_label, 7, "%.1f u", res);
// res = GuiSlider(el_rect, res_slider_label, "", res, 0.0f, 1.0f);
// gui_filter.res = res;
// el_rect.y += el_rect.height + el_spacing;
// Shape select
int shape_index = (int)(gui_filter.type);
bool is_dropdown_click =
GuiDropdownBox(el_rect, FILTER_TYPE_OPTIONS,
&shape_index, gui_filter.is_dropdown_open);
if (is_dropdown_click) {
write_log("Dropdown clicked!\n");
gui_filter.is_dropdown_open = !gui_filter.is_dropdown_open;
gui_filter.type = (FilterType)(shape_index);
// APPLY STATE TO REAL SYNTH
synth.SetFilter(gui_filter.type);
}
// apply values to real one
// todo: thrid (order) parameter
// todo: why resonance changing does not work?
filter->SetParameters(gui_filter.freq, filter->GetRes(), filter->GetPeakGain());
return panel_y_offset;
}
void Renderer::draw_ui(Synth& synth, SynthGuiState& synth_gui) { void Renderer::draw_ui(Synth& synth, SynthGuiState& synth_gui) {
Rectangle panel_bounds = {.x = 0, Rectangle panel_bounds = {.x = 0,
.y = 0, .y = 0,
@@ -200,4 +267,7 @@ void Renderer::draw_ui(Synth& synth, SynthGuiState& synth_gui) {
draw_adsr_panel(synth.GetADSR(), synth_gui.adsr, panel_bounds, draw_adsr_panel(synth.GetADSR(), synth_gui.adsr, panel_bounds,
panel_y_offset); panel_y_offset);
draw_oscillators_shape_inputs(oscillators, gui_oscillators); draw_oscillators_shape_inputs(oscillators, gui_oscillators);
draw_second_panel(panel_bounds);
DrawFilterPanel(synth, synth_gui.filter, panel_bounds);
} }

View File

@@ -1,14 +1,16 @@
#include "Synth.h" #include "Synth.h"
#include "ADSR.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"
Synth::Synth(/* args */) { Synth::Synth(/* args */) {
m_lfo = new Oscillator(OscillatorType::Sine, 5.f, VOLUME);
add_oscillator(); add_oscillator();
add_oscillator(); add_oscillator();
AddEffect(new ADSR()); AddEffect(new ADSR());
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);
@@ -68,7 +70,19 @@ void Synth::Trigger(Note input) {
trigger_note_on_effects(); trigger_note_on_effects();
} }
// todo: fix this
void Synth::apply_filter_lfo() {
float dt = m_lfo->Process();
Filter* filter = (Filter*)m_effects[1];
float freq = filter->GetFreq();
//todo: check formula
//filter->SetParameters(freq + dt * 0.2f, filter->GetRes(), filter->GetPeakGain());
}
void Synth::Process() { void Synth::Process() {
//todo: on each sample.
//in order to do that, we need to move to per-sample processing
apply_filter_lfo();
get_note(); get_note();
apply_effects(); apply_effects();
} }
@@ -80,3 +94,12 @@ void Synth::Release() {
} }
void Synth::AddEffect(Effect* fx) { m_effects.push_back(fx); } void Synth::AddEffect(Effect* fx) { m_effects.push_back(fx); }
void Synth::SetFilter(FilterType type) {
Filter* old_filter = this->GetFilter();
if (!old_filter->IsSameFilterType(type)) {
Filter* new_filter = FilterFactory::CreateFilter(old_filter, type);
delete old_filter;
m_effects[1] = new_filter;
}
}