Created
September 20, 2021 04:43
-
-
Save spencersalazar/40109d20cac7446b4851386e6cb7522b to your computer and use it in GitHub Desktop.
Virtual Analog filters in ChucK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ChucK implementation of Pirkle's Korg35 filter digitization | |
// see https://www.willpirkle.com/app-notes/virtual-analog-korg35-lpf/ | |
// Virtual Analog one-pole filter | |
class VAOnePole extends Chugen | |
{ | |
1 => float a; | |
1 => float b; | |
0 => float z1; | |
1 => int isLPF; | |
fun void set(float freq) | |
{ | |
second/samp => float srate; | |
if (freq > srate/2) srate/2 => freq; | |
if (freq < 10) 10 => freq; | |
1.0/srate => float T; | |
2*pi*freq => float wd; | |
(2/T)*Math.tan(wd*T/2) => float wa; | |
wa*T/2 => float g; | |
g/(1.0 + g) => a; | |
} | |
fun float getFeedback() | |
{ | |
return z1*b; | |
} | |
fun float tick(float xn) | |
{ | |
// calculate v(n) | |
(xn - z1)*a => float vn; | |
// form LP output | |
vn + z1 => float lpf; | |
// update memory | |
vn + lpf => z1; | |
// do the HPF | |
xn - lpf => float hpf; | |
if(isLPF) | |
return lpf; | |
else | |
return hpf; | |
} | |
}; | |
// virtual analog Korg35 filter | |
class Korg35Filter extends Chugen | |
{ | |
VAOnePole lpf1; | |
VAOnePole lpf2; | |
VAOnePole hpf; | |
1 => float a0; | |
1 => float K; | |
1 => float KNorm; | |
1 => float saturation; | |
0 => int nonLinear; | |
fun void set(float freq, float _K) | |
{ | |
second/samp => float srate; | |
if (freq > srate/2) srate/2 => freq; | |
if (freq < 10) 10 => freq; | |
_K => K; | |
1.0/srate => float T; | |
2*pi*freq => float wd; | |
(2/T)*Math.tan(wd*T/2) => float wa; | |
wa*T/2 => float g; | |
g/(1.0 + g) => float G; | |
G => lpf1.a => lpf2.a => hpf.a; | |
(K - K*G)/(1.0 + g) => lpf2.b; | |
-1.0/(1.0 + g) => hpf.b; | |
1.0/(1.0-K*G+K*G*G) => a0; | |
if (K > 0) 1.0/K => KNorm; | |
else 1.0 => KNorm; | |
} | |
fun float tick(float xn) | |
{ | |
lpf1.tick(xn) => float y1; | |
hpf.getFeedback() + lpf2.getFeedback() => float S35; | |
a0*(y1+S35) => float u; | |
if (nonLinear) { | |
Math.tanh(saturation*u) => u; | |
} | |
K*lpf2.tick(u) => float y; | |
hpf.tick(y); | |
KNorm*y => y; | |
return y; | |
} | |
}; | |
/* | |
// test code if desired | |
MAUI_Slider slider; | |
slider.display(); | |
BlitSaw saw => Korg35Filter kf => dac; | |
0.5 => saw.gain; | |
440 => saw.freq; | |
20 => saw.harmonics; | |
while(true) { | |
100*Math.pow(10, slider.value()*2) => float val; | |
kf.set(val, 1.5); | |
20::ms => now; | |
} | |
*/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment