Created
June 20, 2017 19:14
-
-
Save joslloand/97de4c48417b35d19f203555d90b4cd6 to your computer and use it in GitHub Desktop.
This file contains 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
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