Analog synthesis engine for Klang Modular
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.
 
 
 

142 lines
3.7 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"
#include "../luts.h"
#include "../perf.h"
#define FILTER_K_SCALE (2 * CV_FREQ_MIN / SAMPLE_RATE)
inline float cvToK(float cv) {
return tanf((float) M_PI_2 * std::min(0.5f, (float) FILTER_K_SCALE * exp2f(cv)));
}
class SVF12Stereo {
protected:
float hp[2], bp[2], lp[2];
float as1[2], as2[2];
public:
SVF12Stereo() : hp{0, 0}, bp{0, 0}, lp{0, 0}, as1{0, 0}, as2{0, 0} {}
inline void tick(float *in, float *hpo, float *bpo, float *lpo, size_t bufferSize, float *kK, float *kQ) {
size_t frameCount = bufferSize / 2;
float kQ_1[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
kQ_1[i] = 1.f / kQ[i];
}
float kMul[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
kMul[i] = 1.f / (1.f + kK[i]*kQ_1[i] + kK[i]*kK[i]);
}
for(size_t i = 0, j = 0; i < bufferSize; ++i) {
size_t ch = i & 1;
hp[ch] = (in[i] - (kQ_1[j] + kK[j]) * as1[ch] - as2[ch]) * kMul[j];
float au = hp[ch] * kK[j];
bp[ch] = au + as1[ch];
as1[ch] = au + bp[ch];
au = bp[ch] * kK[j];
lp[ch] = au + as2[ch];
as2[ch] = au + lp[ch];
hpo && (hpo[i] = hp[ch]);
bpo && (bpo[i] = bp[ch]);
lpo && (lpo[i] = lp[ch]);
j += ch;
}
}
};
// 12 and 24 dB/oct
class FilterStereo {
public:
enum Type {
TYPE_NONE = 0,
TYPE_LP,
TYPE_BP,
TYPE_HP
};
enum Slope {
SLOPE_12 = 0,
SLOPE_24,
};
typedef struct {
float freq;
float res;
Type type;
Slope slope;
} Settings;
private:
Settings const * settings;
SVF12Stereo fltA, fltB;
float freq, res;
public:
void assign(Settings const * settings);
inline void tick(float *in, float *out, size_t bufferSize, float *freqMod) {
nano_t started = nanos();
size_t frameCount = bufferSize / 2;
float dt = 1.f / (float) frameCount;
float kK[frameCount];
float dFreq = dt * (settings->freq - freq);
for(size_t i = 0; i < frameCount; i++) {
kK[i] = fastCVtoK(freq + freqMod[i]);
freq += dFreq;
}
float kQ[frameCount];
float dRes = dt * (settings->res - res);
for(size_t i = 0; i < frameCount; i++) {
kQ[i] = (float) M_SQRT1_2 + res;
res += dRes;
}
switch(settings->type) {
case TYPE_NONE:
std::copy(in, in + bufferSize, out);
break;
case TYPE_LP:
fltA.tick(in, NULL, NULL, out, bufferSize, kK, kQ);
if(settings->slope == SLOPE_24) {
fltB.tick(out, NULL, NULL, out, bufferSize, kK, kQ);
}
break;
case TYPE_BP:
fltA.tick(in, NULL, out, NULL, bufferSize, kK, kQ);
if(settings->slope == SLOPE_24) {
fltB.tick(out, NULL, out, NULL, bufferSize, kK, kQ);
}
break;
case TYPE_HP:
fltA.tick(in, out, NULL, NULL, bufferSize, kK, kQ);
if(settings->slope == SLOPE_24) {
fltB.tick(out, out, NULL, NULL, bufferSize, kK, kQ);
}
break;
}
nano_t finished = nanos();
perfNanos.filter += finished - started;
}
};
#endif