SuperCollider CLASSES (extension)
Convenience

# Vowel

convenience class to store and manipulate formant information
Source: /Users/zzk/Library/Application Support/SuperCollider/downloaded-quarks/Vowel/Vowel.sc
Inherits from: Object
See also: Formlet, Formant

## Description

A Vowel contains and handles data to describe formants that allow to distinguish the vowel sounds \a, \e, \i, \o, \u for the registers \bass, \tenor, \counterTenor, \alto and \soprano. The current implementation uses formant data that originally appeared in the C-sound Manual: http://www.csounds.com/manual/html/MiscFormants.html

2011 -- Florian Grond, Till Bovermann

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 go to Alberto de Campo and and Julian Rohrhuber.

## Class Methods

### *formLib

holds formant information

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

#### Returns:

a Dictionary

### *new (vowel: 'a', register: 'bass')

creates a new instance of Vowel

#### Arguments:

vowel | select 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'.

#### Returns:

a Vowel

#### Discussion:

    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

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

#### Arguments:

freqs | an array of freqs
dBs | an array of dBs
widths | an array of widths in Hz

#### Returns:

a Vowel

### *compose (vowelNames, registers, weights)

compose your own vowel based on the ones defined in formTable. Wraps when argument lengths differ (as in channel expansion).

#### Arguments:

vowelNames | an array describing the vowels' character: \a, \e, \i, \o, \u.
registers | an array describing the vowels' voice: \bass, \tenor, \counter, \alt, \sopran.
weights | an array describing the vowels' weight relative to the others. Should be a normalized sum.

#### Returns:

a Vowel

#### Discussion:

    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);

    (
    { 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;
    )

### Inherited class methods

### Undocumented class methods

### *initLib

### *load (path)

## Instance Methods

### -freqs

### -freqs = value

Array of freqs, one for each formant.

    v = Vowel(\e, \tenor)
    v.freqs
    v.freqs_({|i| 100 * (i+1) }!5)
    v.freqs

### -dBs

### -dBs = value

Array of dBs,

    v = Vowel(\u, \counterTenor)
    v.dBs
    v.dBs_({|i| -10 * (i) }!5)
    v.dBs

### -widths

### -widths = value

array of widths in Hz, one for each formant.

    v = Vowel(\o, \soprano)
    v.widths
    v.widths_({ 500.rand }!5)
    v.widths

### -amps

array of amps in dB, one for each formant.

### -rqs

array of rQ values, one for each formant.

### -midinotes

array of midinotes, one for each formant.

### -addFormants (freq: 0, db: 1, width: -100)

    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])

#### Arguments:

freq |
db |
width |

### -removeFormants (index)

#### Arguments:

index | index of the formant to remove

### -ampAt (freq, filterOrder: 1)

get amp value for freq according to order of filter

    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)

#### Arguments:

freq |
filterOrder |

### -plot (fmin: 0, fmax: 6000, fstep: 2, order: 1)

plot the frequency spectrum of the vowel.

#### Arguments:

fmin |
fmax |
fstep |
order |

### -asArray

serialise the vowel into an array

#### Returns:

an arrays of [ freqs, dBs, widths ]

### -asEvent

convert into an Event

### -asKeyValuePairs (id: "")

convert into an array of key value pairs

#### Arguments:

id | optional argument added to each key

### -addControls (id: "", rate: 'kr', lag)

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

#### Arguments:

id | Optional. If more than one vowel need to be controlled inside one Synth, this id can be used to differentiate them from each other.
rate | either \kr or \ar
lag | array of lag times

#### Discussion:

    (
    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));

### -+ (that, adverb)

plus

#### Arguments:

that |
adverb |

### -- (that, adverb)

minus

#### Arguments:

that |
adverb |

### -* (that, adverb)

multiply

#### Arguments:

that |
adverb |

### -/ (that, adverb)

divide

#### Arguments:

that |
adverb |

### -blend (that, blendFrac: 0.5)

blends two vowels by linear interpolation between of midinotes, widths and dBs.

#### Arguments:

that | a Vowel
blendFrac | coefficient. Range from 0.0 (this) to 1.0 (that).

#### Returns:

a new instance of Vowel

### -brightenRel (bright: 1, refFormant: 0)

lift the upper formants by multiplying their dBs with a factor. The sum of all dBs remains fixed.

#### Arguments:

bright | coefficient to brighten the vowel. Typical ranges are from 1 (no change) to 3.
refFormant | reference formant, whose amplitude remains unchanged. Default value is 0 (first formant)

#### Discussion:

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: 1, refFormant: 0)

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

#### Arguments:

bright | coefficient to brighten the vowel. Typical ranges are from 1 (no change) to 3.
refFormant | reference formant, whose amplitude remains unchanged. Default value is 0 (first formant)

#### Discussion:

    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: 1)

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

#### Arguments:

bright | coefficient to brighten the vowel. Typical ranges are from 1 (no change) to 3.

#### Discussion:

    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;

### -asKlankRef (baseFreq: 440, numPartials: 13, ring: 1)

(describe method here)

#### Arguments:

baseFreq | undocumented
numPartials |
ring |

### -printOn (stream)

prettyprint support

#### Arguments:

stream |

### -storeArgs

archival support

### Inherited instance methods

### Undocumented instance methods

### -asFlatArray

### -performBinaryOpOnSimpleNumber (aSelector, aNumber)

### -performBinaryOpOnVowel (aSelector, aVowel)

### -save (path, timbre: 'mytimbre', register: 'myregister')

## Examples

In SynthDefs:

    (
    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));

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
    )

helpfile source: /Users/zzk/Library/Application Support/SuperCollider/downloaded-quarks/Vowel/HelpSource/Classes/Vowel.schelp
link::Classes/Vowel::
sc version: 3.8.0