// title: Scanned Synthesis Visualisation // author: Matthew Kane // description: // Visualization of scanned string synthesis. // code: ~numsprings = 41; ( SynthDef(\scansynth41, { arg amp=0.1, gate=1, pan=0, freq=440, spring, damp; var in, ou; var springs; var sel; var env; in = 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)); spring = spring.dup(~numsprings); damp = damp.dup(~numsprings); ou = in; springs = spring.collect { arg spr, i; ou = Spring.ar(ou, spr, damp[i]); }; LocalOut.ar(ou); sel = 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); SendReply.ar(Impulse.ar(30), cmdName: '/sel', values: sel); SendReply.ar(Impulse.ar(30), cmdName: '/springs', values: springs); ou = Splay.ar(SelectX.ar(sel, springs), 1); env = EnvGen.ar(Env.adsr(0.3,0.1,0.8,0.1, peakLevel:1),gate:gate,doneAction:2); ou = Pan2.ar(ou, pan, amp); // ou.poll(label:"out"); Out.ar(0, ou[0]); Out.ar(0, ou[1]); }).load; ) ( var window, view, redraw; // OSC bridge from synth to language if ( ~updateSelector != nil, {~updateSelector.free}); if ( ~updateSprings != nil, {~updateSprings.free}); o = OSCFunc({|msg, time, addr, recvPort| ~sel = msg[3..]; }, '/sel'); n = OSCFunc({|msg, time, addr, recvPort| // ~springs = msg[3..]; msg[3..].do { |item, i| ~springs[i] = item; } // ~springs.postln; }, '/springs'); ~springs=Array.fill(~numsprings, 0); ~sel = Array.fill(5, 0); // simulation window drawing if (~max == nil) { ~max = 0.001; "max".postln; }; window = Window.new(bounds:Rect(0,0,800,600),server:s); window.front; CmdPeriod.doOnce({window.close}); view = UserView(window, window.bounds); view.background = Color.white; view.resize = 5; view.drawFunc = { |view| var jumpwidth = view.bounds.width / (~numsprings-1); Pen.fillColor = Color.black; Pen.strokeColor = Color.black; ~springs.do({ |spring, i| var height = (spring/~max*view.bounds.height/2) + (view.bounds.height/2); var circlesize = (view.bounds.height.pow(2) + view.bounds.width.pow(2)).sqrt / 50; if (spring.abs > ~max, { ~max = spring.abs; "maxspring"+~max.postln; }); if (i == 0, {Pen.moveTo(Point(0, height))}, {Pen.lineTo(Point(i*jumpwidth, height))}); Pen.addOval(Rect(i*jumpwidth-(circlesize/2), height-(circlesize/2), circlesize, circlesize)); }); Pen.fillStroke; Pen.color = Color.red; ~sel.do({ |sel, i| Pen.moveTo(Point(sel*jumpwidth, view.bounds.height)); Pen.lineTo(Point(sel*jumpwidth, 0)); }); Pen.fillStroke; }; view.frameRate = 30; view.animate = true; ~charmap = Dictionary.new(17); ~charmap.put($a, 45); // C ~charmap.put($w, 46); // C# ~charmap.put($s, 47); // D ~charmap.put($e, 48); // D# ~charmap.put($d, 49); // E ~charmap.put($f, 50); // F ~charmap.put($t, 51); // F# ~charmap.put($g, 52); // G ~charmap.put($y, 53); // G# ~charmap.put($h, 54); // A ~charmap.put($u, 55); // Bb ~charmap.put($j, 56); // B ~charmap.put($k, 57); // C ~charmap.put($o, 58); // C# ~charmap.put($l, 59); // D ~charmap.put($p, 60); // D# ~charmap.put($;, 61); // E view.keyDownAction = { |view, char| // ("keydown" + char).postln; ~lastchar = char; if (~charmap.at(char) != nil, { ("kd freeing" + ~syn).postln; ~syn.release(0.02); ~max = 0.001; ~syn = Synth(\scansynth41, [\freq, ~charmap.at(char).midicps, \spring, ~spring, \damp, ~damp, \pan, ~pan], s); ("kd creating" + ~syn).postln; }); }; ) ( // control window var window, dampknob, damptext, springknob, springtext, resetbutton, panknob, pantext, newbounds; window = Window.new(server:s); damptext = StaticText.new(); damptext.string = "damping\n"; dampknob = Knob.new(window); dampknob.mode = \vert; dampknob.action = { |knob| var value = 100.pow(knob.value)*0.01; damptext.string_("damping\n"++value); value.postln; ~damp = value; ~syn.set(\damp, value); }; springtext = StaticText.new(); springtext.string = "spring\n"; springknob = Knob.new(window); springknob.mode = \vert; springknob.action = { |knob| var value = 12.pow(knob.value)*0.01; springtext.string_("spring\n"++value); value.postln; ~spring = value; ~syn.set(\spring, value); }; pantext = StaticText.new(); pantext.string = "pan\n"; panknob = Knob.new(window); panknob.mode = \vert; panknob.action = { |knob| var value = knob.value * 2 - 1; pantext.string_("pan\n"++value); value.postln; ~pan = value; ~syn.set(\pan, value); }; springknob.valueAction_(0.5); dampknob.valueAction_(0.5); panknob.valueAction_(0.5); resetbutton = Button(window).states_([['Reset', Color.black]]).action_({ ("resetting max to 0, was" + ~max).postln; ~max = 0;}); window.layout = VLayout(HLayout(springknob, springtext), HLayout(dampknob, damptext), HLayout(panknob, pantext), resetbutton); window.bounds = Rect(800,0,400,400); window.front; )