// title: Simple Vocable Synthesiser // author: uiae // description: // A SuperCollider version of Alex McLean's vocable synthesiser. The original is written in haskell and uses hsc3. // // [Source Code](http://yaxu.org/software/vocable/) // // [Background and examples of the original system on Alex's blog](http://yaxu.org/category/vocable/) // code: // alex mclean's vocable synthesis // see: // http://yaxu.org/software/vocable/ // http://yaxu.org/category/vocable/ ( Server.default.waitForBoot({ q = (); q.putnam = ( sA: // soprano [ [800, 1150, 2900, 3900, 4950] , [0, -6, -32, -20, -50] , [80, 90, 120, 130, 140] ] ,sE: [ [350, 2000, 2800, 3600, 4950] , [0, -20, -15, -40, -56] , [60, 100, 120, 150, 200] ] ,sI: [ [270, 2140, 2950, 3900, 4950] , [0, -12 , -26 , -26 , -44] , [60, 90, 100, 120, 120] ] ,sO: [ [450, 800, 2830, 3800, 4950] , [0, -11 , -22 , -22 , -50] , [70, 80, 100, 130, 135] ] ,sU: [ [325, 700, 2700, 3800, 4950] , [0, -16, -35, -40, -60] , [50, 60, 170, 180, 200] ] // alto , aA: [ [800, 1150, 2800, 3500, 4950] , [0, -4 , -20, -36 , -60] , [80, 90, 120, 130, 140] ] ,aE: [ [400, 1600, 2700, 3300, 4950] , [0, -24 , -30, -35, -60] , [60, 80, 120, 150, 200] ] ,aI: [ [350, 1700, 2700, 3700, 4950] , [0, -20, -30, -36 , -60] , [50, 100, 120, 150, 200] ] ,aO: [ [450, 800, 2830, 3500, 4950] , [0, -9 , -16 , -28 , -55] , [70, 80, 100, 130, 135] ] ,aU: [ [325, 700, 2530, 3500, 4950] , [0, -12 , -30, -40, -64] , [50, 60, 170, 180, 200] ] // counter tenor , ctA: [ [660, 1120, 2750, 3000, 3350] , [0, -6 , -23 , -24 , -38] , [80, 90, 120, 130, 140] ] ,ctE: [ [440, 1800, 2700, 3000, 3300] , [0, -14 , -18 , -20, -20] , [70, 80, 100, 120, 120] ] ,ctI: [ [270, 1850, 2900, 3350, 3590] , [0, -24 , -24 , -36 , -36] , [40, 90, 100, 120, 120] ] ,ctO: [ [430, 820, 2700, 3000, 3300] , [0, -10, -26 , -22 , -34] , [40, 80, 100, 120, 120] ] ,ctU: [ [370, 630, 2750, 3000, 3400] , [0, -20, -23 , -30, -34] , [40, 60, 100, 120, 120] ] // tenor ,tA: [ [650, 1080, 2650, 2900, 3250] , [0, -6 , -7 , -8 , -22] , [80, 90, 120, 130, 140] ] ,tE: [ [400, 1700, 2600, 3200, 3580] , [0, -14 , -12 , -14 , -20] , [70, 80, 100, 120, 120] ] ,tI: [ [290, 1870, 2800, 3250, 3540] , [0, -15, -18 , -20, -30] , [40, 90, 100, 120, 120] ] ,tO: [ [400, 800, 2600, 2800, 3000] , [0, -10, -12 , -12 , -26] , [40, 80, 100, 120, 120] ] ,tU: [ [350, 600, 2700, 2900, 3300] , [0, -20, -17 , -14 , -26] , [40, 60, 100, 120, 120] ] // bass ,bA: [ [600, 1040, 2250, 2450, 2750] , [0, -7 , -9 , -9 , -20] , [60, 70, 110, 120, 130] ] ,bE: [ [400, 1620, 2400, 2800, 3100] , [0, -12 , -9 , -12 , -18] , [40, 80, 100, 120, 120] ] ,bI: [ [250, 1750, 2600, 3050, 3340] , [0, -30, -16 , -22 , -28] , [60, 90, 100, 120, 120] ] ,bO: [[400, 750, 2400, 2600, 2900], [0, -11 , -21 , -20, -40], [40, 80, 100, 120, 120]] ,bU: [ [350, 600, 2400, 2675, 2950] , [0, -20, -32 , -28 , -36] , [40, 80, 100, 120, 120] ] ); q.consonants = (b: [ 0.00223, 1.00000], c: [ 0.00317, 0.86667], d: [ 0.00596, 0.00571], f: [ 0.00105, 0.89714], g: [ 0.00340, 0.95330], h: [ 0.00474, 0.99619], j: [ 0.00187, 0.90000], k: [ 0.00310, 0.47238], l: [ 0.00315, 0.95333], m: [ 0.00389, 1.00000], n: [ 0.00384, 0.99905], p: [ 0.00572, 0.99810], q: [ 0.00983, 0.08762], r: [ 0.00106, 0.93619], s: [ 0.00103, 0.88286], t: [ 0.00125, 0.12762], v: [ 0.00359, 0.00095], w: [ 0.00940, 1.00000], x: [ 0.00550, 0.51905], y: [ 0.00299, 0.98286], z: [ 0.00477, 0.99905] ); q.playWord = { |dict,voice=0, word="test"| { q.fd[voice].set(\gain, 1); word.toLower.do{ |letter| q.playLetter(voice, letter); 0.05.wait; }; q.fd[voice].set(\gain, 0); q.playLetter(voice, $a); //"done".postln; }.fork; }; q.playLetter = { |dict,voice,letter| if(letter.isVowel, { q.playVowel(voice, letter.asSymbol) }, { if(letter.isAlpha, { q.playConsonant(voice, letter.asSymbol, 0.25) }, { //"non-alphabetic character... ignored".postln; }); }) }; q.playConsonant = { |dict,voice, letter, lagTime| q.fd[voice].set(\lagTime, lagTime, \delay, q.consonants[letter][0], \blend, q.consonants[letter][1]) }; q.playVowel = { |dict,voice, letter| q.sendVowel(voice, 'a' ++ letter.asString.toUpper.asSymbol); }; q.sendVowel = { |dict,voice, letter| letter = letter.asSymbol; q.fd[voice].set( \ffreqs, q.putnam[letter][0], \fgains, q.putnam[letter][1], \fbws, q.putnam[letter][2]) }; q.initVoices = { |dict,numVoices| if(q.fd.isArray, { q.fd.do{ |synth| synth.set(\gain, 0); synth.free; }}); q.fd = 0!numVoices; numVoices.do{ |i| q.fd[i] = Synth(\ks); }}; SynthDef(\ks, { |out=0,gain=0,delay=1,blend=440, ffreqs=#[350, 600, 2400, 2675, 2950], fgains=#[0, -20, -32 , -28 , -36], fbws=#[40, 80, 100, 120, 120], lag = 0.1| var laggedDelay = Lag.kr(delay, lag); var laggedBlend = Lag.kr(blend, lag); var snd0,snd1,snd2,snd3,snd4,snd5,snd6, snd7; var wn = WhiteNoise.ar; var sin = SinOsc.ar(200, 0); snd0 = Decay.ar(Impulse.ar(200, 0), 0.0025) * sin; snd1 = LocalIn.ar(1) + (snd0 * gain.lag(0.01)); snd2 = DelayN.ar(snd1, 0.01, laggedDelay); snd3 = Delay1.ar(snd2); snd4 = (snd2 + snd3) / 2; snd5 = ((wn > blend) * snd4) + ((1 - (wn > blend)) * (snd4 * -1)); snd6 = Mix( Resonz.ar(snd5, ffreqs, fbws/ffreqs) * fgains.dbamp); snd7 = snd6 * 1.5; LocalOut.ar(snd5 * 0.99); Out.ar(out, snd7.dup); }).add; q.initVoices(3); }) ) q.playWord(0, "Hello how are you today"); q.playWord(1, "asdf"); q.playWord(1, "hello"); q.playWord(1, "cmo zeoiza mi xe wvizu"); ( Task{ ["hello", "how", "are", "you", "today"].do{ |word| q.playWord(0, word); 0.5.wait; } }.play ) ( Pdef(\vocable, Pbind( \amp, 0, \word, Pseq(["soup", "tackle", "qop", "", "hello"],inf), \dur, 0.25, \voice, Pseq([0],inf), \func, Pfunc{|k| q.playWord(k.voice,k.word)})) ) ( Pdef(\vocable, Pbind( \degree, \rest, \word, Pclump(Pwhite(1,2), Pseq(["soup", "tackle", "qop", "asdf", "ho", "hi"],inf)), \dur, 0.25, \strum, 0.125, \func, Pfunc{|k| if(k.word.isString, { q.playWord(0,k.word); }, { k.word[..(q.fd.size-1)].do{ |word,i| q.playWord(i, word) }} )} )) ) Pdef(\vocable).play Pdef(\vocable).stop