Submit
Browse
Anonymous
Login
RSS
SuperCollider Code
Fork Code: Formant Synthesis Singers GUI Demo
name
code content
// ************************************ // Formant Synthesis Demo (GUI) // Patch 1 - Singing vowels // Bruno Ruviaro, 2013-08-29 // ************************************ /* Four voice types available: Soprano, Alto, Tenor, Bass (buttons SATB). Two vibrato controls: speed and depth (horizontal sliders). Fundamental frequency control (vertical slider). Vowel control: choose a vowel for each corner of the 2D space (drop down menus). How to start: select all (ctrl + A), then evaluate (ctrl + enter). (on a Mac, use the command key instead of control) */ s.waitForBoot({ var formantTable, win, subwin, slider2D, vowels, menu00, menu01, menu11, menu10, display00, display01, display11, display10, f00, f01, f11, f10, fxy, synth, startButton, buttonsSATB, selectedSATB, updateAll, freqSlider, fundamentalFrequency, vibratoSpeed, vibratoDepth, vibratoSpeedSlider, vibratoDepthSlider; // This was taken from FormantTable class from SC3plugins. // Hard-coded here so that this patch won't depend on SC3plugins. formantTable = IdentityDictionary.new; formantTable.put(\sopranoA, [[800, 1150, 2900, 3900, 4950], [0, -6, -32, -20, -50].dbamp, [80, 90, 120, 130, 140]]); formantTable.put(\sopranoE, [[350, 2000, 2800, 3600, 4950], [0, -20, -15, -40, -56].dbamp, [60, 100, 120, 150, 200]]); formantTable.put(\sopranoI, [[270, 2140, 2950, 3900, 4950], [0, -12, -26, -26, -44].dbamp, [60, 90, 100, 120, 120]]); formantTable.put(\sopranoO, [[450, 800, 2830, 3800, 4950], [0, -11, -22, -22, -50].dbamp, [70, 80 ,100, 130, 135]]); formantTable.put(\sopranoU, [[325, 700, 2700, 3800, 4950], [0, -16, -35, -40, -60].dbamp, [50, 60, 170, 180, 200]]); formantTable.put(\altoA, [[800, 1150, 2800, 3500, 4950], [0, -4, -20, -36, -60].dbamp, [80, 90, 120, 130, 140]]); formantTable.put(\altoE, [[400, 1600, 2700, 3300, 4950], [0, -24, -30, -35, -60].dbamp, [60, 80, 120, 150, 200]]); formantTable.put(\altoI, [[350, 1700, 2700, 3700, 4950], [0, -20, -30, -36, -60].dbamp, [50, 100, 120, 150, 200]]); formantTable.put(\altoO, [[450, 800, 2830, 3500, 4950], [0, -9, -16, -28, -55].dbamp, [70, 80, 100, 130, 135]]); formantTable.put(\altoU, [[325, 700, 2530, 3500, 4950], [0, -12, -30, -40, -64].dbamp, [50, 60, 170, 180, 200]]); formantTable.put(\counterTenorA, [[660, 1120, 2750, 3000, 3350], [0, -6, -23, -24, -38].dbamp, [80, 90, 120, 130, 140]]); formantTable.put(\counterTenorE, [[440, 1800, 2700, 3000, 3300], [0, -14, -18, -20, -20].dbamp, [70, 80, 100, 120, 120]]); formantTable.put(\counterTenorI, [[270, 1850, 2900, 3350, 3590], [0, -24, -24, -36, -36].dbamp, [40, 90, 100, 120, 120]]); formantTable.put(\counterTenorO, [[430, 820, 2700, 3000, 3300], [0, -10, -26, -22, -34].dbamp, [40, 80, 100, 120, 120]]); formantTable.put(\counterTenorU, [[370, 630, 2750, 3000, 3400], [0, -20, -23, -30, -34].dbamp, [40, 60, 100, 120, 120]]); formantTable.put(\tenorA, [[650, 1080, 2650, 2900, 3250], [0, -6, -7, -8, -22].dbamp, [80, 90, 120, 130, 140]]); formantTable.put(\tenorE, [[400, 1700, 2600, 3200, 3580], [0, -14, -12, -14, -20].dbamp, [70, 80, 100, 120, 120]]); formantTable.put(\tenorI, [[290, 1870, 2800, 3250, 3540], [0, -15, -18, -20, -30].dbamp, [40, 90, 100, 120, 120]]); formantTable.put(\tenorO, [[400, 800, 2600, 2800, 3000], [0, -10, -12, -12, -26].dbamp, [40, 80, 100, 120, 120]]); formantTable.put(\tenorU, [[350, 600, 2700, 2900, 3300], [0, -20, -17, -14, -26].dbamp, [40, 60, 100, 120, 120]]); formantTable.put(\bassA, [[600, 1040, 2250, 2450, 2750], [0, -7, -9, -9, -20].dbamp, [60, 70, 110, 120, 130]]); formantTable.put(\bassE, [[400, 1620, 2400, 2800, 3100], [0, -12, -9, -12, -18].dbamp, [40, 80, 100, 120, 120]]); formantTable.put(\bassI, [[250, 1750, 2600, 3050, 3340], [0, -30, -16, -22, -28].dbamp, [60, 90, 100, 120, 120]]); formantTable.put(\bassO, [[400, 750, 2400, 2600, 2900], [0, -11, -21, -20, -40].dbamp, [40, 80, 100, 120, 120]]); formantTable.put(\bassU, [[350, 600, 2400, 2675, 2950], [0, -20, -32, -28, -36].dbamp, [40, 80, 100, 120, 120]]); // convert bandwidth to 1/q formantTable.keysDo({ arg key; formantTable[key][0].do({ arg center, i; formantTable[key][2][i] = formantTable[key][2][i] / center; }); }); vowels = ["I", "E", "A", "O", "U"]; fundamentalFrequency = 100; vibratoSpeed = 6; vibratoDepth = 7; // Interpolation between corners fxy = {arg x, y; 3.collect({arg e; 5.collect({arg i; ( f00[e][i] * (1-x) * (1-y) ) + ( f10[e][i] * x * (1-y) ) + ( f01[e][i] * (1-x) * y ) + ( f11[e][i] * x * y ); })}); }; // Main Window Window.closeAll; win = Window("Formant Synthesis", Rect(150, 150, 500, 480)).front; win.onClose = {"done".postln}; // win.background = Color.gray(0.87, 0.95); win.background = Color.new255(193, 98, 90, 235); // Slider2D slider2D = Slider2D(win, Rect(50, 40, 400, 400)) .x_(0.5) // initial location of x .y_(0.5) // initial location of y .action_({arg sl; var interpol = fxy.value(sl.x, sl.y); if(synth.isNil.not, { synth.set( \freqs, interpol[0], \amps, interpol[1], \qs, interpol[2]) }, { "not playing".postln }); }); // Vowel display (inside Slider2D) display00 = StaticText(win, Rect(65, 355, 50, 80)) .font_(Font(size: 80)) .stringColor_(Color.gray(0.8)) .align_(\center) .string_("i"); display01 = StaticText(win, Rect(60, 40, 60, 75)) .font_(Font(size: 80)) .stringColor_(Color.gray(0.8)) .align_(\center) .string_("i"); display11 = StaticText(win, Rect(380, 40, 50, 75)) .font_(Font(size: 80)) .stringColor_(Color.gray(0.8)) .align_(\center) .string_("i"); display10 = StaticText(win, Rect(380, 355, 50, 80)) .font_(Font(size: 80)) .stringColor_(Color.gray(0.8)) .align_(\center) .string_("i"); // Menus menu00 = PopUpMenu(win, Rect(10, 440, 40, 30)) .items_(vowels) .action_({ |v| var key = (selectedSATB ++ v.item).asSymbol; f00 = formantTable.at(key); key.postln; f00.postln; display00.string = v.item.toLower; }); menu01 = PopUpMenu(win, Rect(10, 10, 40, 30)) .items_(vowels) .action_({ |v| var key = (selectedSATB ++ v.item).asSymbol; f01 = formantTable.at(key); key.postln; f01.postln; display01.string = v.item.toLower; }); menu11 = PopUpMenu(win, Rect(450, 10, 40, 30)) .items_(vowels) .action_({ |v| var key = (selectedSATB ++ v.item).asSymbol; f11 = formantTable.at(key); key.postln; f11.postln; display11.string = v.item.toLower; }); menu10 = PopUpMenu(win, Rect(450, 440, 40, 30)) .items_(vowels) .action_({ |v| var key = (selectedSATB ++ v.item).asSymbol; f10 = formantTable.at(key); key.postln; f10.postln; display10.string = v.item.toLower; }); // Fundamental Frequency Slider freqSlider = EZSlider( parent: win, bounds: Rect(457, 100, 35, 290), label: "freq", controlSpec: ControlSpec(90, 880, 'exp', 1), action: {arg slider; fundamentalFrequency = slider.value; if(synth.isNil.not, {synth.set(\freq, fundamentalFrequency)}) }, initVal: fundamentalFrequency, layout: 'vert'); // Vibrato Speed Slider vibratoSpeedSlider = EZSlider( parent: win, bounds: Rect(50, 10, 232, 20), label: "vibrato speed", controlSpec: ControlSpec(2, 9, 'lin', 1), action: {arg slider; vibratoSpeed = slider.value; if(synth.isNil.not, {synth.set(\vibratoSpeed, vibratoSpeed)}) }, initVal: vibratoSpeed, labelWidth: 100, numberWidth: 30); // Vibrato Depth Slider vibratoDepthSlider = EZSlider( parent: win, bounds: Rect(270, 10, 170, 20), label: "depth", controlSpec: ControlSpec(1, 12, 'lin', 1), action: {arg slider; vibratoDepth = slider.value; if(synth.isNil.not, {synth.set(\vibratoDepth, vibratoDepth)}) }, numberWidth: 30, initVal: vibratoDepth); // Function to use when a new SATB button // is clicked: update all four corners updateAll = { var f00key, f01key, f11key, f10key; f00key = (selectedSATB ++ menu00.item).asSymbol; f01key = (selectedSATB ++ menu01.item).asSymbol; f11key = (selectedSATB ++ menu11.item).asSymbol; f10key = (selectedSATB ++ menu10.item).asSymbol; f00 = formantTable.at(f00key); f01 = formantTable.at(f01key); f11 = formantTable.at(f11key); f10 = formantTable.at(f10key); [f00key, f01key, f11key, f10key].postln; }; // Singer Buttons (will go inside a subview) subwin = CompositeView(win, Rect(8, 100, 40, 280)); subwin.addFlowLayout(5@5, 5@5); buttonsSATB = Array.fill(4, {arg i; var satb = ["S", "A", "T", "B"]; Button(subwin, 25@65) .states_([[satb[i]], [satb[i], Color.black, Color.gray]]) .font_(Font(size: 20)) .action_({arg button; if(button.value==1, { case {i==0} { selectedSATB = "soprano"; buttonsSATB[[1,2,3]].do{|bt| bt.value=0}; updateAll.value; freqSlider.valueAction_(580); } {i==1} { selectedSATB = "alto"; buttonsSATB[[0,2,3]].do{|bt| bt.value=0}; updateAll.value; freqSlider.valueAction_(380); } {i==2} { selectedSATB = "tenor"; buttonsSATB[[0,1,3]].do{|bt| bt.value=0}; updateAll.value; freqSlider.valueAction_(280); } {i==3} { selectedSATB = "bass"; buttonsSATB[[0,1,2]].do{|bt| bt.value=0}; updateAll.value; freqSlider.valueAction_(100); }; selectedSATB.postln; }, {("button " ++ i ++ " says goodbye").postln}); }); }); // Button initialization buttonsSATB[3].valueAction = 1; // bass // Menus initialization menu00.valueAction = 0; // bassI menu01.valueAction = 1; // bassE menu11.valueAction = 2; // bassA menu10.valueAction = 3; // bassO // Start Button startButton = Button(win, Rect(200, 445, 100, 25)) .states_([["start", Color.white, Color.green(0.6, 0.6)], ["stop", Color.white, Color.red(0.4, 0.3)]]) .action_({arg button; if(button.value==1, {synth = Synth("formantVoice", [ \freq, fundamentalFrequency, \vibratoSpeed, vibratoSpeed, \vibratoDepth, vibratoDepth])}, {synth.release; synth = nil}) }); // SynthDef SynthDef("formantVoice", { arg freqs = #[ 400, 750, 2400, 2600, 2900 ], amps = #[ 1, 0.28, 0.08, 0.1, 0.01 ], qs = #[ 0.1, 0.1, 0.04, 0.04, 0.04 ], lag = 0.5, vibratoSpeed = 6, vibratoDepth = 4, freq = 220, gate = 1; var vibrato, in, env, snd; vibrato = SinOsc.kr(vibratoSpeed, mul: vibratoDepth); in = Saw.ar(Lag.kr(freq + vibrato, 0.2)); env = EnvGen.kr(Env.asr(1), gate, doneAction: 2); snd = Mix.new(BBandPass.ar(in, Lag.kr(freqs, lag), Lag.kr(qs, lag)) * Lag.kr(amps, lag)).dup; Out.ar(0, snd * env); }).add; }); // end of block;
code description
Four voice types available: Soprano, Alto, Tenor, Bass (buttons SATB). Two vibrato controls: speed and depth (horizontal sliders). Fundamental frequency control (vertical slider). Vowel control: choose a vowel for each corner of the 2D space (drop down menus).
use markdown for formating
category tags
comma separated, i.g. "wild, siren" (do not enter default SC class names, please)
ancestor(s)
comma separated identificators, i.g. "1-C,1-1,1-4M,1-x"
Private?
the code will be accessible by direct url and not visible in public activity
signup to submit public code without captcha
comment of change