Vowel

Inherits from: Object


a Vowel contains and manipulates the Formants of a vowel sound. 

Implememted jointly by Florian Grond and Till Bovermann, June 2011



This class implementation was supported by:

 the Ambient Intelligence Group, CITEC ( http://www.techfak.uni-bielefeld.de/ags/ami )  Bielefeld University, and

 the TAI Studio ( http://TAI-Studio.org ), Department of Media, Aalto University, Helsinki.

Many thanks to Alberto de Campo and and Julian Rohrhuber.



the vowel class contains and handles data to describe the 5 Formants that allow to distinguish the vowel sounds \a, \e, \i, \o, \u for the registers \bass, \tenor, \counterTenor, \alto, \soprano.

The centre frequency, width and gain of these formants are taken from the C-soundmanual: 

http://www.csounds.com/manual/html/MiscFormants.html


The vowel class and ugens


the vowel class is meant to be used together with the two pseudo ugen classes Formants.ar and BPFStack.ar (see below).


// Start internal server

s = Server.internal.boot;

Server.default = Server.internal;

// Create analyzer in a window you might want to see the spectrum of your vowels

(

w = Window("Frequency Spectrum", Rect(0, 0, 300, 200)); // width should be 511

f = FreqScopeView(w, w.view.bounds);

f.active_(true); // turn it on the first time;

f.dbRange_(100); // turn it on the first time;

w.onClose_({ f.kill }); // you must have this

w.front;

)


Creation / Class Methods


*new (vowel, register)

creates a new instance of Vowel

vowel - selecty a vowel by the symbols \a, \e, \i, \o, \u. default value is 'a'.

register - select a register by the symbols \bass, \tenor, \counterTenor, \alto, \soprano. Default value is 'bass'.

Vowel(); // defaults to A bass

Vowel(\a, \bass); 

Vowel(\e, \tenor); 

Vowel(\i, \counterTenor); 

Vowel(\o, \alto); 

Vowel(\u, \soprano); 

{Formants.ar(100, Vowel(\e, \bass)) * 0.1 }.play

{Formants.ar(200, Vowel(\o, \alto)) * 0.1 }.play

{Formants.ar(300, Vowel(\i, \soprano)) * 0.1 }.play

The class exhibits multichannel expansion behaviour

{Formants.ar(100, Vowel([\e, \o], [\bass])) * 0.1 }.play

{Formants.ar(100, Vowel(\e, [\bass,\alto])) * 0.1 }.play

{Formants.ar(100, Vowel([\e, \o], [\bass,\alto])) * 0.1 }.play


*basicNew (freqs, dBs, widths)

You can also explicitly set the formants

freqs - an array of 5 freqs. Default value is nil. 

dBs -  an array of 5 dBs. Default value is nil. 

widths - an array of 5 widths in Hz. Default value is nil.

Vowel.basicNew([ 300, 400, 2700, 3800, 4950 ], [ 0, -16, -35, -40, -6 ], [ 50, 10, 170, 180, 200 ])


*compose (vowels, registers, weights)

compose your own vowel out of the formTable filled with the initClass method, the arguments are arrays that do not need to be of the same size

vowels - vowels is an array of symbols \a, \e, \i, \o, \u. Default value is nil. 

registers -  registers is an array of symbols \bass, \tenor, \counter, \alt, \sopran.  Default value is nil.

weights - registers is an array of weights Default value is nil, for reasonable values it should be a normalized sum.

Vowel.compose([\a,\e,\i], [\bass,\soprano,\alto],        [0.2, 0.3, 0.5]);

Vowel.compose([\a,\e], [\soprano],                   [9, 4].normalizeSum);

Vowel.compose([\a],       [\tenor, \counterTenor, \soprano], [1, 4, 2].normalizeSum);


Vowel() + Vowel()

(

{ var v = Vowel.compose([\a, \e, \i], [\soprano, \bass, \tenor, \counterTenor, \alto], ({10.rand}!5).normalizeSum);

Formants.ar(50 + 300.rand, v) * 0.1 

}.play

)


*formLib

formLib is a Library and holds the formants freqs, widths, and dBs. of formTable and a link to its help file.

Vowel.formLib.at(\a)

Vowel.formLib.at(\a, \bass)

Vowel.formLib.at(\a, \bass, \freq)

Vowel.formLib.postTree;

save(path, timbre, register)

v = Vowel.compose([\a,\e], [\bass, \tenor], [1,1].normalizeSum )

v.save(Platform.userAppSupportDir++"/myVowelLibrary.scd", \timbre0, \register0)

*load(path)

Vowel.load(Platform.userAppSupportDir++"/myVowelLibrary.scd")

Vowel( \timbre0, \register0)


Accessing Instance variables

freqs_(arg1)

freqs

array of freqs for each vowel of a certain register

v = Vowel(\e, \tenor)

v.freqs

v.freqs_({|i| 100 * (i+1) }!5)

v.freqs

dBs_(arg1)

dBs

array of dBs for each vowel of a certain register


v = Vowel(\u, \counterTenor)

v.dBs

v.dBs_({|i| -10 * (i) }!5)

v.dBs

widths_(arg1)

widths

array of widths in Hz for each vowel of a certain register


v = Vowel(\o, \soprano)

v.widths

v.widths_({ 500.rand }!5)

v.widths

asArray

asArray is a method used internally but aslo for the controlling the pseudo ugens Formants and BPFStack.

It returns an arrays of [ freqs, dBs, widths ] of a vowel instance.

v = Vowel(\i, \soprano)

v.asArray

addFormants (freq, dB, width) 

removeFormants (index)

v = Vowel()

v.addFormants([3000,4000], -6, [200, 200])

{Formants.ar(100, v) * 0.1 }.play

{Formants.ar(100, Vowel()) * 0.1}.play

{[Formants.ar(100, Vowel()) * 0.1, Formants.ar(100, v) * 0.1 ]}.play

v.removeFormants([5,6])

ampAt (freq, filterOrder)

Vowel(\u).ampAt(100);

~range = {|i|i*2}!2000;

Vowel(\u).ampAt(~range  ).plot;

Vowel(\u).ampAt(~range  ).ampdb.plot(minval: -100, maxval: 0);

Vowel(\u).ampAt(~range, 0.5  ).plot;

Vowel(\u).ampAt(~range, 0.5  ).ampdb.plot(minval: -100, maxval: 0);

Vowel(\u).ampAt(~range, 3  ).plot;

Vowel(\u).ampAt(~range,  3 ).ampdb.plot(minval: -100, maxval: 0);

Vowel(\u).ampAt(~range, [3,1,0.5,2]  ).plot

Vowel(\u).ampAt(~range,  [3,1,0.5,2]  ).ampdb.plot(minval: -100, maxval: 0)

blend (that, blendFrac)

blends two vowels with the coefficient blendFrac. The blending is a linear interpolation between  midinotes, widths and dBs.

that - Vowel

blendFrac - coefficient. Default value is 0.5. range from 0.0 to 1.0

~v1 = Vowel(\i, \soprano)

~v2 = Vowel(\o, \alto)

~v1.blend(~v2, 0)

~v1.blend(~v2, 0.5)

~v1.blend(~v2, 1)

~v3 = Vowel(\a, \bass)


{ Formants.ar(150,  ~v1.blend(~v2, MouseX.kr(0,1)).blend(~v3, MouseY.kr(0,1)) ) * 0.1  }.play

blend also allows you to blend two vowels with individual coefficients for freq, amp, and width. 

freqFrac - frequency coefficient. Default value is 0.5. range from 0.0 to 1.0

ampFrac - amplitude coefficient. Default value is 0.5. range from 0.0 to 1.0

widthFrac - bandwidth coefficient. Default value is 0.5. range from 0.0 to 1.0

v = Vowel(\a, \bass)

v = Vowel(\i, \soprano)

v.blend(w, 0)

v.blend(w, 0.5)

v.blend(w, [0.1, 0.5, 0.8])


in the following example you''l notice the biggest contributioin to the recognizability of a vowel are the frequencies (MouseX) and the amplitudes (MouseY).  

The bandwidths do not contribute much (modulated by a sinewave):


{ Formants.ar(150,  Vowel(\a, \bass).blend(Vowel(\u, \bass), MouseX.kr(0,1), MouseY.kr(0,1), SinOsc.kr(0.5, 0, 0.5, 0.5) ) ) * 0.1  }.play

{ Formants.ar(150,  Vowel(\e, \bass).blend(Vowel(\i, \bass), MouseX.kr(0,1), MouseY.kr(0,1), SinOsc.kr(0.5, 0, 0.5, 0.5) ) ) * 0.1  }.play

{ Formants.ar(150,  Vowel(\i, \bass).blend(Vowel(\o, \bass), MouseX.kr(0,1), MouseY.kr(0,1), SinOsc.kr(0.5, 0, 0.5, 0.5) ) ) * 0.1  }.play


brightenRel (bright , refFormant)

allows to lift the upper formants by multiplying their dBs. The sum of all dBs remains fixed.

bright - coefficient to brighten the vowel. Default value is 1 (no change). Values > 3 cause strange behaviour (should be between 0 and 3).

refFormant - reference  formant, whose amplitude remains unchanged. Default value is 0 (first formant)

NOTE: If the coefficient bright is > 1 and the refFormant is > 0, the resulting signal may raise to a very big amplitude

Vowel().brightenRel(1  ).dBs.postln; 

Vowel().brightenRel(0.1).dBs.postln;

Vowel().brightenRel(1.5).dBs.postln;

// refFormant = 0 loud on the left

{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 0 )) * 0.1}.play

// refFormant = 1

{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 1 )) * 0.1}.play

// refFormant = 2

{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 2 )) * 0.1}.play

// refFormant = 3

{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 3 )) * 0.1}.play

// refFormant = 4 loud on the right

{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 4 )) * 0.1}.play

brightenLin (bright , refFormant  )

allows to lift the upper formants by multiplying their dBs. the sum of all dBs remains constant.

bright - coefficient to brighten the vowel. Default value is 1 (no change). do not use very big values (typically between 0 and 3).

refFormant - reference  formant, whose amplitude remains unchanged. Default value is 0 (first formant)

NOTE: If the coefficient bright is > 1 and the refFormant is > 0, the resulting signal may raise to a very big amplitude


Vowel().brightenLin(-1).dBs.postln; 

Vowel().brightenLin( 0).dBs.postln;

Vowel().brightenLin( 1).dBs.postln;

// refFormant = 0 loud on the left

{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 0 )) * 0.1}.play

// refFormant = 1

{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 1 )) * 0.1}.play

// refFormant = 2

{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 2 )) * 0.1}.play

// refFormant = 3

{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 3 )) * 0.1}.play

// refFormant = 4 loud on the right

{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 4 )) * 0.1}.play


brightenExp (bright)

allows to relatively lift the upper formants by keeping the sum of gains of all formants constant.

bright - coefficient to brighten the vowel. Default value is 1 (no change). you can use very small and very big values (typically between 0 and 5).

Vowel().brightenExp(1).dBs.postln; 

Vowel().brightenExp(0.1).dBs.postln;

Vowel().brightenExp(1.5).dBs.postln;

{Formants.ar(100, Vowel(\a, \bass).brightenExp(MouseX.kr(0,5)))  * 0.1 }.play


addControls(id, rate, lag)

creates controls in the wrapping synth such that the vowel's parameters can be controlled seamlessly

from the language.

id - an id to identify one of possibly several vowels to control. Optional.

rate - either kr or ar.

lag - lagtimes


(

x = SynthDef(\vowel, {|freq = 420|

Out.ar(0, 

Formants.ar(freq.lag(0.1), Vowel(\a).addControls(lag: 1)) * 0.01

)

}).play

)

x.setn(*([\freq, exprand(100, 1000)] ++ Vowel(\u, \soprano).asKeyValuePairs));

x.setn(*([\freq, exprand(100, 500)] ++ Vowel(\e, \bass).asKeyValuePairs));

x.setn(*([\freq, exprand(400, 1000)] ++ Vowel(\a, \soprano).asKeyValuePairs));

PseudoUgens to be used with Vowel:

Formants.ar(baseFreq,  vowel,  freqMods, ampMods, widthMods, unfold )

Formants is a Stack of the Formant ugens it needs a baseFreq and either an arrays of  #freqs,  #dBs and #widths or a Vowel.

baseFreq - base frequency. Default value is 100.

vowel - an instamnce of Vowel, has no Default value.

freqMods -  frequency modulation. Default value is 1.

ampMods - amplitude modulation. Default value is 1.

widthMods - bandwidth modulation. Default value is 1.

unfold - whether to apply channel expansion or not. Default value is false.

on how "Formants" works internally:


since Formants are PseudoUgens, they return a BinaryOpUGen

Formants.ar(400, Vowel(\a)) 

// -> a BinaryOpUGen


if you set unfold to true: you see a Formant plus 4 BinaryOpUGen 

Formants.ar(400, Vowel(\a), unfold: true)

// [ a Formant, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ]

this gives the same as unfold: false

Formants.ar(400, Vowel(\a), unfold: true).sum 

// a BinaryOpUGen


here you get an array of two unfolded Formants the first with 200 hz the second with 400 Hz

Formants.ar([400, 200], Vowel(\a), unfold: true) 

// [ [ a Formant, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ], [ a Formant, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ] ]


this gives two folded Formants the first with 200 hz the second with 400 Hz

Formants.ar([400, 200], Vowel(\a), unfold: false) 

// [ a BinaryOpUGen, a BinaryOpUGen ]

the same applies if you have one basefrequency and several Vowels

Formants.ar(200, [Vowel(\a), Vowel(\a)], unfold: true)

// [ [ a Formant, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ], [ a Formant, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ] ]

Formants.ar(200, [Vowel(\a), Vowel(\a)], unfold: false)

// [ a BinaryOpUGen, a BinaryOpUGen ]


or several basefrequencies and several Vowels

Formants.ar([400, 200], [Vowel(\a), Vowel(\a)], unfold: false)

// [ a BinaryOpUGen, a BinaryOpUGen ]

Formants.ar([400, 200], [Vowel(\a), Vowel(\a)], unfold: true)

// [ [ a Formant, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ], [ a Formant, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ] ]

Note the difference:

Formants.ar([400, 200], Vowel(\a), unfold: true).sum // which is summing the first two formants (400, 200) Hz pairwise till the fifth formant

// [ a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ]

Formants.ar([400, 200], Vowel(\a), unfold: true).collect({|vow| vow.sum}) // only this equals to unfold: false

// [ a BinaryOpUGen, a BinaryOpUGen ]

listening section:

{Formants.ar(150, Vowel(\a))}.play // is equal to

{Formants.ar(150, Vowel(\a), unfold: true).sum }.play

{Formants.ar(150, Vowel(\a), unfold: true)}.play // on headphones you'll ony hear formant1 in chan1 and formant2 in chan2 the rest is in chan 3..5

{Formants.ar([100, 200], Vowel(\u), unfold: false)}.play // you hear 100 Hz U on chan1 and 200 Hz on chan2

 

{Formants.ar(100, [Vowel(\a), Vowel(\e)], unfold: true)}.play // \a on chan1 and \e on chan2 

{Formants.ar([150, 200], [Vowel(\a, \bass), Vowel(\i, \counterTenor)], unfold: true)}.play // \a 150 Hz chan1  \i 200 Hz chan2

using freqMods, ampMods, widthMods:

{Formants.ar(70, Vowel(\e), freqMods: SinOsc.kr(1,0,0.1,1) )}.play; 

{Formants.ar(70, Vowel(\e), freqMods: [SinOsc.kr(1,0,0.2,1), SinOsc.kr(1.1,0,0.2,1), SinOsc.kr(1.2,0,0.2,1), SinOsc.kr(1.3,0,0.2,1), SinOsc.kr(1.4,0,0.2,1) ] )}.play; 

{Formants.ar(70, Vowel(\e), ampMods: SinOsc.kr(1,0,0.5,1) )}.play; 

{Formants.ar(70, Vowel(\e), ampMods: [SinOsc.kr(1,0,0.5,1), SinOsc.kr(1.1,0,0.5,1), SinOsc.kr(1.2,0,0.5,1), SinOsc.kr(1.3,0,0.5,1), SinOsc.kr(1.4,0,0.5,1) ] )}.play; 

{Formants.ar(70, Vowel(\e), widthMods: SinOsc.kr(1,0,0.99,1) )}.play; 

{Formants.ar(70, Vowel(\e), widthMods: [SinOsc.kr(1,0,0.99,1), SinOsc.kr(1.1,0,0.99,1), SinOsc.kr(1.2,0,0.99,1), SinOsc.kr(1.3,0,0.99,1), SinOsc.kr(1.4,0,0.99,1) ] )}.play; 

all together now:

{Formants.ar(70, Vowel(\e), freqMods: SinOsc.kr(1,0,0.1,1),  ampMods: SinOsc.kr(1.1,0,0.5,1),  widthMods: SinOsc.kr(1.2,0,0.999,1) )}.play; 

(

{Formants.ar(70, Vowel(\u), 

freqMods: [SinOsc.kr(1,0,0.2,1), SinOsc.kr(1.1,0,0.2,1), SinOsc.kr(1.2,0,0.2,1), SinOsc.kr(1.3,0,0.2,1), SinOsc.kr(1.4,0,0.2,1) ],  

ampMods: [SinOsc.kr(1,0,0.5,1), SinOsc.kr(1.1,0,0.5,1), SinOsc.kr(1.2,0,0.5,1), SinOsc.kr(1.3,0,0.5,1), SinOsc.kr(1.4,0,0.5,1) ],  

widthMods: [SinOsc.kr(1,0,0.99,1), SinOsc.kr(1.1,0,0.99,1), SinOsc.kr(1.2,0,0.99,1), SinOsc.kr(1.3,0,0.99,1), SinOsc.kr(1.4,0,0.99,1) ])

}.play; 

)

(

{Formants.ar(SinOsc.ar(5, 0, 10, 80), Vowel(\i), 

freqMods: [SinOsc.kr(1,0,0.2,1), SinOsc.kr(1.1,0,0.2,1), SinOsc.kr(1.2,0,0.2,1), SinOsc.kr(1.3,0,0.2,1), SinOsc.kr(1.4,0,0.2,1) ],  

ampMods: [SinOsc.kr(1,0,0.5,1), SinOsc.kr(1.1,0,0.5,1), SinOsc.kr(1.2,0,0.5,1), SinOsc.kr(1.3,0,0.5,1), SinOsc.kr(1.4,0,0.5,1) ],  

widthMods: [SinOsc.kr(1,0,0.99,1), SinOsc.kr(1.1,0,0.99,1), SinOsc.kr(1.2,0,0.99,1), SinOsc.kr(1.3,0,0.99,1), SinOsc.kr(1.4,0,0.99,1) ])

}.play; 

)



use unfolding for independently spatialized formants of one vowel (use Mouse)

{ [Formants.ar(100, Vowel(\o), unfold: true),  {|i| SinOsc.ar(MouseY.kr(0.2,1), ((i*MouseX.kr(0,pi*0.5))) )}!5 ].flop.collect({|args| Pan2.ar( args[0], args[1] ) }).sum }.play


and more complex with the modulation from above (use Mouse)

(

{ [Formants.ar(60, Vowel(\o).brightenRel(MouseY.kr(0,2), 1), 

freqMods: [SinOsc.kr(1,0,0.2,1), SinOsc.kr(1.1,0,0.2,1), SinOsc.kr(1.2,0,0.2,1), SinOsc.kr(1.3,0,0.2,1), SinOsc.kr(1.4,0,0.2,1) ],  

ampMods: [SinOsc.kr(1,0,0.5,1), SinOsc.kr(1.1,0,0.5,1), SinOsc.kr(1.2,0,0.5,1), SinOsc.kr(1.3,0,0.5,1), SinOsc.kr(1.4,0,0.5,1) ],  

widthMods: [SinOsc.kr(1,0,0.99,1), SinOsc.kr(1.1,0,0.99,1), SinOsc.kr(1.2,0,0.99,1), SinOsc.kr(1.3,0,0.99,1), SinOsc.kr(1.4,0,0.99,1) ]),  

{|i| SinOsc.ar((i+1)/MouseX.kr(0.2,1), ((i*pi)/10) )}!5 ]

.flop.collect({|args| Pan2.ar( args[0], args[1] ) }).sum }.play

)



BPFStack.ar(in,  vowel,  freqMods, ampMods, widthMods, unfold )

Formants is a Stack of the BPF filters it needs an in to chew on and either an arrays of  #freqs,  #dBs and #widths or a Vowel.

in - exciting Signal. Default value is nil.

BPFStack.ar(Impulse.ar(1), Vowel(\a)) 

~freq = 100;

{BPFStack.ar(Decay.ar(Saw.ar(~freq, 0.1), ~freq.reciprocal), Vowel(\a)) * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(Decay.ar(Saw.ar(~freq, 0.1), ~freq.reciprocal), Vowel(\e)) * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(Decay.ar(Saw.ar(~freq, 0.1), ~freq.reciprocal), Vowel(\i)) * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(Decay.ar(Saw.ar(~freq, 0.1), ~freq.reciprocal), Vowel(\o)) * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(Decay.ar(Saw.ar(~freq, 0.1), ~freq.reciprocal), Vowel(\u)) * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(ClipNoise.ar(0.3), Vowel(\a))  * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(ClipNoise.ar(0.3), Vowel(\e))  * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(ClipNoise.ar(0.3), Vowel(\i))  * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(ClipNoise.ar(0.3), Vowel(\o))  * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play

{BPFStack.ar(ClipNoise.ar(0.3), Vowel(\u))  * EnvGen.kr(Env.perc, 2.0, doneAction: 2)}.play



and more complex with the modulation from above (use Mouse)

(

{ [BPFStack.ar(ClipNoise.ar(0.1), Vowel(\i).brightenExp(MouseY.kr(0,2), 1), 

freqMods: [SinOsc.kr(1,0,0.2,1), SinOsc.kr(1.1,0,0.2,1), SinOsc.kr(1.2,0,0.2,1), SinOsc.kr(1.3,0,0.2,1), SinOsc.kr(1.4,0,0.2,1) ],  

ampMods: [SinOsc.kr(1,0,0.5,1), SinOsc.kr(1.1,0,0.5,1), SinOsc.kr(1.2,0,0.5,1), SinOsc.kr(1.3,0,0.5,1), SinOsc.kr(1.4,0,0.5,1) ],  

widthMods: [SinOsc.kr(1,0,0.99,1), SinOsc.kr(1.1,0,0.99,1), SinOsc.kr(1.2,0,0.99,1), SinOsc.kr(1.3,0,0.99,1), SinOsc.kr(1.4,0,0.99,1) ]),  

{|i| SinOsc.ar((i+1)/MouseX.kr(0.2,1), ((i*pi)/10) )}!5 ]

.flop.collect({|args| Pan2.ar( args[0], args[1] ) }).sum }.play

)




Examples


the following example show how Vowels can be used within a SynthDef:


(

SynthDef(\vowblend,{|freq = 100, b1 = 0.5, b2 = 0.5, b3 = 0.5, b4 = 0.5 bright = 0, pan = 0|

var va = Vowel(\a, \bass),

    ve = Vowel(\e, \tenor),

    vi = Vowel(\i, \counterTenor),

    vo = Vowel(\o, \alto),

    vu = Vowel(\u, \soprano),

    sig;

   

    sig =  Formants.ar(

    freq, 

    va

    .blend(ve, b1)

.blend(vi, b2)

.blend(vo, b3)

.blend(vu, b4)

.brightenExp(bright, 1) 

) 

* EnvGen.kr(Env.perc, 3.0, doneAction: 2);  


Out.ar(0,  Pan2.ar(sig, pan, 0.1));

}).add

)



(

Task({  32.do({ arg i; 

Synth(\vowblend, [

\pan, i.linlin(0,32, -1, 1 ),

\freq, i.linlin(0,32, 30, 66 ).midicps,

\b1, 2.rand,

\b2, 2.rand, 

\b3, 2.rand,

\b4, 2.rand, 

\bright,  1.5.rand

]); 

0.25.wait


});}).play

)



using addControls to create buses 


(

x = SynthDef(\test, {

Out.ar(0, 

Formants.ar(420, Vowel(\a).addControls(3)) * 0.01

)

}).play

)


x.inspect


x.setn(*Vowel(\i).asKeyValuePairs(3));



the following example show how Vowels can be used within JITLib style:



NdefMixer(s);


Ndef(\vowel, {Formants.ar(200, Vowel(\a, \soprano))  * 0.01 }).play


(

Ndef(\vowel, {

Formants.ar(200, 

Vowel(\o, \soprano)

.blend(Vowel(\i, \tenor), SinOsc.kr(10).range(0,1))) * 0.01

}).play

)



(

Ndef(\vowel, {

Formants.ar(LFNoise0.kr(10).exprange(100, 400), 

Vowel(\o, \soprano)

.brightenExp(SinOsc.kr(2).range(0,1), 1),

unfold: true

).mean * 0.01

}).play

)



Ndef(\vowel, {

Formants.ar(200, Vowel(\a, \soprano).addControls(4)) * 0.01

}).play


Ndef(\vowel).setn(*Vowel(\e, \bass).asKeyValuePairs(4).flatten)

Ndef(\vowel).setn(*Vowel(\u).asKeyValuePairs(4).flatten)



Ndef(\vowel, {

Formants.ar(200, Vowel(\a, \soprano), unfold: true).scramble.keep(2) * 0.1

}).play


Ndef(\vowel).free(2)


Ndef(\vowel).fadeTime = 4;


(

Ndef(\vowel, {

Formants.ar([1, 2, 4] * 240 * {LFNoise1.kr(5, 0.003, 1)}!3, Vowel(\a, [\bass, \tenor, \soprano]), 

freqMods: LFNoise1.ar(4*[0.1, 0.2, 0.3, 0.4, 0.5].scramble, 0.1, ampMods: [1, 1, 1, 0]

)).sum * 0.1

}).play

)




// FUN:



( // CPU demanding 

~freqs = {|i| 50 * 1.5.pow(i) }!9;

~numChan = 2;

r = Routine{

var sustain = 8, transition = 3, overlap = 4;

var period = 1.5 * 2.sqrt;

var harms, amps;

0.5.wait;

inf.do{

harms = {|i| (i+1) * ~freqs.choose }!60;

amps  = Vowel([\a,\e,\i,\o,\u].choose,[\bass,\tenor,\counterTenor,\alto,\soprano].choose).ampAt(harms);

{ PanAz.ar(~numChan, DynKlank.ar( `[~freqs,amps,amps],

Decay.ar(Impulse.ar( exp(1)/5.0 ), SinOsc.kr( pi/9.0, 1.0.rand ).range(0.05,0.7) )   ) *

EnvGen.kr(Env.new([-40,-20, -30,-40].dbamp, [2/5.0, 1/5.0,2/5.0],'exponential'), 1.0, timeScale: 35, levelScale: 0.1, doneAction: 2) ,SinOsc.kr(0.5, 1.0.rand) )}.play;

period.wait;

}

};

r.play;

)

r.stop; //stop spawning new synths



(

Ndef(\vowel).fadeTime = 5;

Ndef(\vowel, {

var freqs, dBs, widths, out;

var baseFreq = LFNoise0.kr([5, 10] * 0.1).round(0.1).exprange(50, 200) * [2, 1.01];

#freqs, dBs, widths =  (Vowel(\i, \soprano).blend(Vowel(\o, \bass), LFNoise1.kr(0.1265))).blend(Vowel(\e, \bass), LFNoise1.kr(10)).asArray;

//freqs = freqs * SinOsc.ar([0.1, 0.2, 0.3, 0.4].scramble, Rand(), 0.1, 1);

freqs = freqs * LFNoise1.ar([0.1, 0.2, 0.3, 0.4].scramble, 0.1, 1);

out = [freqs, widths, dBs.dbamp].flop.collect{ |args| 

Formant.ar(baseFreq, *args); 

}.flop;


out = out.collect{|vocal|

Splay.ar(vocal)

}.sum.postln;


out

* LFPulse.ar([9, 9.01], 0, 0.4).range(0, 1).lag(0.01, 0.5) 

* LFPulse.ar(0.1, [0, 0.35], [0.9, 0.8]).lag(0.01)


 * 0.1


}).play

)