//audiovisual programming / mapping and visualization
//organized by lullcec, barcelona 18-20 may 2012
//----------------------------
//session #03.0
//audiovisual mappings and sharing of control data
//--sharing variables
//each time the window is updated, parameters of synths are also set
s.boot
(
var width= 640;
var height= 480;
var win= Window("red oval", Rect(10, 10, width, height), false);
var usr= UserView(win, Rect(0, 0, width, height));
var syn= {|freq= 400, pan= 0| Pan2.ar(SinOsc.ar(freq, 0, 0.2), pan)}.play;
usr.background= Color.black;
win.front;
~speed= 0.01;
~size= 100;
CmdPeriod.doOnce({win.close});
usr.drawFunc= {
var siz= ~size.value(usr.frame);
var theta= usr.frame*~speed.value(usr.frame);
var x= sin(theta)*siz;
var y= cos(theta)*siz;
Pen.translate(width*0.5, height*0.5);
Pen.fillColor= Color.red(1, 0.6);
Pen.fillOval(Rect.aboutPoint(Point(x, y), 100, 100));
syn.set(\freq, y.linexp(0-height, height, 800, 400), \pan, x.linlin(0, width, -1, 1));
};
usr.animate= true;
)
~size= 150
~speed= 0.1
~size= 50
~speed= 1
//long-winded example with three ovals
(
var width= 640;
var height= 480;
var win= Window("red green blue ovals", Rect(10, 10, width, height), false);
var usr= UserView(win, Rect(0, 0, width, height));
var syn1= {|freq= 400, pan= 0| Pan2.ar(SinOsc.ar(freq, 0, 0.2), pan)}.play;
var syn2= {|freq= 400, pan= 0| Pan2.ar(SinOsc.ar(freq, 0, 0.2), pan)}.play;
var syn3= {|freq= 400, pan= 0| Pan2.ar(SinOsc.ar(freq, 0, 0.2), pan)}.play;
usr.background= Color.black;
win.front;
~speed= 0.01;
~size= 50;
CmdPeriod.doOnce({win.close});
usr.drawFunc= {
var siz= ~size.value(usr.frame);
var theta= usr.frame*~speed.value(usr.frame);
var x1= sin(theta)*siz;
var y1= cos(theta)*siz;
var x2= sin(theta+(2pi/3))*siz;
var y2= cos(theta+(2pi/3))*siz;
var x3= sin(theta+(2pi*2/3))*siz;
var y3= cos(theta+(2pi*2/3))*siz;
Pen.translate(width*0.5, height*0.5);
Pen.fillColor= Color.red(1, 0.6);
Pen.fillOval(Rect.aboutPoint(Point(x1, y1), siz, siz));
Pen.fillColor= Color.green(1, 0.6);
Pen.fillOval(Rect.aboutPoint(Point(x2, y2), siz, siz));
Pen.fillColor= Color.blue(1, 0.6);
Pen.fillOval(Rect.aboutPoint(Point(x3, y3), siz, siz));
syn1.set(\freq, y1.linexp(0-height, height, 800, 400), \pan, x1.linlin(0, width, -1, 1));
syn2.set(\freq, y2.linexp(0-height, height, 800, 400), \pan, x2.linlin(0, width, -1, 1));
syn3.set(\freq, y3.linexp(0-height, height, 800, 400), \pan, x3.linlin(0, width, -1, 1));
};
usr.animate= true;
)
~speed= 0
~speed= 0.9pi
~speed= 1
~size= 100
~size= 300
~speed= 0.01
~speed= 0.1pi
~size= 15
~speed= -200
~size= 100
~speed= -180
~speed= -181
~speed= -181.5
~speed= -182.2
//--reading data from a bus
//the synth is tracking pitch of microphone input
//and writing out to control rate bus number 123
(
var width= 640;
var height= 480;
var win= Window("pitch tracking", Rect(10, 10, width, height), false);
var usr= UserView(win, Rect(0, 0, width, height));
var bus= Bus.control(s); //.kr, static values
var num= 10;
var mem= List.fill(num, {0});
var syn= {
var src= Pitch.kr(SoundIn.ar)[0].lag2(0.1);
var snd= SinOsc.ar(src, 0, 0.1);
Out.kr(bus.index, src);
Pan2.ar(snd, 0.5);
}.play;
usr.background= Color.black;
win.front;
CmdPeriod.doOnce({win.close});
usr.drawFunc= {
Pen.strokeColor= Color.magenta;
num.do{|i|
Pen.addRect(Rect.aboutPoint(Point(width*0.5+(i*20), mem[i]*height), 30, 10));
Pen.addRect(Rect.aboutPoint(Point(width*0.5-(i*20), mem[i]*height), 30, 10));
};
Pen.stroke;
bus.get({|x| //asynchronous operation
mem.pop; //fifo
mem.addFirst(x.explin(100, 1000, 0.9, 0.1));
});
};
usr.animate= true;
)
//--sending back data via osc
//either use SendTrig or SendReply
//in this example 5 channel control rate data is sent back to sclang 60 times per second
(
var width= 640;
var height= 480;
var win= Window("lfos", Rect(10, 10, width, height), false);
var usr= UserView(win, Rect(0, 0, width, height));
var num= 5;
var syn= {
var lfos= LFNoise2.kr((1..num)); //some lfos
var snd= SinOsc.ar(0, SinOsc.ar(lfos*400+500+lfos, 0, (lfos>0.5).lag2(0.1)));
SendReply.kr(Impulse.kr(60), '/lfos', lfos);
Splay.ar(snd);
}.play;
var data= 0.dup(num);
OSCFunc({|msg| data= msg[3..(3+num)]}, '/lfos');
usr.background= Color.black;
win.front;
CmdPeriod.doOnce({win.close});
usr.drawFunc= {
data.postln;
Pen.translate(width*0.1, 0);
num.do{|i|
Pen.fillColor= Color.hsv(i/num, 1, 1, 0.5);
Pen.fillOval(Rect.aboutPoint(Point(width/num*i, data[i].linlin(-1, 1, 0, height)), width*0.2, width*0.2));
};
};
usr.animate= true;
)
//----------------------------
//session #03.1
//mode advanced mappings with sound analysis, perception tricks, relation of audio and visuals
//check this article
//http://fredrikolofsson.com/f0blog/?q=node/316
//look at examples 01 to 11 and see difference between a/b versions. b versions have the inverted mapping of a and often look 'wrong'.
//--analysis example
//this example reads sound input from bus 0 and tracks amplitude and 'noisyness'
//clean simple sounds will draw a star with few arms
//noisy sounds will draw a start with many arms
(
s.latency= 0.05;
s.waitForBoot{
var width= 640;
var height= 480;
var win= Window("brighter is sharper", Rect(100, 100, width, height), false);
var usr= UserView(win, Rect(0, 0, width, height));
var amp= 1;
var bri= 0;
var minBri= 999999, maxBri= -999999;//for autoscaling brightness
var minAmp= 999999, maxAmp= -999999;//for autoscaling amplitude
var drawStar= {|point, num, outer, inner| //function that draws a star
num.do{|i|
Pen.moveTo(point+Polar(inner, i/num*2pi).asPoint);
Pen.lineTo(point+Polar(outer, i/num*2pi+(pi/num)).asPoint);
Pen.lineTo(point+Polar(inner, i+1/num*2pi).asPoint);
};
Pen.stroke;
};
SynthDef(\avTrk, {|in= 0, lagTime= 0.3, rate= 60|
var z= In.ar(in, 1);
var trig= Impulse.kr(rate);
var buf= LocalBuf(2048);
var chain= FFT(buf, z);
var brightness= SpecCentroid.kr(chain);
var amplitude= Amplitude.kr(z);
SendReply.kr(trig, '/track', [brightness.lag(lagTime), amplitude.lag(lagTime)]);
}).add;
s.sync;
Synth(\avTrk, [\in, 0], RootNode(s), \addToTail);
OSCFunc({|msg|
if(msg[3]<minBri, {minBri= msg[3]});
if(msg[3]>maxBri, {maxBri= msg[3]});
if(msg[4]<minAmp, {minAmp= msg[4]});
if(msg[4]>maxAmp, {maxAmp= msg[4]});
bri= msg[3].linlin(minBri, maxBri, 0, 1);
amp= msg[4].linlin(minAmp, maxAmp, 0, 1);
}, '/track', s.addr);
usr.background= Color.black;
usr.drawFunc= {
Pen.translate(width*0.5, height*0.5); //offset drawing to the centre
Pen.strokeColor= Color.white; //always stroke with white color
//drawStar.value(Point(0, 0), bri.linexp(0, 1, 1, 50).round, amp*width*0.125, amp*width*0.25);
Pen.line(Point(0, 0), Point(amp*width, amp*height).rotate(bri*2pi));
Pen.stroke;
};
win.front;
usr.animate= true;
CmdPeriod.doOnce({if(win.isClosed.not, {win.close})});
};
)
//start some sound to make the graphics work. here a mic input
a= {WhiteNoise.ar}.play //careful with feedback, turn down sound
a.free
a= {SinOsc.ar(400, SinOsc.ar(1000, 0, MouseX.kr(0, 10)), 0.5)}.play
a.free
a= {SoundIn.ar}.play //careful with feedback, turn down sound
a.free
//----------------------------
//session #03.2
//audiovisual instruments with external control
//adapted from Example19 - japan balls in article above
(
s.latency= 0.05;
s.waitForBoot{
var width= 500;
var height= 500;
var win= Window("japan balls", Rect(100, 100, width, height), false);
var usr= UserView(win, Rect(0, 0, width, height));
var theta= 0; //a counter
var num= 15; //try also with more balls
var syns= {
SynthDef(\av, {|freq= 400, amp= 0, pan= 0|
var z= SinOsc.ar(0, SinOsc.ar(freq, 0, 2pi), amp);
Out.ar(0, Pan2.ar(z, pan));
}, #[0.02, 0.05, 0.05]).play(s);
}.dup(num);
s.sync;
//--interface
~speed= 0.25;
~radius= 10;
~spread= 10;
~distancex= 1;
~distancey= 0.5;
~offsetx= -0.1;
~offsety= -0.2;
~root= 100;
~amp= 1;
usr.drawFunc= {
Pen.fillColor= Color.red; //set the fill color to red
Pen.fillOval(usr.bounds); //draw a filled oval with the size of the whole window
Pen.translate(width*0.5, height*0.5); //offset all drawing to middle of the window
Pen.fillColor= Color.grey(1, ~amp); //set the fill color. alpha mapped to amplitude
num.do{|i|
var a, x, y;
a= i%num/num*2pi+theta; //angle in radians
x= sin(a+(i*~offsetx))*(~spread+(i*~distancex));
y= cos(a+(i*~offsety))*(~spread+(i*~distancey));
Pen.fillOval(Rect.aboutPoint(Point(x, y), ~radius, ~radius));
if(((x.hypot(y)/1.42/~spread).min(0.5)/num).isNil, {"nil".postln});
syns[i].set( //update corresponding synth
\freq, y.linexp(height.neg*0.5, height*0.5, ~root, 1000),
\amp, (x.hypot(y)/1.42/~spread).min(0.5)/num*~amp,
\pan, x.linlin(width.neg*0.5, width*0.5, -1, 1)
);
};
theta= theta-~speed%2pi; //our counter counts in radians
};
usr.background= Color.white;
win.onClose= {syns.do{|x| x.free}};
win.front;
usr.animate= true;
CmdPeriod.doOnce({if(win.isClosed.not, {win.close})});
};
)
~speed= 1
( //use midi input to control the program
//below optimised for korg nanokontrol
//change control change numbers to match your midi device
MIDIIn.connectAll;
MIDIFunc.cc({|...args|
var val= args[0];
args.postln;
switch(args[1],
2, {~speed= val.linlin(0, 127, 0, 2pi)}, //sets the ~speed
3, {~radius= val.linlin(0, 127, 1, 100)},
4, {~spread= val.linlin(0, 127, 0, 300)},
5, {~distancex= val.linlin(0, 127, -10, 10)},
6, {~distancey= val.linlin(0, 127, -10, 10)},
8, {~offsetx= val.linlin(0, 127, -pi*0.5, pi*0.5)},
9, {~offsety= val.linlin(0, 127, -pi*0.5, pi*0.5)},
12, {~root= val.midicps},
13, {~amp= val/127}
);
});
)
//--input possibilities
//see help files for the following - the list is incomplete...
//MIDIIn, MIDIFunc : midi controllers, keyboards
//SerialPort : arduino and other serial devices
//OSCFunc : open sound control, remote sc, other applications
//GeneralHID : joysticks and gamepads
//Document, View : with the globalKeyDownAction you can catch keystrokes
//KeyState : ugen for catching keystrokes
//SoundIn : ugen from getting sound from a microphone
//TabFileReader, CSVFileReader : reading data from files
//Pipe : unix stdin
//GUI : built-in sliders, buttons, knobs etc
//RedInfoBat : third-party for reading laptop internal accelerometer, battery capacity, light sensors
//----------------------------
//session #03.3
//finishing up and presenting your own projects