|
|
|
@ -10,12 +10,16 @@ |
|
|
|
|
#include "../globals.h" |
|
|
|
|
#include "util.h" |
|
|
|
|
|
|
|
|
|
#define FILTER_K_BASE 6.214608098422192 |
|
|
|
|
#define FILTER_OVERSAMPLE 4 |
|
|
|
|
#define FILTER_K_MIN_FREQ 20.0f |
|
|
|
|
#define FILTER_K_MAX_FREQ 15000.0f |
|
|
|
|
#define FILTER_K_BASE logf(FILTER_K_MAX_FREQ / FILTER_K_MIN_FREQ) |
|
|
|
|
#define FILTER_K_SCALE (2.f * FILTER_K_MIN_FREQ / (FILTER_OVERSAMPLE * SAMPLE_RATE)) |
|
|
|
|
|
|
|
|
|
#define K_LUT_SIZE 64 |
|
|
|
|
#ifdef USE_LUTS |
|
|
|
|
|
|
|
|
|
#define K_LUT_SIZE 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern float kLUT[K_LUT_SIZE]; |
|
|
|
|
|
|
|
|
|
inline float freqToK(float x) { |
|
|
|
@ -27,13 +31,13 @@ |
|
|
|
|
#else |
|
|
|
|
|
|
|
|
|
inline float freqToK(float x) { |
|
|
|
|
return tan(M_PI_4 * 0.002 * exp(x * FILTER_K_BASE)); |
|
|
|
|
return tanf((float) M_PI_2 * std::min(0.5f, FILTER_K_SCALE * expf(x * FILTER_K_BASE))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
inline float noteToFltFreq(float note) { |
|
|
|
|
return ((note - 69) / 12.0) / (FILTER_K_BASE * M_LOG2E); |
|
|
|
|
return ((note - 69) / 12.0f) / (FILTER_K_BASE * (float) M_LOG2E); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class SVF12 { |
|
|
|
@ -64,6 +68,28 @@ public: |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class Oversampler { |
|
|
|
|
private: |
|
|
|
|
int times; |
|
|
|
|
float kK, kQ = (float) M_SQRT1_2; |
|
|
|
|
SVF12 aa1, aa2; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
Oversampler(int times) : times(times), kK(tanf((float) M_PI_2 * 1.0f / times)) {} |
|
|
|
|
|
|
|
|
|
inline float tick(float in, std::function<float(float)> process) { |
|
|
|
|
if(times < 2) { |
|
|
|
|
return process(in); |
|
|
|
|
} else { |
|
|
|
|
float out = aa2.tick(aa1.tick(times * process(in), kK, kQ).lp, kK, kQ).lp; |
|
|
|
|
for(int i = 1; i < times; ++i) { |
|
|
|
|
aa2.tick(aa1.tick(process(0), kK, kQ).lp, kK, kQ); |
|
|
|
|
} |
|
|
|
|
return out; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 12 and 24 dB/oct
|
|
|
|
|
class Filter { |
|
|
|
|
public: |
|
|
|
@ -88,27 +114,32 @@ public: |
|
|
|
|
private: |
|
|
|
|
Settings const * settings; |
|
|
|
|
SVF12 fltA, fltB; |
|
|
|
|
Oversampler os{FILTER_OVERSAMPLE}; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
void assign(Settings const * settings); |
|
|
|
|
|
|
|
|
|
inline float tick(float in, float freqAdd) { |
|
|
|
|
float freq = clamp(settings->freq + freqAdd, 0, 1); |
|
|
|
|
//float kK = tan(M_PI_4 * clamp(0.002 * exp(freq * FILTER_K_BASE), 0, 1));
|
|
|
|
|
float freq = clamp(settings->freq + freqAdd, 0.f, 1.f); |
|
|
|
|
float kK = freqToK(freq); |
|
|
|
|
float kQ = M_SQRT1_2 + settings->res; |
|
|
|
|
|
|
|
|
|
SVF12::Output outA = fltA.tick(in, kK, kQ); |
|
|
|
|
|
|
|
|
|
switch(settings->type) { |
|
|
|
|
case TYPE_LP: |
|
|
|
|
return settings->slope == SLOPE_24 ? fltB.tick(outA.lp, kK, kQ).lp : outA.lp; |
|
|
|
|
case TYPE_BP: |
|
|
|
|
return settings->slope == SLOPE_24 ? fltB.tick(outA.bp, kK, kQ).bp : outA.bp;
|
|
|
|
|
case TYPE_HP: |
|
|
|
|
return settings->slope == SLOPE_24 ? fltB.tick(outA.hp, kK, kQ).hp : outA.hp; |
|
|
|
|
} |
|
|
|
|
float kQ = (float) M_SQRT1_2 + settings->res; |
|
|
|
|
|
|
|
|
|
return os.tick(in, [this, kK, kQ](float oin) { |
|
|
|
|
SVF12::Output outA = fltA.tick(oin, kK, kQ); |
|
|
|
|
|
|
|
|
|
switch(settings->type) { |
|
|
|
|
case TYPE_LP: |
|
|
|
|
return settings->slope == SLOPE_24 ? fltB.tick(outA.lp, kK, kQ).lp : outA.lp; |
|
|
|
|
case TYPE_BP: |
|
|
|
|
return settings->slope == SLOPE_24 ? fltB.tick(outA.bp, kK, kQ).bp : outA.bp;
|
|
|
|
|
case TYPE_HP: |
|
|
|
|
return settings->slope == SLOPE_24 ? fltB.tick(outA.hp, kK, kQ).hp : outA.hp; |
|
|
|
|
default: |
|
|
|
|
return oin; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif |