Compare commits
3 Commits
feature/os
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
f0e2d98c12
|
|||
|
83fe94b34d
|
|||
| 8fc7fbfd30 |
@@ -35,4 +35,7 @@ for (n = 0; n < totalSamples; n++) {
|
||||
}
|
||||
|
||||
|
||||
We can replace the sin function with any periodic function that returns an amplitude for a given phase angle. Thus, this small piece of code can be used to produce a very wide range of sounds. Functionally, it is the software equivalent of an oscillator, the basic building block of almost all synthesizers.
|
||||
We can replace the sin function with any periodic function that returns an amplitude for a given phase angle. Thus, this small piece of code can be used to produce a very wide range of sounds. Functionally, it is the software equivalent of an oscillator, the basic building block of almost all synthesizers.
|
||||
|
||||
|
||||
https://ccrma.stanford.edu/software/stk/index.html
|
||||
214
docs/StateVariableFilter.md
Normal file
214
docs/StateVariableFilter.md
Normal file
@@ -0,0 +1,214 @@
|
||||
https://www.earlevel.com/main/2003/03/02/the-digital-state-variable-filter/
|
||||
|
||||
The frequency control coefficient, f, is defined as
|
||||
|
||||

|
||||
|
||||
where Fs is the sample rate and Fc is the filter’s corner frequency you want to set. The q coefficient is defined as
|
||||
|
||||

|
||||
|
||||
where Q normally ranges from 0.5 to inifinity (where the filter oscillates).
|
||||
|
||||
|
||||
The main drawback of the digital state variable is that it becomes unstable at higher frequencies. It depends on the Q setting, but basically the upper bound of stability is about where f reaches 1, which is at one-sixth of the sample rate (8 kHz at 48 kHz). The only way around this is to oversample. A simple way to double the filter’s sample rate (and thereby double the filter’s frequency range) is to run the filter twice with the same input sample, and discard one output sample.
|
||||
|
||||
|
||||
|
||||
example with double-sampling
|
||||
|
||||
```
|
||||
input = input buffer;
|
||||
output = output buffer;
|
||||
fs = sampling frequency;
|
||||
fc = cutoff frequency normally something like:
|
||||
440.0*pow(2.0, (midi_note - 69.0)/12.0);
|
||||
res = resonance 0 to 1;
|
||||
drive = internal distortion 0 to 0.1
|
||||
freq = 2.0*sin(PI*MIN(0.25, fc/(fs*2))); // the fs*2 is because it's double sampled
|
||||
damp = MIN(2.0*(1.0 - pow(res, 0.25)), MIN(2.0, 2.0/freq - freq*0.5));
|
||||
notch = notch output
|
||||
low = low pass output
|
||||
high = high pass output
|
||||
band = band pass output
|
||||
peak = peaking output = low - high
|
||||
--
|
||||
double sampled svf loop:
|
||||
for (i=0; i<numSamples; i++)
|
||||
{
|
||||
in = input[i];
|
||||
notch = in - damp*band;
|
||||
low = low + freq*band;
|
||||
high = notch - low;
|
||||
band = freq*high + band - drive*band*band*band;
|
||||
out = 0.5*(notch or low or high or band or peak);
|
||||
notch = in - damp*band;
|
||||
low = low + freq*band;
|
||||
high = notch - low;
|
||||
band = freq*high + band - drive*band*band*band;
|
||||
out += 0.5*(same out as above);
|
||||
output[i] = out;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Also that could work as it's a voltage-controlled filter
|
||||
https://www.musicdsp.org/en/latest/Filters/24-moog-vcf.html
|
||||
|
||||
```
|
||||
//Init
|
||||
cutoff = cutoff freq in Hz
|
||||
fs = sampling frequency //(e.g. 44100Hz)
|
||||
res = resonance [0 - 1] //(minimum - maximum)
|
||||
|
||||
f = 2 * cutoff / fs; //[0 - 1]
|
||||
k = 3.6*f - 1.6*f*f -1; //(Empirical tunning)
|
||||
p = (k+1)*0.5;
|
||||
scale = e^((1-p)*1.386249;
|
||||
r = res*scale;
|
||||
y4 = output;
|
||||
|
||||
y1=y2=y3=y4=oldx=oldy1=oldy2=oldy3=0;
|
||||
|
||||
//Loop
|
||||
//--Inverted feed back for corner peaking
|
||||
x = input - r*y4;
|
||||
|
||||
//Four cascaded onepole filters (bilinear transform)
|
||||
y1=x*p + oldx*p - k*y1;
|
||||
y2=y1*p+oldy1*p - k*y2;
|
||||
y3=y2*p+oldy2*p - k*y3;
|
||||
y4=y3*p+oldy3*p - k*y4;
|
||||
|
||||
//Clipper band limited sigmoid
|
||||
y4 = y4 - (y4^3)/6;
|
||||
|
||||
oldx = x;
|
||||
oldy1 = y1;
|
||||
oldy2 = y2;
|
||||
oldy3 = y3;
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
#pragma once
|
||||
|
||||
namespace DistoCore
|
||||
{
|
||||
template<class T>
|
||||
class MoogFilter
|
||||
{
|
||||
public:
|
||||
MoogFilter();
|
||||
~MoogFilter() {};
|
||||
|
||||
T getSampleRate() const { return sampleRate; }
|
||||
void setSampleRate(T fs) { sampleRate = fs; calc(); }
|
||||
T getResonance() const { return resonance; }
|
||||
void setResonance(T filterRezo) { resonance = filterRezo; calc(); }
|
||||
T getCutoff() const { return cutoff; }
|
||||
T getCutoffHz() const { return cutoff * sampleRate * 0.5; }
|
||||
void setCutoff(T filterCutoff) { cutoff = filterCutoff; calc(); }
|
||||
|
||||
void init();
|
||||
void calc();
|
||||
T process(T input);
|
||||
// filter an input sample using normalized params
|
||||
T filter(T input, T cutoff, T resonance);
|
||||
|
||||
protected:
|
||||
// cutoff and resonance [0 - 1]
|
||||
T cutoff;
|
||||
T resonance;
|
||||
T sampleRate;
|
||||
T fs;
|
||||
T y1,y2,y3,y4;
|
||||
T oldx;
|
||||
T oldy1,oldy2,oldy3;
|
||||
T x;
|
||||
T r;
|
||||
T p;
|
||||
T k;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct Moog-filter.
|
||||
*/
|
||||
template<class T>
|
||||
MoogFilter<T>::MoogFilter()
|
||||
: sampleRate(T(44100.0))
|
||||
, cutoff(T(1.0))
|
||||
, resonance(T(0.0))
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize filter buffers.
|
||||
*/
|
||||
template<class T>
|
||||
void MoogFilter<T>::init()
|
||||
{
|
||||
// initialize values
|
||||
y1=y2=y3=y4=oldx=oldy1=oldy2=oldy3=T(0.0);
|
||||
calc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate coefficients.
|
||||
*/
|
||||
template<class T>
|
||||
void MoogFilter<T>::calc()
|
||||
{
|
||||
// TODO: replace with your constant
|
||||
const double kPi = 3.1415926535897931;
|
||||
|
||||
// empirical tuning
|
||||
p = cutoff * (T(1.8) - T(0.8) * cutoff);
|
||||
// k = p + p - T(1.0);
|
||||
// A much better tuning seems to be:
|
||||
k = T(2.0) * sin(cutoff * kPi * T(0.5)) - T(1.0);
|
||||
|
||||
T t1 = (T(1.0) - p) * T(1.386249);
|
||||
T t2 = T(12.0) + t1 * t1;
|
||||
r = resonance * (t2 + T(6.0) * t1) / (t2 - T(6.0) * t1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Process single sample.
|
||||
*/
|
||||
template<class T>
|
||||
T MoogFilter<T>::process(T input)
|
||||
{
|
||||
// process input
|
||||
x = input - r * y4;
|
||||
|
||||
// four cascaded one-pole filters (bilinear transform)
|
||||
y1 = x * p + oldx * p - k * y1;
|
||||
y2 = y1 * p + oldy1 * p - k * y2;
|
||||
y3 = y2 * p + oldy2 * p - k * y3;
|
||||
y4 = y3 * p + oldy3 * p - k * y4;
|
||||
|
||||
// clipper band limited sigmoid
|
||||
y4 -= (y4 * y4 * y4) / T(6.0);
|
||||
|
||||
oldx = x; oldy1 = y1; oldy2 = y2; oldy3 = y3;
|
||||
|
||||
return y4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter single sample using specified params.
|
||||
*/
|
||||
template<class T>
|
||||
T MoogFilter<T>::filter(T input, T filterCutoff, T filterRezo)
|
||||
{
|
||||
// set params first
|
||||
cutoff = filterCutoff;
|
||||
resonance = filterRezo;
|
||||
calc();
|
||||
|
||||
return process(input);
|
||||
}
|
||||
}
|
||||
```
|
||||
BIN
docs/image-1.png
Normal file
BIN
docs/image-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
BIN
docs/image.png
Normal file
BIN
docs/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
19
docs/matrix.md
Normal file
19
docs/matrix.md
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
// signal -> adsr -> filter
|
||||
// ^ ^ ^
|
||||
// | | |
|
||||
// can be modulated
|
||||
// by lfo, or adsr
|
||||
|
||||
|
||||
|
||||
у каждого из них должна быть ручка для изменения состояния
|
||||
на каждом семпле?????
|
||||
|
||||
|
||||
|
||||
багует сам алгоритм изменения частоты, либо алгоритм пересчета коэфициентов фильтра
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#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; };
|
||||
};
|
||||
77
inc/Filter.h
77
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,45 @@ 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; };
|
||||
};
|
||||
|
||||
class BandPassFilter : public Filter {
|
||||
protected:
|
||||
float GetSampleForFilterType() override;
|
||||
|
||||
public:
|
||||
BandPassFilter();
|
||||
BandPassFilter(Filter* filter);
|
||||
BandPassFilter(float freq, float res, float q);
|
||||
~BandPassFilter();
|
||||
bool IsSameFilterType(FilterType type) override { return type == BandPass; };
|
||||
};
|
||||
|
||||
class HighPassFilter : public Filter {
|
||||
protected:
|
||||
float GetSampleForFilterType() override;
|
||||
|
||||
public:
|
||||
HighPassFilter();
|
||||
HighPassFilter(Filter* filter);
|
||||
HighPassFilter(float freq, float res, float q);
|
||||
~HighPassFilter();
|
||||
bool IsSameFilterType(FilterType type) override { return type == HighPass; };
|
||||
};
|
||||
|
||||
class LowPassFilter : public Filter {
|
||||
protected:
|
||||
float GetSampleForFilterType() override;
|
||||
|
||||
public:
|
||||
LowPassFilter();
|
||||
LowPassFilter(Filter* filter);
|
||||
LowPassFilter(float freq, float res, float q);
|
||||
~LowPassFilter();
|
||||
bool IsSameFilterType(FilterType type) override { return type == LowPass; };
|
||||
};
|
||||
@@ -1,8 +1,5 @@
|
||||
#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) {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#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; };
|
||||
};
|
||||
@@ -11,10 +11,3 @@ public:
|
||||
void SetFreq(float freq) { m_phase_dt = (this->*m_dt_function)(freq); }
|
||||
};
|
||||
|
||||
LFO::LFO(/* args */): Oscillator(Sine, 0.f, 0.5f)
|
||||
{
|
||||
}
|
||||
|
||||
LFO::~LFO()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#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; };
|
||||
};
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Oscillator.h"
|
||||
#include "Settings.h"
|
||||
#include <vector>
|
||||
#include "LFO.h"
|
||||
|
||||
class Synth {
|
||||
private:
|
||||
@@ -15,7 +16,7 @@ class Synth {
|
||||
std::vector<Oscillator*> m_oscillators;
|
||||
std::vector<IEffect*> m_effects;
|
||||
std::vector<float> m_out_signal;
|
||||
Oscillator* m_lfo;
|
||||
LFO* m_lfo;
|
||||
void ZeroSignal();
|
||||
void GetNote();
|
||||
void TriggerNoteOnEffects();
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
#include "BandPassFilter.h"
|
||||
#include "Filter.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 "Filter.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;
|
||||
}
|
||||
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()
|
||||
{
|
||||
}
|
||||
@@ -1,33 +1,22 @@
|
||||
#include "LowPassFilter.h"
|
||||
#include "Filter.h"
|
||||
#include "Settings.h"
|
||||
|
||||
LowPassFilter::LowPassFilter() {
|
||||
// todo: defaults
|
||||
m_freq = 200.f / SAMPLE_RATE;
|
||||
m_q = 1.0f;//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;
|
||||
}
|
||||
@@ -257,6 +257,10 @@ 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 filter lowest frequency to ~40 hz
|
||||
if (gui_filter.freq < 40.0) {
|
||||
gui_filter.freq = 50.0;
|
||||
}
|
||||
filter->SetParameters(gui_filter.freq, filter->GetRes(),
|
||||
filter->GetPeakGain());
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "Synth.h"
|
||||
#include "FilterFactory.h"
|
||||
#include "KeyBoard.h"
|
||||
#include "Logger.h"
|
||||
#include "OscillatorType.h"
|
||||
#include "Settings.h"
|
||||
#include "FilterFactory.h"
|
||||
#include "LFO.h"
|
||||
|
||||
Synth::Synth(/* args */) {
|
||||
m_lfo = new LFO();
|
||||
m_lfo->SetFreq(5.0);
|
||||
AddOscillator();
|
||||
AddOscillator();
|
||||
AddEffect(new ADSR());
|
||||
@@ -38,8 +38,14 @@ void Synth::GetNote() {
|
||||
}
|
||||
|
||||
void Synth::ApplyEffects() {
|
||||
for (IEffect* effect : m_effects) {
|
||||
effect->Process(m_out_signal);
|
||||
auto* adsr = m_effects[0];
|
||||
adsr->Process(m_out_signal);
|
||||
|
||||
Filter* filter = (Filter*)m_effects[1];
|
||||
|
||||
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 +62,7 @@ void Synth::UntriggerNoteOnEffects() {
|
||||
}
|
||||
|
||||
void Synth::AddOscillator() {
|
||||
m_oscillators.push_back(
|
||||
new Oscillator(OscillatorType::Sine, 0.0f, VOLUME));
|
||||
m_oscillators.push_back(new Oscillator(OscillatorType::Sine, 0.0f, VOLUME));
|
||||
}
|
||||
|
||||
void Synth::Trigger(Note input) {
|
||||
@@ -70,19 +75,14 @@ void Synth::Trigger(Note input) {
|
||||
TriggerNoteOnEffects();
|
||||
}
|
||||
|
||||
// todo: fix this
|
||||
void Synth::ApplyFilterLfo() {
|
||||
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());
|
||||
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();
|
||||
}
|
||||
@@ -98,6 +98,7 @@ void Synth::AddEffect(IEffect* fx) { m_effects.push_back(fx); }
|
||||
void Synth::SetFilter(FilterType type) {
|
||||
Filter* old_filter = this->GetFilter();
|
||||
if (!old_filter->IsSameFilterType(type)) {
|
||||
// todo: implement other types of state variable filters;
|
||||
Filter* new_filter = FilterFactory::CreateFilter(old_filter, type);
|
||||
delete old_filter;
|
||||
m_effects[1] = new_filter;
|
||||
|
||||
Reference in New Issue
Block a user