;; Diode Ladder Filter ;; ;; Based on code by Will Pirkle, presented in: ;; ;; http://www.willpirkle.com/Downloads/AN-6DiodeLadderFilter.pdf ;; ;; and in his book "Designing software synthesizer plug-ins in C++ : for ;; RackAFX, VST3, and Audio Units" ;; ;; UDO version by Steven Yi (2016.xx.xx) ;; ARGS ;; ain - signal to filter ;; acf/kcf - cutoff frequency ;; ak/kk - k-value that controls resonance, self-resonance occurs at k=17; ;; knlp - use non-linear processing: ;; 0 - none ;; 1 - normalized (outputs to range +-1.0) ;; 2 - non-normalized (less expensive than normalized, range +-0.8) ;; ksaturation - saturation amount for non-linear processing ;; (default: 1.0, greater values lead to higher saturation) opcode diode_ladder, a, aKKKK ain, kcf, kk, knlp, ksaturation xin ;; initialization aout init 0 ka1 init 1.0 ka2 init 0.5 ka3 init 0.5 ka4 init 0.5 ;; state for each 1-pole's integrator kz1 init 0 kz2 init 0 kz3 init 0 kz4 init 0 ;; coefficients kG1 init 0 kG2 init 0 kG3 init 0 kG4 init 0 kbeta1 init 0 kbeta2 init 0 kbeta3 init 0 kbeta4 init 0 kdelta1 init 0 kdelta2 init 0 kdelta3 init 0 kepsilon1 init 0 kepsilon2 init 0 kepsilon3 init 0 kSG1 init 0 kSG2 init 0 kSG3 init 0 kSG4 init 0 kSIGMA init 0 kGAMMA init 0 klastcut init -1 if (klastcut != kcf) then ; pre-warp the cutoff- these are bilinear-transform filters kwd = 2 * $M_PI * kcf iT = 1/sr kwa = (2/iT) * tan(kwd * iT/2) kg = kwa * iT/2 kG4 = 0.5 * kg / (1.0 + kg) kG3 = 0.5 * kg / (1.0 + kg - 0.5 * kg * kG4) kG2 = 0.5 * kg / (1.0 + kg - 0.5 * kg * kG3) kG1 = kg / (1.0 + kg - kg * kG2) kGAMMA = kG4 * kG3 * kG2 * kG1 kSG1 = kG4 * kG3 * kG2 kSG2 = kG4 * kG3 kSG3 = kG4 kSG4 = 1.0 kalpha = kg / (1.0 + kg) kbeta1 = 1.0 / (1.0 + kg - kg * kG2) kbeta2 = 1.0 / (1.0 + kg - 0.5 * kg * kG3) kbeta3 = 1.0 / (1.0 + kg - 0.5 * kg * kG4) kbeta4 = 1.0 / (1.0 + kg) kgamma1 = 1.0 + kG1 * kG2 kgamma2 = 1.0 + kG2 * kG3 kgamma3 = 1.0 + kG3 * kG4 kdelta1 = kg kdelta2 = 0.5 * kg kdelta3 = 0.5 * kg kepsilon1 = kG2 kepsilon2 = kG3 kepsilon3 = kG4 endif klastcut = kcf kindx = 0 while kindx < ksmps do kin = ain[kindx] ;; feedback inputs kfb4 = kbeta4 * kz4 kfb3 = kbeta3 * (kz3 + kfb4 * kdelta3) kfb2 = kbeta2 * (kz2 + kfb3 * kdelta2) ;; feedback process kfbo1 = (kbeta1 * (kz1 + kfb2 * kdelta1)) kfbo2 = (kbeta2 * (kz2 + kfb3 * kdelta2)) kfbo3 = (kbeta3 * (kz3 + kfb4 * kdelta3)) kfbo4 = kfb4 kSIGMA = kSG1 * kfbo1 + kSG2 * kfbo2 + kSG3 * kfbo3 + kSG4 * kfbo4 ;; non-linear processing if (knlp == 1) then kin = (1.0 / tanh(ksaturation)) * tanh(ksaturation * kin) elseif (knlp == 2) then kin = tanh(ksaturation * kin) endif ;; form input to loop kun = (kin - kk * kSIGMA) / (1.0 + kk * kGAMMA) ;; 1st stage kxin = (kun * kgamma1 + kfb2 + kepsilon1 * kfbo1) kv = (ka1 * kxin - kz1) * kalpha klp = kv + kz1 kz1 = klp + kv ;; 2nd stage kxin = (klp * kgamma2 + kfb3 + kepsilon2 * kfbo2) kv = (ka2 * kxin - kz2) * kalpha klp = kv + kz2 kz2 = klp + kv ;; 3rd stage kxin = (klp * kgamma3 + kfb4 + kepsilon3 * kfbo3) kv = (ka3 * kxin - kz3) * kalpha klp = kv + kz3 kz3 = klp + kv ;; 4th stage kv = (ka4 * klp - kz4) * kalpha klp = kv + kz4 kz4 = klp + kv aout[kindx] = klp kindx += 1 od xout aout endop opcode diode_ladder, a, aaaKK ain, acf, ak, knlp, ksaturation xin ;; initialization aout init 0 ka1 init 1.0 ka2 init 0.5 ka3 init 0.5 ka4 init 0.5 ;; state for each 1-pole's integrator kz1 init 0 kz2 init 0 kz3 init 0 kz4 init 0 ;; coefficients kG1 init 0 kG2 init 0 kG3 init 0 kG4 init 0 kbeta1 init 0 kbeta2 init 0 kbeta3 init 0 kbeta4 init 0 kdelta1 init 0 kdelta2 init 0 kdelta3 init 0 kepsilon1 init 0 kepsilon2 init 0 kepsilon3 init 0 kSG1 init 0 kSG2 init 0 kSG3 init 0 kSG4 init 0 kSIGMA init 0 kGAMMA init 0 klastcut init 0 kindx = 0 while kindx < ksmps do kin = ain[kindx] kcf = acf[kindx] kk = ak[kindx] if (klastcut != kcf) then ; pre-warp the cutoff- these are bilinear-transform filters kwd = 2 * $M_PI * kcf iT = 1/sr kwa = (2/iT) * tan(kwd * iT/2) kg = kwa * iT/2 kG4 = 0.5 * kg / (1.0 + kg) kG3 = 0.5 * kg / (1.0 + kg - 0.5 * kg * kG4) kG2 = 0.5 * kg / (1.0 + kg - 0.5 * kg * kG3) kG1 = kg / (1.0 + kg - kg * kG2) kGAMMA = kG4 * kG3 * kG2 * kG1 kSG1 = kG4 * kG3 * kG2 kSG2 = kG4 * kG3 kSG3 = kG4 kSG4 = 1.0 kalpha = kg / (1.0 + kg) kbeta1 = 1.0 / (1.0 + kg - kg * kG2) kbeta2 = 1.0 / (1.0 + kg - 0.5 * kg * kG3) kbeta3 = 1.0 / (1.0 + kg - 0.5 * kg * kG4) kbeta4 = 1.0 / (1.0 + kg) kgamma1 = 1.0 + kG1 * kG2 kgamma2 = 1.0 + kG2 * kG3 kgamma3 = 1.0 + kG3 * kG4 kdelta1 = kg kdelta2 = 0.5 * kg kdelta3 = 0.5 * kg kepsilon1 = kG2 kepsilon2 = kG3 kepsilon3 = kG4 endif klastcut = kcf ;; feedback inputs kfb4 = kbeta4 * kz4 kfb3 = kbeta3 * (kz3 + kfb4 * kdelta3) kfb2 = kbeta2 * (kz2 + kfb3 * kdelta2) ;; feedback process kfbo1 = (kbeta1 * (kz1 + kfb2 * kdelta1)) kfbo2 = (kbeta2 * (kz2 + kfb3 * kdelta2)) kfbo3 = (kbeta3 * (kz3 + kfb4 * kdelta3)) kfbo4 = kfb4 kSIGMA = kSG1 * kfbo1 + kSG2 * kfbo2 + kSG3 * kfbo3 + kSG4 * kfb4 ;; non-linear processing if (knlp == 1) then kin = (1.0 / tanh(ksaturation)) * tanh(ksaturation * kin) elseif (knlp == 2) then kin = tanh(ksaturation * kin) endif ;; form input to loop kun = (kin - kk * kSIGMA) / (1.0 + kk * kGAMMA) ;; 1st stage kxin = (kun * kgamma1 + kfb2 + kepsilon1 * kfbo1) kv = (ka1 * kxin - kz1) * kalpha klp = kv + kz1 kz1 = klp + kv ;; 2nd stage kxin = (klp * kgamma2 + kfb3 + kepsilon2 * kfbo2) kv = (ka2 * kxin - kz2) * kalpha klp = kv + kz2 kz2 = klp + kv ;; 3rd stage kxin = (klp * kgamma3 + kfb4 + kepsilon3 * kfbo3) kv = (ka3 * kxin - kz3) * kalpha klp = kv + kz3 kz3 = klp + kv ;; 4th stage kv = (ka4 * klp - kz4) * kalpha klp = kv + kz4 kz4 = klp + kv aout[kindx] = klp kindx += 1 od xout aout endop opcode diode_ladder, a, aaKKK ain, acf, kk, knlp, ksaturation xin ares diode_ladder ain, acf, a(kk), knlp, ksaturation xout ares endop