{
   "ancestor_list" : [],
   "description" : "Graphical interface for experimenting with waveshape synthesis.",
   "name" : "Waveshaping Synthesis GUI Demo 2",
   "author" : "Bruno Ruviaro",
   "code" : "// ************************************\r\n// Waveshaping Synthesis (GUI)\r\n// Patch 2 - Waveshaping Demo\r\n// Bruno Ruviaro, 2013-08-14\r\n// ************************************\r\n\r\n/*\r\n\r\nHow to start:\r\nSelect all (ctrl + A), then evaluate (ctrl + period).\r\n\r\nSINE WAVE INPUT:\r\nAmplitude of input is a key parameter of waveshaping synthesis.\r\nHigher amplitudes, more distortion of original shape.\r\nLower amplitudes, less distortion of original shape.\r\n\r\nTRANSFER FUNCTION:\r\nChoose the desired amount (amplitude) for each partial, between 0-1.\r\n\r\nOUTPUT (examples):\r\nChoose between continuous tone, two examples of short notes, and one pattern example (Pbind).\r\n*/\r\n\r\ns.waitForBoot({\r\n\r\n\t/////////////////\r\n\t// Variables\r\n\t/////////////////\r\n\r\n\tvar updateSineView, pBindExample, player, continuous, continuousButton, applyChangesButton, distortionBus, updateCheby, harmonics;\r\n\r\n\tdistortionBus = Bus.control(s, 1); // input amp goes into this Bus\r\n\tharmonics = Array.newClear(10); // store values from Number Boxes\r\n\r\n\t/////////////////\r\n\t// Main Window\r\n\t/////////////////\r\n\r\n\tWindow.closeAll;\r\n\r\n\tf = FlowView.new(\r\n\t\tbounds: 580@600,\r\n\t\tmargin: 10@10, gap: 10@10)\r\n\t.background_(Color.white);\r\n\r\n\tf.onClose = {s.freeAll; player.stop};\r\n\tCmdPeriod.doOnce({Window.closeAll});\r\n\r\n\t////////////////////\r\n\t// Input Row (GUI)\r\n\t////////////////////\r\n\r\n\t// Sub-FlowView\r\n\tg = FlowView.new(f, 310@160, margin: 10@10)\r\n\t.background_(Color.red(0.8, 0.6));\r\n\r\n\t// Label: Input (sine wave)\r\n\tStaticText.new(g, 150@30)\r\n\t.string_(\"INPUT (sine wave)\")\r\n\t.align_(\\top)\r\n\t.font_(Font(\"Verdana\", 14, true));\r\n\r\n\tg.startRow;\r\n\r\n\t// EZNumber for amplitude\r\n\to = EZNumber.new(\r\n\t\tparent: g,\r\n\t\tbounds: Rect(0, 0, 188, 80),\r\n\t\tlabel: \" \",\r\n\t\tcontrolSpec: ControlSpec(0.0, 1.0, 'lin', 0.01),\r\n\t\taction: {arg box;\r\n\t\t\tupdateSineView.value(box.value);\r\n\t\t\tdistortionBus.value = box.value;\r\n\t\t},\r\n\t\tnumberWidth: 90,\r\n\t\tinitVal: 0.5)\r\n\t// unitWidth: 0)\r\n\t.font_(Font(\"Verdana\", 30))\r\n\t.numberView.align_(\\center);\r\n\r\n\t// Init distortionBus (corresponds to input amplitude box)\r\n\tdistortionBus.value = 0.5;\r\n\r\n\tg.startRow;\r\n\r\n\t// Simple label for number box\r\n\tStaticText.new(g, g.bounds.width-20@20)\r\n\t.string_(\"amplitude\")\r\n\t.align_(\\center)\r\n\t.font_(Font(\"Verdana\", 14));\r\n\r\n\t// Plotter for Sine Wave (Input GUI)\r\n\tx = CompositeView.new(f, Rect(0, 0, 240, 160));\r\n\ta = Plotter.new(\"Input\", parent: x)\r\n\t.value_(Signal.sineFill(1000, [1]) * 0.5)\r\n\t.setProperties(\r\n\t\t\\backgroundColor, Color.red(0.8, 0.6));\r\n\ta.minval = -1; a.maxval = 1;\r\n\ta.refresh;\r\n\r\n\tupdateSineView = {arg amp;\r\n\t\ta.value = Signal.sineFill(1000, [1]) * amp;\r\n\t\ta.minval = -1;\r\n\t\ta.maxval = 1;\r\n\t\ta.refresh;\r\n\t};\r\n\r\n\t////////////////////////////////\r\n\t// Transfer Function Row (GUI)\r\n\t////////////////////////////////\r\n\r\n\t// Sub-FlowView\r\n\th = FlowView.new(f, 560@150, margin: 10@10)\r\n\t.background_(Color.yellow(0.9, 0.5));\r\n\r\n\t// Label Chebyshev\r\n\tStaticText.new(h, 280@30)\r\n\t.string_(\"TRANSFER FUNCTION (Chebyshev)\")\r\n\t.align_(\\top)\r\n\t// .background_(Color.red)\r\n\t.font_(Font(\"Verdana\", 14, true));\r\n\r\n\th.startRow;\r\n\r\n\t// Array of number boxes\r\n\tc = Array.fill(10, {arg i;\r\n\t\tEZNumber.new(\r\n\t\t\tparent: h,\r\n\t\t\tbounds: 50@60,\r\n\t\t\tlabel: (i+1).asString ++ \"f\",\r\n\t\t\tcontrolSpec: ControlSpec(0, 1, 'lin', 0.1),\r\n\t\t\taction: {arg thisBox;\r\n\t\t\t\tharmonics = c.collect({arg item; item.value});\r\n\t\t\t\tapplyChangesButton.states = [[\"apply changes\", Color.red]];\r\n\t\t\t\tthisBox.setColors(numNormalColor: Color.red);\r\n\t\t\t},\r\n\t\t\tinitVal: 1.0.rand.round(0.1),\r\n\t\t\tlayout: 'line2')\r\n\t\t.font_(Font(\"Verdana\", 18))\r\n\t\t.setColors()\r\n\t\t.numberView.align_(\\center);\r\n\t});\r\n\r\n\t// At start up\r\n\tharmonics = c.collect({arg item; item.value});\r\n\r\n\t// Apply changes to Chebyshev function\r\n\tapplyChangesButton = Button.new(\r\n\t\tparent: CompositeView(h, 580@40)/*.background_(Color.red)*/,\r\n\t\tbounds: Rect(0, 5, 535, 27))\r\n\t.states_([[\"apply changes\", Color.grey(0.8)]])\r\n\t.action_({arg thisButton;\r\n\t\tapplyChangesButton.states = [[\"apply changes\", Color.gray(0.8)]];\r\n\t\tc.do({arg item; item.normalColor_(Color.black)});\r\n\t\tupdateCheby.value(harmonics);\r\n\t\t// if continuous note is running, stop it and play a new one\r\n\t\tif(continuousButton.value==1,\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcontinuous.release;\r\n\t\t\t\t\t\tcontinuous = Synth(\"shaper\", [\\transFunc, b, \\att, 1, \\dist, distortionBus.asMap]);},\r\n\t\t\t\t\t{/*do nothing*/});\r\n\r\n\r\n\t\t\t});\r\n\r\n\t////////////////////////////////\r\n\t// Playback Examples Row (GUI)\r\n\t////////////////////////////////\r\n\r\n\ti = FlowView.new(f, 300@250, margin: 10@10, gap: 15@15)\r\n\t.background_(Color.green(0.4, 0.5));\r\n\r\n\t// CompositeView for Stethoscope\r\n\ty = CompositeView.new(f, Rect(0, 0, 250, 250));\r\n\t// .background_(Color.yellow(0.5));\r\n\r\n\t// Label: Input (sine wave)\r\n\tStaticText.new(i, 160@30)\r\n\t.string_(\"OUTPUT (examples)\")\r\n\t.align_(\\top)\r\n\t.font_(Font(\"Verdana\", 14, true));\r\n\r\n\t// Scope\r\n\tStethoscope.new(s, view: y);\r\n\r\n\t// Scope Label (and hiding space for scope options)\r\n\tk = CompositeView.new(y, Rect(2, 0, 250, 25))\r\n\t.background_(Color.white);\r\n\r\n\t// Label: Input (sine wave)\r\n\tStaticText.new(k , 230@30)\r\n\t.string_(\"output waveform\")\r\n\t.align_(\\top)\r\n\t.font_(Font(\"Verdana\", 14, true));\r\n\r\n\ti.startRow;\r\n\r\n\t// Buttons\r\n\tcontinuousButton = Button.new(i, 280@50)\r\n\t.states_([[\"Continuous\"], [\"Continuous\", Color.white, Color.black]])\r\n\t.action_({arg button;\r\n\t\tif(button.value==1,\r\n\t\t\t{continuous = Synth(\"shaper\", [\\transFunc, b, \\att, 1, \\dist, distortionBus.asMap])},\r\n\t\t\t{continuous.release});\r\n\t});\r\n\r\n\tButton.new(i, 132@50)\r\n\t.states_([[\"Short Note 1\"]])\r\n\t.action_({\r\n\t\t// Choose duration\r\n\t\tvar shortNoteDur = rrand(0.8, 4);\r\n\t\t// Play note\r\n\t\t{var freq = rrand(100, 300);\r\n\t\t\tLeakDC.ar(Shaper.ar(\r\n\t\t\t\tbufnum: b,\r\n\t\t\t\tin: Splay.ar(\r\n\t\t\t\t\tSinOsc.ar(\r\n\t\t\t\t\t\tfreq: [freq, freq*1.25, freq*1.4],\r\n\t\t\t\t\t\tmul: EnvGen.kr(Env.perc(0.01, shortNoteDur), doneAction: 2))\r\n\t\t)))}.play;\r\n\t\t// Display amplitude change in EZNumber box\r\n\t\t{\r\n\t\t\t100.do({arg i;\r\n\t\t\t\t{o.valueAction = i.linlin(0, 99, 1, 0).round(0.01)}.defer;\r\n\t\t\t\t(shortNoteDur/100).wait;\r\n\t\t\t})\r\n\t\t}.fork;\r\n\t\t// button.value.postln;\r\n\t});\r\n\r\n\t\tButton.new(i, 132@50)\r\n\t.states_([[\"Short Note 2\"]])\r\n\t.action_({\r\n\t\t// Choose duration\r\n\t\tvar shortNoteDur = rrand(0.5, 2);\r\n\t\t// Play note\r\n\t\t{var freq = rrand(60, 400);\r\n\t\t\tLeakDC.ar(Shaper.ar(\r\n\t\t\t\tbufnum: b,\r\n\t\t\t\tin: Splay.ar(\r\n\t\t\t\t\tSinOsc.ar(\r\n\t\t\t\t\t\tfreq: [freq, freq*2, freq*4],\r\n\t\t\t\t\t\tmul: EnvGen.kr(Env.perc(0.01, shortNoteDur), doneAction: 2))\r\n\t\t)))}.play;\r\n\t\t// Display amplitude change in EZNumber box\r\n\t\t{\r\n\t\t\t100.do({arg i;\r\n\t\t\t\t{o.valueAction = i.linlin(0, 99, 1, 0).round(0.01)}.defer;\r\n\t\t\t\t(shortNoteDur/100).wait;\r\n\t\t\t})\r\n\t\t}.fork;\r\n\t\t// button.value.postln;\r\n\t});\r\n\r\n\tButton.new(i, 280@50)\r\n\t.states_([[\"Pbind\"], [\"Pbind\", Color.white, Color.black]])\r\n\t.action_({arg button;\r\n\t\tif(button.value==1, {player = pBindExample.play}, {player.stop; player = nil});\r\n\r\n\t});\r\n\r\n\t////////////////////////////////\r\n\t// AUDIO ///////////////////////\r\n\t////////////////////////////////\r\n\r\n\r\n\tSynthDef(\"shaper\", {\r\n\t\targ freq = 440, gate = 1, amp = 0.6, dist = 0.4, transFunc, att = 0.1, sus = 1, rel = 1;\r\n\t\tvar snd, env;\r\n\t\tenv = EnvGen.kr(Env.asr(att, sus, rel), gate, doneAction: 2);\r\n\t\tsnd = Shaper.ar(transFunc, SinOsc.ar(freq, 0, Lag.kr(dist)));\r\n\t\tsnd = LeakDC.ar(snd * env * amp);\r\n\t\tOut.ar(0, snd!2);\r\n\t}).add;\r\n\r\n\t// Define function to update Cheby buffer\r\n\tupdateCheby = {arg array;\r\n\t\t// Buffer.freeAll;\r\n\t\tb = Buffer.alloc(s, 1024, 1, { |buf| buf.chebyMsg(array) });\r\n\t\t\"end\".postln;\r\n\t};\r\n\r\n\t// Create first with start up values\r\n\tupdateCheby.value(harmonics);\r\n\r\n\tpBindExample = Pbind(\r\n\t\t\\instrument, \"shaper\",\r\n\t\t\\scale, Scale.phrygian,\r\n\t\t\\degree, Pseq([\r\n\t\t\tPtuple([\r\n\t\t\t\tPseq([5, 6, 5, 6, 8, 7, 6, 7], 4),\r\n\t\t\t\tPseq([3, 4, 3, 5, 5, 5, 5, 4], 4),\r\n\t\t\t\tPseq([0, 0, 0, 2, 3, 4, 3, 1], 4)\r\n\t\t\t], 1),\r\n\t\t\tPtuple([\r\n\t\t\t\tPseq([7, 8, 9, 10, 6, 7, 8, 9, 8, 8, 7, 6, 5, 4]),\r\n\t\t\t\tPseq([3, 5, 4, 4, 3, 4, 5, 4, 3, 2, 3, 4, 5, 7]),\r\n\t\t\t\tPseq([-7, -8, -8, -5, -4, -6, -8, -8, -10, -9, -11, -10, -9, -9])\r\n\t\t\t])\r\n\t\t], inf),\r\n\t\t\\dur, Pseq([\r\n\t\t\tPseq([0.15, 0.15, 0.25, 0.25, 0.15, 0.15, 0.25, 0.2], 4),\r\n\t\t\tPrand([0.3, 0.43, 0.383], 14)\r\n\t\t], inf),\r\n\t\t\\legato, Pseq([Pseq([0.1], 32), Pseq([0.5], 14)], inf),\r\n\t\t\\amp, 0.5,\r\n\t\t\\dist, Pseq([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], inf),\r\n\t\t\\transFunc, Pfunc({b});\r\n\t).collect({arg event; {o.valueAction_(event[\\dist])}.defer; event});\r\n\r\n\r\n}); // end of block",
   "is_private" : null,
   "id" : "1-4UK",
   "labels" : [
      "gui",
      "synthesis techniques",
      "waveshaping"
   ]
}
