Skip to content

Instantly share code, notes, and snippets.

@joslloand
Created June 20, 2017 19:14
Show Gist options
  • Save joslloand/97de4c48417b35d19f203555d90b4cd6 to your computer and use it in GitHub Desktop.
Save joslloand/97de4c48417b35d19f203555d90b4cd6 to your computer and use it in GitHub Desktop.
SSB {} // for viewing class with shortcut
// a 12 pole (6 per side) Hilbert IIR filter
// based on Sean Costello and Bernie Hutchins
// created by jl anderson - 7 jan 2001
HilbertIIR {
*ar {
arg in, // input signal
mul = 1.0,
add = 0.0;
var numPoles, poles, gammas, coefs;
var hilbertCos, hilbertSin;
var out;
// values taken from Bernie Hutchins, "Musical Engineer's Handbook"
numPoles = 12;
poles = [ 0.3609, 2.7412, 11.1573, 44.7581, 179.6242, 798.4578,
1.2524, 5.5671, 22.3423, 89.6271, 364.7914, 2770.1114 ];
// gammas = (15.0 * pi / Synth.sampleRate) * poles;
gammas = (15.0 * pi / SampleRate.ir) * poles;
coefs = [];
numPoles.do({ arg i;
coefs = coefs.add((gammas.at(i)-1)/(gammas.at(i)+1))
});
// Cos and Sin - not the prettiest - but it works!!!
hilbertCos = in;
numPoles.div(2).do({ arg i;
hilbertCos = FOS.ar(hilbertCos, coefs.at(i), 1.0, coefs.at(i).neg)
});
hilbertSin = in;
numPoles.div(2).do({ arg i;
hilbertSin = FOS.ar(hilbertSin, coefs.at(i+6), 1.0, coefs.at(i+6).neg)
});
// ^[ hilbertCos, hilbertSin ]
^( mul * ( add + [ hilbertCos, hilbertSin ] ) )
}
*ar1 {
arg in, // input signal
mul = 1.0,
add = 0.0;
var numPoles, poles, gammas, coefs, b1, b2;
var hilbertCos, hilbertSin;
var out;
// values taken from Bernie Hutchins, "Musical Engineer's Handbook"
// also found in Electronotes #43
numPoles = 12;
// pole values are grouped in a strange order, to allow for easy
// generation of the second order coefficients
poles = [ 0.3609, 798.4578, 2.7412, 179.6242, 11.1573, 44.7581,
1.2524, 2770.1114, 5.5671, 364.7914, 22.3423, 89.6271 ];
// math for bilinear transform of pole coefficients for 1st order allpass filters
// gammas = (15.0 * pi / Synth.sampleRate) * poles;
gammas = (15.0 * pi / SampleRate.ir) * poles;
coefs = [];
numPoles.do({ arg i;
coefs = coefs.add((gammas.at(i)-1)/(gammas.at(i)+1))
});
// 1st order allpass filters coefs are grouped into coefs for 2nd order sections
b1 = [];
b2 = [];
numPoles.div(2).do({ arg i;
b1 = b1.add(coefs.at(2*i) + coefs.at((2*i)+1));
b2 = b2.add(coefs.at(2*i) * coefs.at((2*i)+1));
});
b1.postln; b2.postln;
// Cos and Sin - not the prettiest - but it works!!!
hilbertCos = in;
numPoles.div(4).do({ arg i;
hilbertCos = SOS.ar(hilbertCos, b2.at(i), b1.at(i), 1.0, b1.at(i).neg, b2.at(i).neg)
});
hilbertSin = in;
numPoles.div(4).do({ arg i;
hilbertSin = SOS.ar(hilbertSin, b2.at(i+3), b1.at(i+3), 1.0, b1.at(i+3).neg, b2.at(i+3).neg)
});
// ^[ hilbertCos, hilbertSin ]
^( mul * ( add + [ hilbertCos, hilbertSin ] ) )
}
}
// new version using convoloution
HilbertConv {
*ar { arg in, size=2048, mul=1.0, add=0.0;
var kernel_r, kernel_i;
var hilbertCoeffs, r, i, real, imag;
hilbertCoeffs = {|size|
var xReal, xImag, reflect, window, half_win, arr;
half_win = (size-1)/2;
reflect = [1.0, -1.0].dup(size/2).flat;
window = Signal.hanningWindow(size);
// real response
xReal = Array.series(size, half_win.neg, 1);
xReal = xReal.collect({|i| (i == 0).if({ 1 }, { sin(pi * i) / (pi * i) }) }) * window;
// imaginary response
xImag = xReal * reflect;
[xReal, xImag]
};
#r, i = hilbertCoeffs.(size);
kernel_r = LocalBuf(size, 1).set(r);
kernel_i = LocalBuf(size, 1).set(i);
#real, imag = Convolution2.ar(in, [kernel_r, kernel_i], framesize: size);
^( mul * ( add + [ real, imag ] ) )
}
}
// a Hilbert SSB. . . using the Hilbert IIR
HilbertSSB {
*ar {
arg in, // input signal
freq = 0.0, // shift, in cps
phase = 0.0, // phase of SSB
mul = 1.0,
add = 0.0;
// multiply by quadrature
// and add together. . .
^(mul * (add + Mix.ar(HilbertIIR.ar(in) * SinOsc.ar(freq,
(phase + [ 0.5*pi, 0.0 ])))))
}
}
HilbertLSB {
*ar {
arg in, // input signal
freq = 0.0, // shift, in cps
phase = 0.0, // phase of SSB
mul = 1.0,
add = 0.0;
var cos_term, sin_term;
cos_term = SinOsc.ar(freq, phase + 0.5*pi);
sin_term = SinOsc.ar(freq, phase).neg;
// multiply by quadrature
// and add together. . .
^(mul * (add + Mix.ar(HilbertIIR.ar(in) * [cos_term, sin_term])))
}
}
// filters steepened - mtm
SSBAM {
*arUp {
arg in, amp=1.0, car_freq;
var cos_term, sin_term;
cos_term = SinOsc.ar(car_freq, 0.5*pi, 0.5);
sin_term = SinOsc.ar(car_freq, 0, 0.5);
^(BHPF.ar(
Mix.new( HilbertIIR.ar(in, 1.0, 1.0) * [cos_term, sin_term] ),
10, car_freq, amp)
);
}
*arLow {
arg in, amp=1.0, car_freq;
var cos_term, sin_term;
cos_term = SinOsc.ar(car_freq, 0.5*pi, 0.5);
sin_term = SinOsc.ar(car_freq, 0, 0.5).neg;
^(BLPF.ar(
Mix.new( HilbertIIR.ar(in, 1.0, 1.0) * [cos_term, sin_term] ),
10, car_freq, amp)
);
}
}
// single side band modulation using convolution
SSBAMConv {
*arLow {
arg in, amp=1.0, car_freq, size=2048;
var cos_term, sin_term;
cos_term = SinOsc.ar(car_freq, 0.5*pi, 0.5);
sin_term = SinOsc.ar(car_freq, 0, 0.5);
^( HilbertConv.ar(in + 1.0, size)
* amp * [cos_term, sin_term] ).sum;
}
*arUp {
arg in, amp=1.0, car_freq, size=2048;
var cos_term, sin_term;
cos_term = SinOsc.ar(car_freq, 0.5*pi, 0.5);
sin_term = SinOsc.ar(car_freq, 0, 0.5).neg;
^( HilbertConv.ar(in + 1.0, size)
* amp * [cos_term, sin_term] ).sum;
}
}
// a 12 pole (6 per side) Hilbert IIR filter
// based on Bernie Hutchins phase differencing network, Electronotes #43
// Supercollider class created by JL Anderson & Sean Costello
// uses 2nd order sections for efficiency
/*
HilbertIIR2 {
*ar {
arg in, // input signal
mul = 1.0,
add = 0.0;
var numPoles, poles, gammas, coefs, b1, b2;
var hilbertCos, hilbertSin;
var out;
// values taken from Bernie Hutchins, "Musical Engineer's Handbook"
// also found in Electronotes #43
numPoles = 12;
// pole values are grouped in a strange order, to allow for easy
// generation of the second order coefficients
poles = [ 0.3609, 798.4578, 2.7412, 179.6242, 11.1573, 44.7581,
1.2524, 2770.1114, 5.5671, 364.7914, 22.3423, 89.6271 ];
// math for bilinear transform of pole coefficients for 1st order allpass filters
// gammas = (15.0 * pi / Synth.sampleRate) * poles;
gammas = (15.0 * pi / SampleRate.ir) * poles;
coefs = [];
numPoles.do({ arg i;
coefs = coefs.add((gammas.at(i)-1)/(gammas.at(i)+1))
});
// 1st order allpass filters coefs are grouped into coefs for 2nd order sections
b1 = [];
b2 = [];
numPoles.div(2).do({ arg i;
b1 = b1.add(coefs.at(2*i) + coefs.at((2*i)+1));
b2 = b2.add(coefs.at(2*i) * coefs.at((2*i)+1));
});
b1.postln; b2.postln;
// Cos and Sin - not the prettiest - but it works!!!
hilbertCos = in;
numPoles.div(4).do({ arg i;
hilbertCos = SOS.ar(hilbertCos, b2.at(i), b1.at(i), 1.0, b1.at(i).neg, b2.at(i).neg)
});
hilbertSin = in;
numPoles.div(4).do({ arg i;
hilbertSin = SOS.ar(hilbertSin, b2.at(i+3), b1.at(i+3), 1.0, b1.at(i+3).neg, b2.at(i+3).neg)
});
// ^[ hilbertCos, hilbertSin ]
^( mul * ( add + [ hilbertCos, hilbertSin ] ) )
}
}
*/
// single sideband amplitude modulation, using optimized Hilbert phase differencing network
// basically coded by Joe Anderson, except Sean Costello changed the word HilbertIIR.ar
// to Hilbert.ar
//FreqShift {
//
// *ar {
// arg in, // input signal
// freq = 0.0, // shift, in cps
// phase = 0.0, // phase of SSB
// mul = 1.0,
// add = 0.0;
//
// // multiply by quadrature
// // and add together. . .
// ^(mul * (add + Mix.ar(Hilbert.ar(in) * SinOsc.ar(freq,
//(phase + [ 0.5*pi, 0.0 ])))))
// }
//}
// quick test of the above so it expands properly with multichannel input
// outputs [[real, imag],[real, imag],...]
HilbertConvExpand {
*ar { arg in, size=2048, mul=1.0, add=0.0;
var hilbertCoeffs, r, i;
hilbertCoeffs = {|size|
var xReal, xImag, reflect, window, half_win, arr;
half_win = (size-1)/2;
reflect = [1.0, -1.0].dup(size/2).flat;
window = Signal.hanningWindow(size);
// real response
xReal = Array.series(size, half_win.neg, 1);
xReal = xReal.collect({|i| (i == 0).if({ 1 }, { sin(pi * i) / (pi * i) }) }) * window;
// imaginary response
xImag = xReal * reflect;
[xReal, xImag]
};
#r, i = hilbertCoeffs.(size);
^in.asArray.collect({|input|
var real, imag;
var kernel_r, kernel_i;
kernel_r = LocalBuf(size, 1).set(r);
kernel_i = LocalBuf(size, 1).set(i);
#real, imag = Convolution2.ar(input, [kernel_r, kernel_i], framesize: size);
( mul * ( add + [ real, imag ] ) )
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment