#define BLOCK_SIZE 256 #define OVERSAMPLING 2 #define FULL_SIZE (BLOCK_SIZE*OVERSAMPLING) /* an implementation of: S. D Angelo and V. Välimäki. Generalized Moog Ladder Filter: Part II Explicit Nonlinear Model through a Novel Delay-Free Loop Implementation Method. IEEE Trans. Audio, Speech, and Lang. Process., vol. 22, no. 12, pp. 1873 1883, December 2014. https://aaltodoc.aalto.fi/bitstream/handle/123456789/14420/article6.pdf */ #include #include #include "util.hpp" #include "Param.hpp" #include "Crap.hpp" #include "os2piir.hpp" #include "BufferOS2.hpp" #define VT 0.026 #define N 4 #define VT2 T(2.*VT) TEMPLATE INNER PURE T tanh2(T x) { //return T(tanh(x[0]), tanh(x[1])); T xx = x*x; T a = ((xx + T(378))*xx + T(17325))*xx + T(135135); T b = ((T(28)*xx + T(3150))*xx + T(62370))*xx + T(135135); return x*a/b; } typedef struct { v2df sum, sumback, dout; } stage; typedef struct { v2df g; v2df p0; v2df q0, q1, q2, q3; v2df r1, r2, r3, r4; v2df L_p0; v2df L_q0; v2df L_r1; } freqdata; // TODO: namespace TEMPLATE INNER T process_stage(stage *s, freqdata fd, T in) { T temp = (in + s->sumback)*VT2*fd.L_p0*fd.g; T out = temp + s->sum; s->sum += T(2)*temp; s->dout = tanh2(out/VT2); s->sumback = in*fd.L_r1 - s->dout*fd.L_q0; return out; } struct Crap_mugi4 :public AdjustAll> { static constexpr ulong id = 0xD8D0D8D0; static constexpr char label[] = "crap_mugi4"; static constexpr char name[] = "crap mugi4 (moog-like)"; static constexpr char author[] = "Connor Olding"; static constexpr char copyright[] = "MIT"; static constexpr ulong parameters = 3; freqdata fd; stage s1, s2, s3, s4; v2df sumback1, sumback2, sumback3, sumback4; v2df drive, feedback; inline Crap_mugi4() { // FIXME //memset(data, 0, sizeof(personal)); } TEMPLATE inline T process_one(T in) { in *= drive; T sum = in + sumback1; T pre = -fd.p0*sum; process_stage(&s1, fd, tanh2(pre/VT2)); process_stage(&s2, fd, s1.dout); process_stage(&s3, fd, s2.dout); T out = process_stage(&s4, fd, s3.dout); T back = feedback*out; sumback1 = fd.r1*in + fd.q0*back + sumback2; sumback2 = fd.r2*in + fd.q1*back + sumback3; sumback3 = fd.r3*in + fd.q2*back + sumback4; sumback4 = fd.r4*in + fd.q3*back; T compensate = -(feedback + T(1)); return out/drive*compensate; } virtual inline v2df process2(v2df in) { return process_one(in); } static inline void construct_params(Param *params) { sprintf(params[0].name, "Frequency"); params[0].min = 20; params[0].max = 20000; params[0].scale = SCALE_HZ; params[0].def = DEFAULT_MAX; sprintf(params[1].name, "Drive"); params[1].min = -40; params[1].max = 0; params[1].scale = SCALE_DB; params[1].def = DEFAULT_MIN; sprintf(params[2].name, "Feedback"); params[2].min = 0; params[2].max = 1; params[2].scale = SCALE_FLOAT; params[2].def = DEFAULT_MIN; params[0].reset(); params[1].reset(); params[2].reset(); } inline void pause() {} inline void resume() { memset(&hb_up, 0, sizeof(halfband_t)); memset(&hb_down, 0, sizeof(halfband_t)); } inline void adjust_all(Param *params) { double f = params[0].value; if (f < 20) f = 20; if (f > fs/6*OVERSAMPLING) f = fs/6*OVERSAMPLING; double _drive = DB2LIN(params[1].value); double k = params[2].value*N; drive = v2df(_drive, _drive); feedback = v2df(k, k); double bc1 = -4; //-binomial(N, 1); double bc2 = -6; //-binomial(N, 2); double bc3 = -4; //-binomial(N, 3); double bc4 = -1; //-binomial(N, 4); #define fd_set(L, R) double L = R; fd.L = (v2df){L, L} fd_set(g, tan(M_PI*f/fs/OVERSAMPLING)); double gg1 = g/(g + 1); double gg1Nk = k*gg1*gg1*gg1*gg1; double g1g1 = (g - 1)/(g + 1); fd_set(p0, 1/(1 + gg1Nk)); fd_set(r1, bc1*gg1Nk); fd_set(r2, bc2*gg1Nk); fd_set(r3, bc3*gg1Nk); fd_set(r4, bc4*gg1Nk); fd_set(q0, r1 + bc1*g1g1); fd_set(q1, r2 + bc2*g1g1*g1g1); fd_set(q2, r3 + bc3*g1g1*g1g1*g1g1); fd_set(q3, r4 + bc4*g1g1*g1g1*g1g1*g1g1); fd_set(L_p0, 1/(1 + g)); fd_set(L_q0, 1 - g); fd_set(L_r1, -g); #undef fd_set fd = fd; } }; constexpr char Crap_mugi4::label[]; constexpr char Crap_mugi4::name[]; constexpr char Crap_mugi4::author[]; constexpr char Crap_mugi4::copyright[];