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.
 
 

128 lines
3.2 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 <algorithm>
#include <functional>
#include "../globals.h"
#include "util.h"
#define FILTER_OVERSAMPLE 4
#define FILTER_K_SCALE (2 * CV_FREQ_MIN / (FILTER_OVERSAMPLE * SAMPLE_RATE))
#define FILTER_CV_MAX 9.25 // MIDI note "135" (19912.126958213178287 Hz)
inline float cvToK(float cv) {
return tanf((float) M_PI_2 * std::min(0.5f, std::min(1.f / FILTER_OVERSAMPLE, (float) FILTER_K_SCALE * exp2f(cv))));
}
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;
}
};
class Oversampler {
private:
float kK, kQ = (float) M_SQRT1_2;
SVF12 aa1, aa2;
public:
Oversampler() : kK(tanf((float) M_PI_2 * 1.0f / FILTER_OVERSAMPLE)) {}
inline float tick(float in, std::function<float(float)> process) {
if(FILTER_OVERSAMPLE < 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);
// }
float out = process(aa2.tick(aa1.tick(FILTER_OVERSAMPLE * in, kK, kQ).lp, kK, kQ).lp);
for(int i = 1; i < FILTER_OVERSAMPLE; ++i) {
process(aa2.tick(aa1.tick(0, kK, kQ).lp, kK, kQ).lp);
}
return out;
}
}
};
// 12 and 24 dB/oct
class Filter {
public:
enum Type {
TYPE_LP = 0,
TYPE_BP,
TYPE_HP
};
enum Slope {
SLOPE_24 = 0,
SLOPE_12,
};
typedef struct {
float freq;
float res;
Type type;
Slope slope;
} Settings;
private:
Settings const * settings;
SVF12 fltA, fltB;
Oversampler os;
public:
void assign(Settings const * settings);
inline float tick(float in, float freqMod) {
float kK = cvToK(settings->freq + freqMod);
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