{
   "name" : "Scanned Synthesis Visualisation",
   "author" : "Matthew Kane",
   "description" : "Visualization of scanned string synthesis.",
   "ancestor_list" : [
      "1-4Rm"
   ],
   "labels" : [
      "scanned synthesis",
      "visualization"
   ],
   "id" : "1-4VP",
   "is_private" : null,
   "code" : "~numsprings = 41;\r\n(\r\nSynthDef(\\scansynth41, { arg amp=0.1, gate=1, pan=0, freq=440, spring, damp;\r\n    var in, ou;\r\n    var springs;\r\n    var sel;\r\n\tvar env;\r\n\tin = LPF.ar(LocalIn.ar(1), XLine.ar(1,10,0.3)) * 0.97 + (LPF.ar(Trig.ar(gate, 0.01),200) * WhiteNoise.ar.range(0.98,1));\r\n\tspring = spring.dup(~numsprings);\r\n\tdamp = damp.dup(~numsprings);\r\n    ou = in;\r\n    springs = spring.collect { arg spr, i;\r\n        ou = Spring.ar(ou, spr, damp[i]);\r\n    };\r\n    LocalOut.ar(ou);\r\n\tsel = VarSaw.ar(freq * [0.999,1,0.499,2,1.01] * 0.5, width: LFNoise2.ar(1/2).range(0.2,0.5)).range(0, spring.size-1);\r\n\tSendReply.ar(Impulse.ar(30), cmdName: '/sel', values: sel);\r\n\tSendReply.ar(Impulse.ar(30), cmdName: '/springs', values: springs);\r\n\tou = Splay.ar(SelectX.ar(sel, springs), 1);\r\n\tenv = EnvGen.ar(Env.adsr(0.3,0.1,0.8,0.1, peakLevel:1),gate:gate,doneAction:2);\r\n\tou = Pan2.ar(ou, pan, amp);\r\n\t// ou.poll(label:\"out\");\r\n\tOut.ar(0, ou[0]);\r\n\tOut.ar(0, ou[1]);\r\n}).load;\r\n)\r\n\r\n(\r\nvar window, view, redraw;\r\n\r\n// OSC bridge from synth to language\r\nif ( ~updateSelector != nil, {~updateSelector.free});\r\nif ( ~updateSprings != nil, {~updateSprings.free});\r\no = OSCFunc({|msg, time, addr, recvPort|\r\n\t~sel = msg[3..];\r\n}, '/sel');\r\nn = OSCFunc({|msg, time, addr, recvPort|\r\n\t// ~springs = msg[3..];\r\n\tmsg[3..].do { |item, i|\r\n\t\t~springs[i] = item;\r\n\t}\r\n\t// ~springs.postln;\r\n}, '/springs');\r\n~springs=Array.fill(~numsprings, 0);\r\n~sel = Array.fill(5, 0);\r\n\r\n// simulation window drawing\r\nif (~max == nil) { ~max = 0.001; \"max\".postln; };\r\nwindow = Window.new(bounds:Rect(0,0,800,600),server:s);\r\nwindow.front;\r\nCmdPeriod.doOnce({window.close});\r\nview = UserView(window, window.bounds);\r\nview.background = Color.white;\r\nview.resize = 5;\r\nview.drawFunc = {\r\n\t|view|\r\n\tvar jumpwidth = view.bounds.width / (~numsprings-1);\r\n\tPen.fillColor = Color.black;\r\n\tPen.strokeColor = Color.black;\r\n\t~springs.do({\r\n\t\t|spring, i|\r\n\t\tvar height = (spring/~max*view.bounds.height/2) + (view.bounds.height/2);\r\n\t\tvar circlesize = (view.bounds.height.pow(2) + view.bounds.width.pow(2)).sqrt / 50;\r\n\r\n\t\tif (spring.abs > ~max, {\r\n\t\t\t~max = spring.abs;\r\n\t\t\t\"maxspring\"+~max.postln;\r\n\t\t});\r\n\r\n\t\tif (i == 0, {Pen.moveTo(Point(0, height))},\r\n\t\t{Pen.lineTo(Point(i*jumpwidth, height))});\r\n\t\tPen.addOval(Rect(i*jumpwidth-(circlesize/2), height-(circlesize/2), circlesize, circlesize));\r\n\t});\r\n\tPen.fillStroke;\r\n\tPen.color = Color.red;\r\n\t~sel.do({\r\n\t\t|sel, i|\r\n\t\tPen.moveTo(Point(sel*jumpwidth, view.bounds.height));\r\n\t\tPen.lineTo(Point(sel*jumpwidth, 0));\r\n\t});\r\n\tPen.fillStroke;\r\n};\r\nview.frameRate = 30;\r\nview.animate = true;\r\n~charmap = Dictionary.new(17);\r\n~charmap.put($a, 45); // C\r\n~charmap.put($w, 46); // C#\r\n~charmap.put($s, 47); // D\r\n~charmap.put($e, 48); // D#\r\n~charmap.put($d, 49); // E\r\n~charmap.put($f, 50); // F\r\n~charmap.put($t, 51); // F#\r\n~charmap.put($g, 52); // G\r\n~charmap.put($y, 53); // G#\r\n~charmap.put($h, 54); // A\r\n~charmap.put($u, 55); // Bb\r\n~charmap.put($j, 56); // B\r\n~charmap.put($k, 57); // C\r\n~charmap.put($o, 58); // C#\r\n~charmap.put($l, 59); // D\r\n~charmap.put($p, 60); // D#\r\n~charmap.put($;, 61); // E\r\n\r\nview.keyDownAction = {\r\n\t|view, char|\r\n\t//\t(\"keydown\" + char).postln;\r\n\r\n\t~lastchar = char;\r\n\tif (~charmap.at(char) != nil, {\r\n\t\t(\"kd freeing\" + ~syn).postln;\r\n\t\t~syn.release(0.02);\r\n\t\t~max = 0.001;\r\n\t\t~syn = Synth(\\scansynth41, [\\freq, ~charmap.at(char).midicps, \\spring, ~spring, \\damp, ~damp, \\pan, ~pan], s);\r\n\t\t(\"kd creating\" + ~syn).postln;\r\n\t});\r\n};\r\n)\r\n\r\n( // control window\r\nvar window, dampknob, damptext, springknob, springtext, resetbutton, panknob, pantext, newbounds;\r\nwindow = Window.new(server:s);\r\ndamptext = StaticText.new();\r\ndamptext.string = \"damping\\n\";\r\ndampknob = Knob.new(window);\r\ndampknob.mode = \\vert;\r\ndampknob.action = {\r\n\t|knob|\r\n\tvar value = 100.pow(knob.value)*0.01;\r\n\tdamptext.string_(\"damping\\n\"++value);\r\n\tvalue.postln;\r\n\t~damp = value;\r\n\t~syn.set(\\damp, value);\r\n};\r\nspringtext = StaticText.new();\r\nspringtext.string = \"spring\\n\";\r\nspringknob = Knob.new(window);\r\nspringknob.mode = \\vert;\r\nspringknob.action = {\r\n\t|knob|\r\n\tvar value = 12.pow(knob.value)*0.01;\r\n\tspringtext.string_(\"spring\\n\"++value);\r\n\tvalue.postln;\r\n\t~spring = value;\r\n\t~syn.set(\\spring, value);\r\n};\r\npantext = StaticText.new();\r\npantext.string = \"pan\\n\";\r\npanknob = Knob.new(window);\r\npanknob.mode = \\vert;\r\npanknob.action = {\r\n\t|knob|\r\n\tvar value = knob.value * 2 - 1;\r\n\tpantext.string_(\"pan\\n\"++value);\r\n\tvalue.postln;\r\n\t~pan = value;\r\n\t~syn.set(\\pan, value);\r\n};\r\nspringknob.valueAction_(0.5);\r\ndampknob.valueAction_(0.5);\r\npanknob.valueAction_(0.5);\r\nresetbutton = Button(window).states_([['Reset', Color.black]]).action_({ (\"resetting max to 0, was\" + ~max).postln; ~max = 0;});\r\nwindow.layout = VLayout(HLayout(springknob, springtext), HLayout(dampknob, damptext), HLayout(panknob, pantext), resetbutton);\r\nwindow.bounds = Rect(800,0,400,400);\r\nwindow.front;\r\n)"
}
