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
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 |