You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
2.6 KiB
114 lines
2.6 KiB
#ifndef __SVF_H__
|
|
#define __SVF_H__
|
|
|
|
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */
|
|
|
|
#include <cmath>
|
|
#include <cfloat>
|
|
#include <iostream>
|
|
|
|
#include "../globals.h"
|
|
#include "util.h"
|
|
|
|
#define FILTER_K_BASE 6.214608098422192
|
|
|
|
#ifdef USE_LUTS
|
|
|
|
#define K_LUT_SIZE 64
|
|
|
|
extern float kLUT[K_LUT_SIZE];
|
|
|
|
inline float freqToK(float x) {
|
|
float intPart, fracPart = modf(x * (K_LUT_SIZE - 1), &intPart);
|
|
int index = (int) intPart;
|
|
return fracPart < FLT_EPSILON ? kLUT[index] : (1 - fracPart) * kLUT[index] + fracPart * kLUT[index + 1];
|
|
}
|
|
|
|
#else
|
|
|
|
inline float freqToK(float x) {
|
|
return tan(M_PI_4 * 0.002 * exp(x * FILTER_K_BASE));
|
|
}
|
|
|
|
#endif
|
|
|
|
inline float noteToFltFreq(float note) {
|
|
return ((note - 69) / 12.0) / (FILTER_K_BASE * M_LOG2E);
|
|
}
|
|
|
|
class SVF12 {
|
|
public:
|
|
typedef struct {
|
|
float lp; // low-pass
|
|
float bp; // band-pass
|
|
float hp; // high-pass
|
|
} Output;
|
|
|
|
protected:
|
|
float as1, as2;
|
|
|
|
public:
|
|
SVF12() : as1(0), as2(0) {}
|
|
|
|
inline Output tick(float in, float kK, float kQ) {
|
|
Output out;
|
|
float kdiv = 1 + kK/kQ + kK*kK;
|
|
out.hp = (in - (1/kQ + kK) * as1 - as2) / kdiv;
|
|
float au = out.hp * kK;
|
|
out.bp = au + as1;
|
|
as1 = au + out.bp;
|
|
au = out.bp * kK;
|
|
out.lp = au + as2;
|
|
as2 = au + out.lp;
|
|
return out;
|
|
}
|
|
};
|
|
|
|
// 12 and 24 dB/oct
|
|
class Filter {
|
|
public:
|
|
enum Type {
|
|
TYPE_LP = 0,
|
|
TYPE_BP = 42,
|
|
TYPE_HP = 84
|
|
};
|
|
|
|
enum Slope {
|
|
SLOPE_24 = 0,
|
|
SLOPE_12 = 64,
|
|
};
|
|
|
|
typedef struct {
|
|
float freq;
|
|
float res;
|
|
Type type;
|
|
Slope slope;
|
|
} Settings;
|
|
|
|
private:
|
|
Settings const * settings;
|
|
SVF12 fltA, fltB;
|
|
|
|
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 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;
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif |