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


)