{
   "id" : "1-4Uw",
   "is_private" : null,
   "code" : "// ************************************\r\n// Additive Synthesis Demo (GUI)\r\n// Patch 3 - Inharmonic Partials\r\n// Bruno Ruviaro, 2013-07-24\r\n// ************************************\r\n\r\n/*\r\n\r\n2 button rows to play up to 16 inharmonic partials of a given fundamental frequency.\r\n\r\nHorizontal Sliders control ADSR envelope, Global Volume, and Fundamental Frequency.\r\n\r\nAll partials have equal amplitude.\r\n\r\nHow to start: select all (ctrl + A), then evaluate (ctrl + enter)\r\n(on a Mac, use command key instead of control key)\r\n\r\n*/\r\n\r\ns.waitForBoot({\r\n\r\n\tvar win, buttonArray, knobArray, notesArray, partialArray, attackSlider, decaySlider, sustainSlider, releaseSlider, volumeSlider, fundamentalSlider, slidersWidth, fundamentalBus, updateButtonDisplay, att = 0.01, dec = 0.3, sus = 0.5, rel = 1.0, masterOut = 0.1;\r\n\r\n\tnotesArray = Array.newClear(16);\r\n\tpartialArray = Array.newClear(16);\r\n\tslidersWidth = 375;\r\n\tfundamentalBus = Bus.control(s, 1);\r\n\tfundamentalBus.set(110);\r\n\r\n\tWindow.closeAll;\r\n\r\n\ts.meter;\r\n\r\n\twin = Window.new(\"Additive Synthesis - Inharmonics\", Rect(500, 100, 800, 550)).front;\r\n\twin.background = Color.grey;\r\n\twin.alpha = 0.95;\r\n\r\n\twin.onClose = {s.freeAll; \"Done!\".postln};\r\n\tCmdPeriod.doOnce({win.close});\r\n\r\n\t// Change the gaps and margins to see how they work\r\n\twin.view.decorator = FlowLayout(win.view.bounds, margin: 10@10, gap: 20@20 );\r\n\r\n\tbuttonArray = Array.fill(16, {Button(win.view, 80@80)});\r\n\tknobArray = Array.fill(16, {Knob(win.view, 80@50)});\r\n\r\n\t// Little function to update numbers displayed on buttons\r\n\tupdateButtonDisplay = {arg button, index;\r\n\t\tvar current = button.value;\r\n\t\tbutton.states = [\r\n\t\t\t[(partialArray[index]).asString, Color.black],\r\n\t\t\t[(partialArray[index]).asString, Color.black, Color.green]];\r\n\t\tbutton.value = current;\r\n\t};\r\n\r\n\tknobArray.do({arg item, count;\r\n\t\tknobArray[count].centered = true;\r\n\t\tknobArray[count].value = rrand(0.4, 0.6).round(0.01); // de-centered positions (center=0.5)\r\n\t\tknobArray[count].action = {arg knob;\r\n\t\t\t// update array of partials\r\n\t\t\tpartialArray[count] = knob.value.linlin(0, 1, -0.2, 0.2).round(0.01) + count + 1;\r\n\t\t\t// update button display\r\n\t\t\tupdateButtonDisplay.value(buttonArray[count], count);\r\n\t\t\t// if this partial is currently playing, update its partial number:\r\n\t\t\tif(notesArray[count].isNil.not,\r\n\t\t\t\t{notesArray[count].set(\\partial, partialArray[count])})}});\r\n\r\n\r\n\t// Initialize array of partials with first knob values\r\n\tpartialArray.do({arg item, count;\r\n\t\tpartialArray[count] = knobArray[count].value.linlin(0, 1, -0.2, 0.2).round(0.01) + count + 1});\r\n\t// in the line above, \"+ 0.5\" adjusts for knob center = 0.5 (becomes center = 1);\r\n\t// this deviation value then is multiplied by the partial number\r\n\r\n\t// Initialize Buttons with proper number display\r\n\tbuttonArray.do({arg item, count; updateButtonDisplay.value(item, count)});\r\n\r\n\tbuttonArray.do({arg item, count;\r\n\t\titem.action = {arg state;\r\n\t\t\tcase\r\n\t\t\t{state.value==1} {notesArray[count] = Synth(\"addsynth\", [\r\n\t\t\t\t\\fundamental, fundamentalBus.asMap,\r\n\t\t\t\t\\partial, partialArray[count],\r\n\t\t\t\t\\amp, 0.1,\r\n\t\t\t\t\\att,  att,\r\n\t\t\t\t\\dec,  dec,\r\n\t\t\t\t\\sus,  sus,\r\n\t\t\t\t\\rel,  rel])}\r\n\t\t\t{state.value==0} {notesArray[count].release; notesArray[count] = nil}\r\n\t}});\r\n\r\n\tattackSlider = EZSlider(\r\n\t\tparent: win,\r\n\t\tbounds: slidersWidth @ 30,\r\n\t\tlabel: \"Attack\",\r\n\t\tcontrolSpec: ControlSpec(0.01, 4.0, \\lin, 0.01, 0.1, \"sec\"),\r\n\t\taction: {|ez|  att = ez.value},\r\n\t\tinitVal: 0.01,\r\n\t\tunitWidth: 30)\r\n\t.setColors(\r\n\t\tstringColor: Color.black,\r\n\t\tsliderBackground: Color.grey(0.7),\r\n\t\tnumNormalColor: Color.black);\r\n\r\n\tdecaySlider = EZSlider(\r\n\t\tparent: win,\r\n\t\tbounds: slidersWidth @ 30,\r\n\t\tlabel: \"Decay\",\r\n\t\tcontrolSpec: ControlSpec(0.01, 1, \\lin, 0.01, 0.1, \"sec\"),\r\n\t\taction: {|ez|  dec = ez.value},\r\n\t\tinitVal: 0.3,\r\n\t\tunitWidth: 30)\r\n\t.setColors(\r\n\t\tstringColor: Color.black,\r\n\t\tsliderBackground: Color.grey(0.7),\r\n\t\tnumNormalColor: Color.black);\r\n\r\n\tsustainSlider = EZSlider(\r\n\t\tparent: win,\r\n\t\tbounds: slidersWidth @ 30,\r\n\t\tlabel: \"Sustain\",\r\n\t\tcontrolSpec: ControlSpec(1, 100, \\lin, 1, 75, \"%\"),\r\n\t\taction: {|ez|  sus = ez.value/100.0},\r\n\t\tinitVal: 75,\r\n\t\tunitWidth: 30)\r\n\t.setColors(\r\n\t\tstringColor: Color.black,\r\n\t\tsliderBackground: Color.grey(0.7),\r\n\t\tnumNormalColor: Color.black);\r\n\r\n\treleaseSlider = EZSlider(\r\n\t\tparent: win,\r\n\t\tbounds: slidersWidth @ 30,\r\n\t\tlabel: \"Release\",\r\n\t\tcontrolSpec: ControlSpec(0.3, 6, \\lin, 0.1, 0.5, \"sec\"),\r\n\t\taction: {|ez|  rel = ez.value},\r\n\t\tinitVal: 0.5,\r\n\t\tunitWidth: 30)\r\n\t.setColors(\r\n\t\tstringColor: Color.black,\r\n\t\tsliderBackground: Color.grey(0.7),\r\n\t\tnumNormalColor: Color.black);\r\n\r\n\tvolumeSlider = EZSlider(\r\n\t\tparent: win,\r\n\t\tbounds: slidersWidth*2.05 @ 30,\r\n\t\tlabel: \"VOL\",\r\n\t\tcontrolSpec: ControlSpec(1, 100, \\lin, 1, 10, \"%\"),\r\n\t\taction: {|ez|  masterOut.set(\\amp, ez.value/100)},\r\n\t\tinitVal: 10,\r\n\t\tunitWidth: 30)\r\n\t.setColors(\r\n\t\tstringColor: Color.white,\r\n\t\tsliderBackground: Color.grey(0.9),\r\n\t\tnumNormalColor: Color.grey);\r\n\r\n\tfundamentalSlider = EZSlider(\r\n\t\tparent: win,\r\n\t\tbounds: slidersWidth*2.05 @ 30,\r\n\t\tlabel: \"FREQ\",\r\n\t\tcontrolSpec: ControlSpec(50, 200, \\lin, 1, 110, \"Hz\"),\r\n\t\taction: {|ez| fundamentalBus.set(ez.value)},\r\n\t\tinitVal: 110,\r\n\t\tunitWidth: 30)\r\n\t.setColors(\r\n\t\tstringColor: Color.white,\r\n\t\tsliderBackground: Color.grey(0.9),\r\n\t\tnumNormalColor: Color.grey);\r\n\r\n\t// SynthDefs\r\n\r\n\t{\r\n\t\tSynthDef(\"addsynth\", {\r\n\t\t\targ fundamental = 110, partial = 2.1, amp = 0.1, gate = 1, att = 0.01, dec = 0.3, sus = 0.5, rel = 1;\r\n\t\t\tvar snd, env, freq;\r\n\t\t\tfreq = fundamental * partial;\r\n\t\t\tenv = EnvGen.ar(Env.adsr(att, dec, sus, rel), gate, doneAction: 2);\r\n\t\t\tsnd = SinOsc.ar(Lag.kr(freq, 1), 0, amp) * env;\r\n\t\t\tOut.ar(0, snd!2);\r\n\t\t}).add;\r\n\r\n\t\tSynthDef(\\amp, {arg inbus=0, amp = 0.1;\r\n\t\t\tReplaceOut.ar(inbus, In.ar(inbus, 2) * amp);\r\n\t\t}).add;\r\n\r\n\t\t// Wait for SynthDefs to be added...\r\n\t\ts.sync;\r\n\r\n\t\t// Now call the Master Out Synth:\r\n\t\tmasterOut = Synth(\"amp\", addAction: \\addToTail);\r\n\r\n\t}.fork;\r\n\r\n\t\"Additive Synthesis Demo 1\".postln;\r\n\t\"\".postln;\r\n\r\n});",
   "labels" : [
      "gui",
      "additive synthesis",
      "synthesis techniques",
      "inharmonic partials"
   ],
   "ancestor_list" : [],
   "description" : "2 button rows to play up to 16 inharmonic partials of a given fundamental frequency. Horizontal Sliders control ADSR envelope, Global Volume, and Fundamental Frequency. All partials have equal amplitude.",
   "author" : "Bruno Ruviaro",
   "name" : "Additive Synthesis GUI Demo 3"
}
