|
|
|
@ -17,25 +17,27 @@ |
|
|
|
|
//
|
|
|
|
|
// State Variable Filter used in Tape Delay
|
|
|
|
|
//
|
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
|
|
#include "fx.h" |
|
|
|
|
#include "fx_components.h" |
|
|
|
|
|
|
|
|
|
class StateVariableFilter : public FXElement |
|
|
|
|
{ |
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(StateVariableFilter); |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
typedef enum |
|
|
|
|
enum FilterMode |
|
|
|
|
{ |
|
|
|
|
LPF, // Low pass filter
|
|
|
|
|
HPF, // High pass filter
|
|
|
|
|
BPF // Band pass filter
|
|
|
|
|
} Type; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
StateVariableFilter(float32_t sampling_rate, Type type, float32_t cutoff); |
|
|
|
|
StateVariableFilter(float32_t sampling_rate, FilterMode mode, float32_t cutoff); |
|
|
|
|
virtual ~StateVariableFilter(); |
|
|
|
|
|
|
|
|
|
void setFilterType(Type type); |
|
|
|
|
void setFilterMode(FilterMode mode); |
|
|
|
|
void setGainDB(float32_t gainDB); |
|
|
|
|
void setCutoff(float32_t cutoff); |
|
|
|
|
void setResonance(float32_t resonance); |
|
|
|
@ -46,7 +48,7 @@ public: |
|
|
|
|
private: |
|
|
|
|
void updateCoefficients(); |
|
|
|
|
|
|
|
|
|
Type type_; |
|
|
|
|
FilterMode mode_; |
|
|
|
|
float32_t gain_; |
|
|
|
|
float32_t cutoff_; |
|
|
|
|
float32_t resonance_; |
|
|
|
@ -70,6 +72,7 @@ private: |
|
|
|
|
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
|
|
|
|
|
|
|
|
|
SS_RESET(ss, precision, std::left); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "mode_"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "gain_"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "cutoff_"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "resonance_"); |
|
|
|
@ -91,9 +94,11 @@ private: |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
out << "\t" << ss.str() << std::endl; |
|
|
|
|
|
|
|
|
|
SS_RESET(ss, precision, std::left); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->mode_); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->gain_); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->cutoff_); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->resonance_); |
|
|
|
@ -123,4 +128,235 @@ private: |
|
|
|
|
|
|
|
|
|
return nb_errors; |
|
|
|
|
) |
|
|
|
|
}; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class SVF : public FXElement |
|
|
|
|
{ |
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SVF); |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
enum FrequencyApproximation |
|
|
|
|
{ |
|
|
|
|
FrequencyExact, |
|
|
|
|
FrequencyAccurate, |
|
|
|
|
FrequencyFast, |
|
|
|
|
FrequencyDirty |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
enum FilterMode |
|
|
|
|
{ |
|
|
|
|
SVF_LP, |
|
|
|
|
SVF_BP, |
|
|
|
|
SVF_BP_NORMALIZED, |
|
|
|
|
SVF_HP |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
SVF(float32_t sampling_frequency, FilterMode mode = FilterMode::SVF_LP) :
|
|
|
|
|
FXElement(sampling_frequency), |
|
|
|
|
Mode(mode), |
|
|
|
|
g_(0.0f), |
|
|
|
|
r_(0.0f), |
|
|
|
|
h_(0.0f) |
|
|
|
|
{ |
|
|
|
|
this->reset(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
virtual ~SVF() |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline virtual void reset() override |
|
|
|
|
{ |
|
|
|
|
memset(this->state1_, 0, StereoChannels::kNumChannels * sizeof(float32_t)); |
|
|
|
|
memset(this->state2_, 0, StereoChannels::kNumChannels * sizeof(float32_t)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override |
|
|
|
|
{ |
|
|
|
|
float32_t hp, bp, lp; |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
hp = (inL - this->r_ * this->state1_[StereoChannels::Left ] - this->g_ * this->state1_[StereoChannels::Left ] - this->state2_[StereoChannels::Left ]) * this->h_; |
|
|
|
|
bp = this->g_ * hp + this->state1_[StereoChannels::Left ]; |
|
|
|
|
this->state1_[StereoChannels::Left ] = this->g_ * hp + bp; |
|
|
|
|
lp = this->g_ * bp + this->state2_[StereoChannels::Left ]; |
|
|
|
|
this->state2_[StereoChannels::Left ] = this->g_ * bp + lp; |
|
|
|
|
|
|
|
|
|
switch(this->Mode) |
|
|
|
|
{ |
|
|
|
|
case FilterMode::SVF_LP: |
|
|
|
|
outL = lp; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case FilterMode::SVF_BP: |
|
|
|
|
outL = bp; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case FilterMode::SVF_BP_NORMALIZED: |
|
|
|
|
outL = bp * this->r_; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case FilterMode::SVF_HP: |
|
|
|
|
outL = hp; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
hp = (inR - this->r_ * this->state1_[StereoChannels::Right] - this->g_ * this->state1_[StereoChannels::Right] - this->state2_[StereoChannels::Right]) * this->h_; |
|
|
|
|
bp = this->g_ * hp + this->state1_[StereoChannels::Right]; |
|
|
|
|
this->state1_[StereoChannels::Right] = this->g_ * hp + bp; |
|
|
|
|
lp = this->g_ * bp + this->state2_[StereoChannels::Right]; |
|
|
|
|
this->state2_[StereoChannels::Right] = this->g_ * bp + lp; |
|
|
|
|
|
|
|
|
|
switch(this->Mode) |
|
|
|
|
{ |
|
|
|
|
case FilterMode::SVF_LP: |
|
|
|
|
outR = lp; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case FilterMode::SVF_BP: |
|
|
|
|
outR = bp; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case FilterMode::SVF_BP_NORMALIZED: |
|
|
|
|
outR = bp * this->r_; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case FilterMode::SVF_HP: |
|
|
|
|
outR = hp; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline void setGRH(float32_t g, float32_t r, float32_t h) |
|
|
|
|
{ |
|
|
|
|
this->g_ = g; |
|
|
|
|
this->r_ = r; |
|
|
|
|
this->h_ = h; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline void setGR(float32_t g, float32_t r) |
|
|
|
|
{ |
|
|
|
|
this->g_ = g; |
|
|
|
|
this->r_ = r; |
|
|
|
|
this->h_ = 1.0f / (1.0f + this->r_ * this->g_ * this->g_ * this->g_); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template<FrequencyApproximation approximation> |
|
|
|
|
inline void setFQ(float32_t frequency, float32_t resonance) |
|
|
|
|
{ |
|
|
|
|
this->g_ = SVF::tan<approximation>(frequency); |
|
|
|
|
this->r_ = 1.0f / resonance; |
|
|
|
|
this->h_ = 1.0f / (1.0f + this->r_ * this->g_ * this->g_ * this->g_); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
template<FrequencyApproximation approximation> |
|
|
|
|
static inline float32_t tan(float32_t f) |
|
|
|
|
{ |
|
|
|
|
switch(approximation) |
|
|
|
|
{ |
|
|
|
|
case FrequencyApproximation::FrequencyExact: |
|
|
|
|
{ |
|
|
|
|
// Clip coefficient to about 100.
|
|
|
|
|
f = constrain(f, 0.0f, 0.497f); |
|
|
|
|
return ::tan(PI * f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case FrequencyApproximation::FrequencyDirty: |
|
|
|
|
{ |
|
|
|
|
// Optimized for frequencies below 8kHz.
|
|
|
|
|
const float32_t a = 3.736e-01 * Constants::M_PI_POW_3; |
|
|
|
|
return f * (PI + a * f * f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case FrequencyApproximation::FrequencyFast: |
|
|
|
|
{ |
|
|
|
|
// The usual tangent approximation uses 3.1755e-01 and 2.033e-01, but
|
|
|
|
|
// the coefficients used here are optimized to minimize error for the
|
|
|
|
|
// 16Hz to 16kHz range, with a sample rate of 48kHz.
|
|
|
|
|
const float a = 3.260e-01 * Constants::M_PI_POW_3; |
|
|
|
|
const float b = 1.823e-01 * Constants::M_PI_POW_5; |
|
|
|
|
float f2 = f * f; |
|
|
|
|
return f * (PI + f2 * (a + b * f2)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case FrequencyApproximation::FrequencyAccurate: |
|
|
|
|
{ |
|
|
|
|
// These coefficients don't need to be tweaked for the audio range.
|
|
|
|
|
const float a = 3.333314036e-01 * Constants::M_PI_POW_3; |
|
|
|
|
const float b = 1.333923995e-01 * Constants::M_PI_POW_5; |
|
|
|
|
const float c = 5.33740603e-02 * Constants::M_PI_POW_7; |
|
|
|
|
const float d = 2.900525e-03 * Constants::M_PI_POW_9; |
|
|
|
|
const float e = 9.5168091e-03 * Constants::M_PI_POW_11; |
|
|
|
|
float f2 = f * f; |
|
|
|
|
return f * (PI + f2 * (a + f2 * (b + f2 * (c + f2 * (d + f2 * e))))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const FilterMode Mode; |
|
|
|
|
float32_t g_; |
|
|
|
|
float32_t r_; |
|
|
|
|
float32_t h_; |
|
|
|
|
|
|
|
|
|
float32_t state1_[StereoChannels::kNumChannels]; |
|
|
|
|
float32_t state2_[StereoChannels::kNumChannels]; |
|
|
|
|
|
|
|
|
|
IMPLEMENT_DUMP( |
|
|
|
|
const size_t space = 12; |
|
|
|
|
const size_t precision = 6; |
|
|
|
|
|
|
|
|
|
std::stringstream ss; |
|
|
|
|
|
|
|
|
|
out << "START " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
|
|
|
|
|
|
|
|
|
SS_RESET(ss, precision, std::left); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "g_"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "r_"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "h_"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "state1_[ L ]"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "state1_[ R ]"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "state2_[ L ]"); |
|
|
|
|
SS__TEXT(ss, ' ', space, std::left, '|', "state2_[ R ]"); |
|
|
|
|
out << "\t" << ss.str() << std::endl; |
|
|
|
|
|
|
|
|
|
SS_RESET(ss, precision, std::left); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
SS_SPACE(ss, '-', space, std::left, '+'); |
|
|
|
|
out << "\t" << ss.str() << std::endl; |
|
|
|
|
|
|
|
|
|
SS_RESET(ss, precision, std::left); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->g_); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->r_); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->r_); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state1_[StereoChannels::Left ]); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state1_[StereoChannels::Right]); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state2_[StereoChannels::Left ]); |
|
|
|
|
SS__TEXT(ss, ' ', space - 1, std::right, " |", this->state2_[StereoChannels::Right]); |
|
|
|
|
out << "\t" << ss.str() << std::endl; |
|
|
|
|
|
|
|
|
|
out << "END " << tag << "(" << typeid(*this).name() << ") dump" << std::endl << std::endl; |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
IMPLEMENT_INSPECT( |
|
|
|
|
size_t nb_errors = 0u; |
|
|
|
|
|
|
|
|
|
nb_errors += inspector(tag + ".g_", this->g_, 0.0f, 106.11f, deepInspection); |
|
|
|
|
nb_errors += inspector(tag + ".r_", this->r_, 0.0f, 1.0f, deepInspection); |
|
|
|
|
nb_errors += inspector(tag + ".h_", this->h_, 0.0f, 1.0f, deepInspection); |
|
|
|
|
nb_errors += inspector(tag + ".state1_[ L ]", this->state1_[StereoChannels::Left ], -1.0f, 1.0f, deepInspection); |
|
|
|
|
nb_errors += inspector(tag + ".state1_[ R ]", this->state1_[StereoChannels::Right], -1.0f, 1.0f, deepInspection); |
|
|
|
|
nb_errors += inspector(tag + ".state2_[ L ]", this->state2_[StereoChannels::Left ], -1.0f, 1.0f, deepInspection); |
|
|
|
|
nb_errors += inspector(tag + ".state2_[ R ]", this->state2_[StereoChannels::Right], -1.0f, 1.0f, deepInspection); |
|
|
|
|
|
|
|
|
|
return nb_errors; |
|
|
|
|
) |
|
|
|
|
}; |
|
|
|
|