//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