FormantTable Lookup table for emulating vocal tracts
FormantTable.get(preset)
Returns a set of frequencies+resonances+amplitudes for a set of 5 bandpass filters, useful for emulating the main 5 formants of a vocal tract. To see the available voice types run this line:
FormantTable.keys
Examples
(by Eric Skogen)
// first boot the server
s.boot;
// Saw freq is controlled by mouse X position
// BBandPass filter is controlled by
// FormantTable data (frequencies, resonances, amplitudes)
(
SynthDef(\formantVoice, { arg
f = #[ 400, 750, 2400, 2600, 2900 ],
a = #[ 1, 0.28183829312645, 0.089125093813375, 0.1, 0.01 ],
q = #[ 0.1, 0.10666666666667, 0.041666666666667,
0.046153846153846, 0.041379310344828 ];
var scale = [0, 2, 4, 7, 9]; // pentatonic major
var scaleBuf = LocalBuf(scale.size, 1).set(scale);
var degree = Index.kr(scaleBuf, MouseX.kr(0, BufFrames.kr(scaleBuf)));
var vibrato = SinOsc.kr(6, mul:4);
var base = 4 * 12;
var in = Saw.ar(((degree + base).midicps + vibrato).lag(0.3));
Out.ar(0, Mix.new(BBandPass.ar(in, f, q) * a).dup);
}).send(s);
)
// create a menu of the FormantTable options for preview
(
var voices = FormantTable.keys.asArray;
voices.sort;
// start an instance of the synth
#f, a, q = FormantTable.get( voices.first );
x = Synth(\formantVoice, [\f, f, \a, a, \q, q]);
w = Window("FormantTable Browser",
Rect(Window.screenBounds.width/2 - 150,
Window.screenBounds.height/2 - 30,
300, 60));
StaticText(w, Rect(0, 5, w.view.bounds.width, 20))
.font_(Font("Helvetica", 11))
.string_("Move mouse left/right to change pitch")
.align_(\center);
PopUpMenu(w, w.view.bounds.insetBy(15, 20).moveBy(0, 10))
.items_(voices)
.action_({ |v|
// change the data based on user action
#f, a, q = FormantTable.get( v.item );
x.setn(\f, f);
x.setn(\a, a);
x.setn(\q, q);
});
w.onClose_({ x.free });
w.front;
)
// A laughter generator
// NB mac-only at the moment, sorry
(
#f, a, q = FormantTable.get(\altoA);
e = Env(
[ 1.6315789222717, 2.8684210777283, 0, 1.1526317596436, 0, 0.63157892227173, 0, 0, 0.39473664164831, 0.41710549016785, 0, 0.33685091589462, 0.3538348548878, 0, 0.10526323318481, 0 ],
[ 0.099179289557717, 0.029608596454967, 0.070896452123469, 0.015656579624523, 0.045265132730657, 0.08636364069852, 0.027344267630401, 0.021196740851856, 0.025568173912275, 0.020519098486223, 0.029819674626466, 0.025645296259773, 0.057419430367461, 0.18465909090909, 0.16357317837802 ]
);
w = Window("Laugh Editor", Rect(200,200,400,300));
v = SCEnvelopeEdit(w, w.view.bounds.moveBy(20,20).resizeBy(-40,-140), e, 20).resize_(5);
b = Button(w, w.view.bounds.moveBy(20,200).resizeBy(-40,-240)).states_([["Laugh"]]).resize_(5)
.action_({
x = {
var saw = Saw.ar((EnvGen.kr(v.env, doneAction:2) * 440)); // lazy
var out = Mix.new(BBandPass.ar(saw, f, q) * a).dup;
HPF.ar(out, 250); // avoid blowups
}.play;
});
w.front;
)