{
   "labels" : [
      "gui",
      "synthesis techniques",
      "waveshaping"
   ],
   "id" : "1-4UJ",
   "is_private" : null,
   "code" : "// ************************************\r\n// Waveshaping Synthesis (GUI)\r\n// Patch 1 - Visualization of the basic concept\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\nThis is a simple interface to visualize relationship between:\r\n\r\n- input waveform (a sine wave in this example)\r\n- transfer function\r\n- output waveform\r\n\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\nUse the amplitude slider to check this out.\r\n\r\nYou can assume that all x and y axes are -1 to +1.\r\n\r\nHow to understand what is going on:\r\nFor any given input value (i.e., a sample value taken from the y-axis of input), the waveshaper calculates an output sample by looking up the corresponding output value (y-axis) in the transfer function. So for example, if a given input sample is at +0.9 (y-axis), the waveshaper looks up +0.9 on the x-axis of the transfer function, finds a corresponding new value between -1/+1, and that becomes the output sample.\r\n\r\nA linear transfer function (first preset) will cause the output to be exactly the same as the input.\r\n\r\nNote: this patch makes no sound. It is just a visual demo.\r\n\r\n*/\r\n\r\n(\r\n\r\nvar size = 256, inputWaveform, transferFunction, updateOutput, currentScale = 1, windowColor, buttonArray, linearFunction, chebyFunction1, chebyFunction2;\r\n\r\n//\r\n// TRANSFER FUNCTIONS\r\n//\r\n\r\n// output same as input\r\nlinearFunction = Array.fill(size+1, { |i| i-1});\r\n// even partials\r\nchebyFunction1 = Signal.chebyFill(size+1, [0, 1, 0, 0, 0, 1]).linlin(-1, 1, 0, size).round;\r\n// odd partials\r\nchebyFunction2 = Signal.chebyFill(size+1, [1, 0, 1, 0, 1, 0]).linlin(-1, 1, 0, size).round;\r\n\r\n//\r\n// GUI WINDOW\r\n//\r\n\r\nWindow.closeAll;\r\n\r\nw = Window.new(\"Waveshaping Synthesis - visual demo\", Rect(50, 50, 610, 640), false);\r\nw.front;\r\nwindowColor = Color.white;\r\nw.background = windowColor;\r\n\r\nx = CompositeView.new(w, Rect(10, 10, 500, 200)).background_(windowColor);\r\ny = CompositeView.new(w, Rect(10, 220, 500, 200)).background_(windowColor);\r\nz = CompositeView.new(w, Rect(10, 430, 500, 200)).background_(windowColor);\r\n\r\na = Plotter.new(\"Input\", parent: x);\r\nb = Plotter.new(\"Transfer\", parent: y);\r\nc = Plotter.new(\"Output\", parent: z);\r\n\r\nStaticText(x, Rect(5, 0, 50, 20))\r\n.string_(\"input\");\r\nStaticText(y, Rect(5, 0, 200, 20))\r\n.string_(\"transfer function\");\r\nStaticText(z, Rect(5, 0, 50, 20))\r\n.string_(\"output\");\r\n\r\n//\r\n// FEEDING THE PLOTS\r\n//\r\n\r\n// Input\r\ninputWaveform = Signal.sineFill(size, [1]).linlin(-1, 1, 1, size).round;\r\n// Note: linlin above returns a simple Array, not a Signal\r\na.value = inputWaveform;\r\na.setProperties(\\backgroundColor, Color.red(1, 0.5));\r\na.setProperties(\\gridOnX, false);\r\na.setProperties(\\gridOnY, false);\r\na.minval = 0; a.maxval = size;\r\na.refresh;\r\n\r\n// Transfer Function\r\ntransferFunction = linearFunction.copy;\r\nb.value = transferFunction;\r\nb.setProperties(\\backgroundColor, Color.yellow(1, 0.4));\r\nb.setProperties(\\gridOnX, false);\r\nb.setProperties(\\gridOnY, false);\r\nb.minval = 0; b.maxval = size;\r\nb.editMode = true;\r\nb.editFunc = {\r\n\tc.value = updateOutput.value;\r\n\tbuttonArray.do{|i| i.value=0};\r\n};\r\nb.refresh;\r\n\r\n// Output\r\nupdateOutput = {\r\n\tArray.fill(size, { |i|\r\n\t\tvar inputSample, outputSample;\r\n\t\tinputSample = a.value.round.at(i);\r\n\t\toutputSample = b.value.at(inputSample);\r\n\t});\r\n};\r\n\r\nc.value = updateOutput.value;\r\nc.setProperties(\\backgroundColor, Color.green(0.9, 0.4));\r\nc.setProperties(\\gridOnX, false);\r\nc.setProperties(\\gridOnY, false);\r\nc.minval = 0; c.maxval = size;\r\nc.refresh;\r\n\r\n// Input Amplitude Slider\r\nd = Slider(w, Rect(520, 10, 80, 200))\r\n.value_(1)\r\n.background_(Color.grey(0.9, 0.9))\r\n.focusColor_(Color.red(1, 0.3))\r\n.knobColor_(Color.red(1, 0.3))\r\n.action_({arg slider;\r\n\tvar scale, newMax, offset, scaledArray;\r\n\tscale = slider.value.round(0.01);\r\n\t// Ignore repeated values with if/else\r\n\tif(currentScale == scale,\r\n\t\t{/*[currentScale, scale, \"do nothing\"].postln*/},\r\n\t\t{\r\n\t\t\tnewMax = size * scale;\r\n\t\t\toffset = (size - newMax) / 2;\r\n\t\t\tscaledArray = inputWaveform.linlin(0, size, 0, newMax) + offset;\r\n\t\t\ta.value = scaledArray;\r\n\t\t\tc.value = updateOutput.value;\r\n\t\t\ta.minval = 0; a.maxval = size; a.refresh;\r\n\t\t\tc.minval = 0; c.maxval = size; c.refresh;\r\n\t\t\tcurrentScale = scale;\r\n\t});\r\n});\r\n\r\n// Preset buttons\r\nbuttonArray = Array.newClear(3);\r\n\r\nbuttonArray[0] = Button(w, Rect(520, 220, 80, 60))\r\n.states_([\r\n\t[\"linear\", Color.black],\r\n\t[\"linear\", Color.black, Color.grey(0.6)]])\r\n.value_(1)\r\n.action_({arg button;\r\n\tif(button.value==1, {\r\n\t\ttransferFunction = linearFunction.copy;\r\n\t\tb.value = transferFunction;\r\n\t\tb.minval = 0; b.maxval = size;\r\n\t\tb.refresh;\r\n\t\tc.value = updateOutput.value;\r\n\t\tc.minval = 0; c.maxval = size; c.refresh;\r\n\t\tbuttonArray[1].value = 0;\r\n\t\tbuttonArray[2].value = 0;\r\n\t});\r\n});\r\n\r\nbuttonArray[1] = Button(w, Rect(520, 290, 80, 60))\r\n.states_([\r\n\t[\"cheby 1\", Color.black],\r\n\t[\"cheby 1\", Color.black, Color.grey(0.6)]])\r\n.action_({arg button;\r\n\tif(button.value==1, {\r\n\t\ttransferFunction = chebyFunction1.copy;\r\n\t\tb.value = transferFunction;\r\n\t\tb.minval = 0; b.maxval = size;\r\n\t\tb.refresh;\r\n\t\tc.value = updateOutput.value;\r\n\t\tc.minval = 0; c.maxval = size; c.refresh;\r\n\t\tbuttonArray[0].value = 0;\r\n\t\tbuttonArray[2].value = 0;\r\n\t});\r\n});\r\n\r\nbuttonArray[2] = Button(w, Rect(520, 360, 80, 60))\r\n.states_([\r\n\t[\"cheby 2\", Color.black],\r\n\t[\"cheby 2\", Color.black, Color.grey(0.6)]])\r\n.action_({arg button;\r\n\tif(button.value==1, {\r\n\t\ttransferFunction = chebyFunction2.copy;\r\n\t\tb.value = transferFunction;\r\n\t\tb.minval = 0; b.maxval = size;\r\n\t\tb.refresh;\r\n\t\tc.value = updateOutput.value;\r\n\t\tc.minval = 0; c.maxval = size; c.refresh;\r\n\t\tbuttonArray[0].value = 0;\r\n\t\tbuttonArray[1].value = 0;\r\n\t});\r\n});\r\n\r\n50.do({\r\n\tStaticText(w, Rect(\r\n\t\trrand(520, 590),\r\n\t\trrand(430, 615),\r\n\t\t10,\r\n\t\t10))\r\n.string_(\".\");\r\n});\r\n\r\nStaticText(w, Rect(520, 430, 10, 10))\r\n.string_(\".\");\r\nStaticText(w, Rect(590, 430, 10, 10))\r\n.string_(\".\");\r\nStaticText(w, Rect(520, 615, 10, 10))\r\n.string_(\".\");\r\nStaticText(w, Rect(590, 615, 10, 10))\r\n.string_(\".\");\r\n\r\n) // end of block",
   "author" : "Bruno Ruviaro",
   "name" : "Waveshaping GUI Demo 1",
   "ancestor_list" : [],
   "description" : "Visualize the basic concept of waveshaping with a nice & straightforward GUI. This patch makes no sound."
}
