SCDrawServer realtime animation
SCDrawServer is intended for realtime control of procedural animation (as opposed to SCDraw,
which intended more for non-realtime uses). Drawing functions are triggered by incoming OSC
messages.
There are two styles of messages to send to SCDrawServer. One is very like what is sent
to a synth, for example:
addr.sendMsg(\draw, \spot, 3.0, \x, 300.0, \y, 100.0, \r, 10.0);
\draw identifies it as a SCDrawServer command, \spot is a key in a dictionary that specifies
the drawing function. The following float is the duration of the event. The succeeding symbol
and value pairs are placed into a Dictionary that is passed to the drawing function. They can
be whatever appropriate to te function in question.
The other style of message is what comes a SendReply ugen. So, something like the following:
\drawTrig, nodeID, replyID, and some number of floats as args to the drawing function
The replyID is what is used as the key in the Dictionary. The nodeID is ignored. The floats
are put into an array that is passed to the drawing function. The first float after the replyID
represents either the duration of the event (when using *run) or the percentage value that is
passed to the drawing function. This latter is the case when using the *onDemand method. In this
case It is probably best to set this to 0.0 unless you have a reason not to.
Both kinds of messages can be used at the same time. Study the examples to see the different
styles in action.
Class Methods
*run(dict, addr, rate, width, height, color, fullscreen, hideCursor, refresh) -
dict is a Dictionary of drawing functions. addr is a NetAddr that determines where
to listen. The default is nil. width and height default to 500. color defaults to
Color.black. If fullscreen is true, hideCursor will determine wether the cursor
is visible. if refresh is true, the view is cleared every frame before drawing.
Otherwise, the drawing continually draws on top of what's already there. An incoming
\clear (or a 0) message will clear the view.
*onDemand(dict, addr, rate, width, height, color, fullscreen, hideCursor, refresh) -
This method draws only one frame when a message comes in. This is intended for
things like graphic scores where you do not need to continually redraw the image.
The arguments are the same as *run.
Examples
//boot the local server!
(
var win, table;
var server, circle, spot, orch=Dictionary.new();
var addr = NetAddr.localAddr;
server = Server.local;
SynthDef("pluck", { arg dur, amp, freq, pan;
var sig, env;
env = EnvGen.kr(Env([ 1, 0 ], [ dur ], 'linear'), 1, amp, 0, 1, 2);
sig = Pluck.ar(WhiteNoise.ar(1.0), Impulse.kr(0), freq.reciprocal, freq.reciprocal, dur, 0.25);
sig = Pan2.ar(sig, pan);
Out.ar(0, sig*env);
}).load(server);
spot = { arg perc, dict;
var x, y, r;
x = dict.at(\x);
y = dict.at(\y);
r = dict.at(\r)*(1.0-perc);
Pen.fillColor = Color.blue;
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r, 0.0, 2pi);
Pen.fill;
};
circle = { arg perc, dict;
var x, y, r;
x = dict.at(\x);
y = dict.at(\y);
r = dict.at(\r)*(1.0-perc);
Pen.fillColor = Color.blue;
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r, 0.0, 2pi);
Pen.stroke;
};
orch.put('spot', spot);
orch.put('circle', circle);
SCDrawServer.run(orch, refresh: true); //compare with false
//SCDrawServer.onDemand(orch, refresh: false); //compare onDemand
win = SCWindow.new("controller",Rect(40,40,540,540));
table = SCTabletView(win, Rect(20,20,500,500));
table.background = Color.white;
win.front;
table.action = { arg view, x, y;
var ndur, z;
ndur = 3.0;
z = 0.5;
server.sendMsg(\s_new, \pluck, -1, 0, 1, \dur, ndur, \amp, z, \freq, (((500.0-y)/500.0)*24+48).midicps, \pan, (x-250.0)/250.0);
addr.sendMsg("draw", \spot, ndur, \x, x, \y, y, \r, z*10.0);
};
table.mouseDownAction = { arg view, x, y;
var ndur, z;
ndur = 1.0;
z = 1.0;
server.sendMsg(\s_new, \pluck, -1, 0, 1, \dur, ndur, \amp, z, \freq, (((500.0-y)/500.0)*24+36).midicps, \pan, (x-250.0)/250.0);
addr.sendMsg("draw", \spot, ndur, \x, x, \y, y, \r, z*25.0);
};
table.mouseUpAction = { arg view, x, y;
var ndur, z;
ndur = 3.0;
z = 1.0;
server.sendMsg(\s_new, \pluck, -1, 0, 1, \dur, ndur, \amp, z, \freq, (((500.0-y)/500.0)*24+60).midicps, \pan, (x-250.0)/250.0);
addr.sendMsg("draw", \circle, ndur, \x, x, \y, y, \r, z*25.0);
};
//send a \clear message to clear the view
table.keyDownAction = { arg view, char, mod, uni, key;
if(key == 8, { addr.sendMsg("draw", \clear); });
};
)
//use of SendReply, tracking mouse x and y
(
var circle, spot, orch=Dictionary.new();
var rect, width, height;
var server = Server.local;
var addr = NetAddr.localAddr;
var ndur, x, y, z;
rect = Window.screenBounds();
width = rect.width;
height = rect.height;
{
SendReply.kr(Impulse.kr(10.0), 'drawTrig', [ 1.0, MouseX.kr(0.0, 1.0), MouseY.kr(0.0, 1.0), 50.0], 1905); //run: first in array is event duration
SendReply.kr(Impulse.kr(11.0), 'drawTrig', [ 1.0, MouseX.kr(0.0, 1.0), MouseY.kr(0.0, 1.0), 100.0], 1906); //run: first in array is event duration
//SendReply.kr(Impulse.kr(10.0), 'drawTrig', [ 0.0, MouseX.kr(0.0, 1.0), MouseY.kr(0.0, 1.0), 50.0], 1905); //onDemand: use 0.0 first in array
//SendReply.kr(Impulse.kr(11.0), 'drawTrig', [ 0.0, MouseX.kr(0.0, 1.0), MouseY.kr(0.0, 1.0), 100.0], 1906); //onDemand: use 0.0 first in array
SendReply.kr(Impulse.kr(0.1), 'drawTrig', [ 0.0 ], 0); //replyID of 0 work as a \clear message, only really useful for refresh == false
}.play(server);
spot = { arg perc, dict;
var x, y, r;
x = dict.at(0) * width;
y = dict.at(1) * height;
r = dict.at(2)*(1.0-perc);
Pen.fillColor = Color.blue;
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r, 0.0, 2pi);
Pen.fill;
};
circle = { arg perc, dict;
var x, y, r;
x = dict.at(0) * width;
y = dict.at(1) * height;
r = dict.at(2)*(1.0-perc);
Pen.fillColor = Color.blue;
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r, 0.0, 2pi);
Pen.stroke;
};
orch.put(1905, spot); //use replyID as key for function
orch.put(1906, circle); //use replyID as key for function
//use escape key to exit fullscreen
SCDrawServer.run(orch, nil, rate: 10.0, fullscreen: true, hideCursor: false, refresh: false);
//SCDrawServer.onDemand(orch, nil, fullscreen: true, hideCursor: false, refresh: false);
)
//using Amplitude
//use headphones to avoid feedback
(
var circle, spot, orch=Dictionary.new();
var rect, width, height;
var server = Server.local;
var addr = NetAddr.localAddr;
var ndur, x, y, z;
var lofreq=200.0, hifreq=1000.0;
rect = Window.screenBounds();
width = rect.width;
height = rect.height;
{
var sig, amp;
//sig = Mix.new(SoundIn.ar([0,1]));
sig = SinOsc.ar(440.0, 0, LFNoise1.kr(2.0, 0.5, 0.5));
amp = Amplitude.kr(sig);
SendReply.kr(amp - 0.5, 'drawTrig', [ 1.0, SinOsc.ar(0.2, 0, 0.4, 0.5), SinOsc.ar(0.51, 0, 0.4, 0.5), 100.0 ], 1906);
Out.ar(0, sig*amp);
}.play(server);
circle = { arg perc, dict;
var x, y, r, pos;
pos = (1.0-perc).max(0.0).min(1.0);
//pos = perc;
x = dict.at(0) * width;
y = dict.at(1) * height;
r = dict.at(2);
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r*pos, 0.0, 2pi);
Pen.stroke;
Pen.strokeColor = Color.green;
Pen.addArc(x@y, r*pos*pos, 0.0, 2pi);
Pen.stroke;
Pen.strokeColor = Color.yellow;
Pen.addArc(x@y, r*pos*pos*pos, 0.0, 2pi);
Pen.stroke;
};
orch.put(1906, circle); //use replyID as key for function
SCDrawServer.run(orch, nil, rate: 10.0, fullscreen: true, hideCursor: true, refresh: true);
//SCDrawServer.onDemand(orch, nil, fullscreen: true, hideCursor: false, refresh: false);
)
//using Amplitude and Pitch -- Sing!!
//use headphones to avoid feedback
(
var circle, orch=Dictionary.new();
var rect, width, height;
var server = Server.local;
var addr = NetAddr.localAddr;
var ndur, x, y, z;
var lofreq=200.0, hifreq=1000.0;//adjust these to suit your voice or instrument
rect = Window.screenBounds();
width = rect.width;
height = rect.height;
{
var sig, amp, freq, has;
//sig = Mix.new(SoundIn.ar([0,1]));
sig = SinOsc.ar(LFNoise1.kr(0.21, 400.0, 600.0), 0, LFNoise1.kr(1.17, 0.5, 0.5));
amp = Amplitude.kr(sig);
#freq, has = Pitch.kr(sig);
SendReply.kr(amp - 0.25, 'drawTrig', [ 1.0, (amp-0.25) * 0.75.reciprocal, 1.0 - ((freq-lofreq)/(hifreq-lofreq)), 100.0 ], 1906);
Out.ar(0, SinOsc.ar(freq, 0, amp));
}.play(server);
circle = { arg perc, dict;
var x, y, r, pos;
//pos = (1.0-perc).max(0.0).min(1.0);
pos = (1.0-perc);
if((perc < 0.0) || (perc > 1.0), { perc.postln; });
x = dict.at(0) * width;
y = dict.at(1) * height;
r = dict.at(2);
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r*pos, 0.0, 2pi);
Pen.stroke;
Pen.strokeColor = Color.green;
Pen.addArc(x@y, r*pos*pos, 0.0, 2pi);
Pen.stroke;
Pen.strokeColor = Color.yellow;
Pen.addArc(x@y, r*pos*pos*pos, 0.0, 2pi);
Pen.stroke;
};
orch.put(1906, circle); //use replyID as key for function
SCDrawServer.run(orch, nil, rate: 10.0, fullscreen: true, hideCursor: true, refresh: true);
//SCDrawServer.onDemand(orch, nil, fullscreen: true, hideCursor: false, refresh: false);
)
//first example split if you want to use two computers
//boot the local server!
(
var win, table, server;
//var addr = NetAddr.localAddr; //change to address of other computer
var addr = NetAddr("128.95.92.166", 57120);
//server = Server.local; //change to server address of other computer
server = NetAddr("128.95.92.166", 57110);
win = SCWindow.new("controller",Rect(40,40,540,540));
table = SCTabletView(win, Rect(20,20,500,500));
table.background = Color.white;
win.front;
table.action = { arg view, x, y;
var ndur, z;
ndur = 3.0;
z = 0.5;
server.sendMsg(\s_new, \pluck, -1, 0, 1, \dur, ndur, \amp, z, \freq, (((500.0-y)/500.0)*24+48).midicps, \pan, (x-250.0)/250.0);
addr.sendMsg("draw", \spot, ndur, \x, x/500.0, \y, y/500.0, \r, z*10.0);
};
table.mouseDownAction = { arg view, x, y;
var ndur, z;
ndur = 1.0;
z = 1.0;
server.sendMsg(\s_new, \pluck, -1, 0, 1, \dur, ndur, \amp, z, \freq, (((500.0-y)/500.0)*24+36).midicps, \pan, (x-250.0)/250.0);
addr.sendMsg("draw", \spot, ndur, \x, x/500.0, \y, y/500.0, \r, z*25.0);
};
table.mouseUpAction = { arg view, x, y;
var ndur, z;
ndur = 3.0;
z = 1.0;
server.sendMsg(\s_new, \pluck, -1, 0, 1, \dur, ndur, \amp, z, \freq, (((500.0-y)/500.0)*24+60).midicps, \pan, (x-250.0)/250.0);
addr.sendMsg("draw", \circle, ndur, \x, x/500.0, \y, y/500.0, \r, z*25.0);
};
//send a \clear message to clear the view
table.keyDownAction = { arg view, char, mod, uni, key;
if(key == 8, { addr.sendMsg("draw", \clear); });
};
)
//boot the local server!
(
var server, circle, spot, orch=Dictionary.new();
var rect = Window.screenBounds();
server = Server.local;
SynthDef("pluck", { arg dur, amp, freq, pan;
var sig, env;
env = EnvGen.kr(Env([ 1, 0 ], [ dur ], 'linear'), 1, amp, 0, 1, 2);
sig = Pluck.ar(WhiteNoise.ar(1.0), Impulse.kr(0), freq.reciprocal, freq.reciprocal, dur, 0.25);
sig = Pan2.ar(sig, pan);
Out.ar(0, sig*env);
}).load(server);
spot = { arg perc, dict;
var x, y, r;
x = dict.at(\x) * rect.width;
y = dict.at(\y) * rect.height;
r = dict.at(\r)*(1.0-perc);
Pen.fillColor = Color.blue;
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r, 0.0, 2pi);
Pen.fill;
};
circle = { arg perc, dict;
var x, y, r;
x = dict.at(\x) * rect.width;
y = dict.at(\y) * rect.height;
r = dict.at(\r)*(1.0-perc);
Pen.fillColor = Color.blue;
Pen.strokeColor = Color.red;
Pen.addArc(x@y, r, 0.0, 2pi);
Pen.stroke;
};
orch.put('spot', spot);
orch.put('circle', circle);
SCDrawServer.run(orch, refresh: true, fullscreen: false, hideCursor: true);
//SCDrawServer.onDemand(orch, refresh: false); //compare onDemand
)