//SLUGens released under the GNU GPL as extensions for SuperCollider 3, by Nick Collins, http://composerprogrammer.com/index.html
LTI Linear Time Invariant General Filter Equation
LTI.ar(input, bufnuma, bufnumb, mul, add)
Represents the general LTI filter difference equation in the time domain:
y(n) = b0x(n) + b1x(n-1) + ... + b(Nb)x(n-Nb) + a1y(n-1) + ... + a(Na)y(n-Na)
This is not a pole/zero view, so you'd need to calculate time domain coefficients yourself if you want to work from z-plane backwards. A corollary is, stability is not guaranteed. This is part of the fun?
You need to pass in the coefficients via two buffers, of arbitrary size.
input- What do you want to filter?
bufnuma- Feedback filter coefficients, from previous outputs
bufnumb- Feedforward filter coefficients, from previous inputs
(
a=[0.02,-0.01]; //feedback coefficients
b=[1,0.7,0,0,0,0,-0.8,0,0,0,0,0.9,0,0,0,-0.5,0,0,0,0,0,0,0.25,0.1,0.25]; //feedforward coefficients
c=Buffer.sendCollection(s, a, 1);
d=Buffer.sendCollection(s, b, 1);
)
{LTI.ar(AudioIn.ar,c.bufnum, d.bufnum)}.play
//Note- you cannot update buffers during playback unless you stay within the initially allocated sizes
(
a=Array.fill(100,{0.0}); //feedback coefficients
b=Array.rand(100,-0.5,0.5); //feedforward coefficients
b[0]=1;
c=Buffer.sendCollection(s, a, 1);
d=Buffer.sendCollection(s, b, 1);
)
{LTI.ar(AudioIn.ar,c.bufnum, d.bufnum)}.play
(
b=Array.rand(100,-0.5,0.5); //feedforward coefficients
b[0]=1;
d.sendCollection(b);
)
//may explode...
(
10.do({arg i; a[100.rand]=rrand(-0.1,0.1)}); //feedforward coefficients
c.sendCollection(a);
)
//from a routine
(
e={inf.do {
b=Array.rand(100,-0.5,0.5); //feedforward coefficients
b[0]=1;
d.sendCollection(b);
0.1.wait; }}.fork
)
e.stop;
//Code for testing and trying coefficients:
//given two arrays of filter coefficients, calculate an impulse response over 1024 samples, then the fft gives approximate frequency gain and phase response
(
var size = 1024, real, imag, cosTable, complex;
var a,b;
var lastn,lastindex,num;
var y, max;
a=[0.02,0.05,0,0,0.01]; //feedback coefficients
b=[1,1,-0.5,0,0,0,-0.6,0.7]; //feedforward coefficients
//check poles of a are inside the unit circle by factorising the complex polynomial?
//this procedure uses only a finite impulse response so may give fallacious results of stability
num=a.size;
lastn=Array.fill(num,{0});
lastindex=0;
real = Signal.fill(size, {arg i;
y=if(i<(b.size),{b[i]},{0});
y= y+((a.collect({arg val,j; val*(lastn.wrapAt(lastindex+num-1-j));})).sum);
lastn[lastindex]=y;
lastindex=(lastindex+1)%num;
y
});
imag = Signal.newClear(size);
cosTable = Signal.fftCosTable(size);
complex = fft(real, imag, cosTable);
a=complex.postln;
[real, (complex.magnitude), (complex.phase) ].flop.flat
.plot("fft", Rect(0,0, 1024 + 8, 500), numChannels: 3);
max=0;
y=complex.magnitude;
y.do{arg val; if(val>max,{max=val;})};
max
)
//how to create the arbitrary filter from its difference equation coefficients? Need a new UGen (LTI)- or use Csound
(
a=[0.02,0.05,0,0,0.01]; //feedback coefficients
b=[1,1,-0.5,0,0,0,-0.6,0.7]; //feedforward coefficients
c=Buffer.sendCollection(s, a, 1);
d=Buffer.sendCollection(s, b, 1);
)
{Impulse.ar(1)}.play
{LTI.ar(Impulse.ar(1), c.bufnum, d.bufnum)}.play
{LTI.ar(AudioIn.ar(1), c.bufnum, d.bufnum)}.play
(
a=[0.01,-0.01]; //Array.fill(10,{rrand(0.001,0.01)}); //feedback coefficients
b=[1]++Array.fill(100,{exprand(0.1,1)}); //feedforward coefficients
c=Buffer.sendCollection(s, a, 1);
d=Buffer.sendCollection(s, b, 1);
)
//piercing, careful!
{Saw.ar(LFNoise0.kr(10,4000,5000))}.play
{LTI.ar(Saw.ar(LFNoise0.kr(10,4000,5000)), c.bufnum, d.bufnum,0.1)}.play
//Also see [Convolution]