//--spektrografi, workshop@skaneskonst, oct2011
//--generating sounds that look good
/////////////////////////////////////////////
//day1
//--[mac osx only] boot internal server for scope to work
s= Server.internal;
Server.default= s;
s.boot;
//(same as clicking the default and boot buttons)
//--basic sounds
a= {SinOsc.ar([400, 404], 0, 0.1)}.scope
a.free
a= {Saw.ar([400, 404], 0.1)}.scope
a.free
a= {WhiteNoise.ar([0.1, 0.1])}.scope
a.free
//scope keyboard shortcuts: s, S, +, -, *, _, j, l, k, i, o, <space>, .
//cmd+. to stop
//alt+. on windows
//cmd+d for mac help
//f1 for windows help
a= {SinOsc.ar(500)*0.2}.scope
a.free
a= {SinOsc.ar( MouseX.kr(50, 600) )*0.2}.scope
a.free
a= {SinOsc.ar(MouseX.kr(200, 4000))*MouseY.kr(0, 0.2)}.scope
a.free
a= {SinOsc.ar(MouseX.kr(200, 4000))*SinOsc.kr(0.4)*0.2}.scope
a.free
a= {SinOsc.ar(MouseX.kr(200, 4000))*SinOsc.kr(MouseY.kr(0, 5))*0.2}.scope
a.free
//--argument positions
//SinOsc.ar(freq, phase, mul, add)
{SinOsc.ar(500)}.scope //freq only
{SinOsc.ar(500, 0, 0.2)}.scope //freq and amp
//this
{SinOsc.ar(500)*SinOsc.kr(1)}.plot(1)
{SinOsc.ar(500)*SinOsc.kr(1)}.play
//same as...
{SinOsc.ar(500, 0, SinOsc.kr(1))}.plot(1)
{SinOsc.ar(500, 0, SinOsc.kr(1))}.play
//and this...
{SinOsc.ar(500, 0, 0.2)+SinOsc.kr(1)}.scope
{SinOsc.ar(500, 0, 0.2)+SinOsc.kr(1)}.plot(1)
//same as...
{SinOsc.ar(500, 0, 0.2, SinOsc.kr(1))}.scope
{SinOsc.ar(500, 0, 0.2, SinOsc.kr(1))}.plot(1)
//--combining in different ways
{SinOsc.ar(500, 0, 0.2)+SinOsc.kr(1)+SinOsc.kr(4.6)*SinOsc.ar(100)*0.2}.plot(1)
{SinOsc.ar(500, 0, 0.2)+SinOsc.kr(1)+SinOsc.kr(4.6)*SinOsc.ar(100)*0.2}.play
{SinOsc.ar(500, 0, 0.2)*SinOsc.kr(1)+SinOsc.kr(4.6)+SinOsc.ar(100)*0.2}.plot(1)
{SinOsc.ar(500, 0, 0.2)*SinOsc.kr(1)+SinOsc.kr(4.6)+SinOsc.ar(100)*0.2}.play
//division can also give fun results
{SinOsc.ar(500, 0, 0.2)/SinOsc.kr(1)+SinOsc.kr(4.6)+SinOsc.ar(100)*0.2}.plot(1)
//--manual mixing
(
{
SinOsc.ar(100, 0, 0.1)
+SinOsc.ar(200, 0, 0.1)
+SinOsc.ar(300, 0, 0.1)
+SinOsc.ar(400, 0, 0.1)
+SinOsc.ar(500, 0, 0.1)
+SinOsc.ar(600, 0, 0.1)
+SinOsc.ar(700, 0, 0.1)
+SinOsc.ar(800, 0, 0.1)
}.plot(1)
)
//slight detune -> more complex shapes
(
{
SinOsc.ar(100, 0, 0.1)
+SinOsc.ar(202, 0, 0.1)
+SinOsc.ar(300, 0, 0.1)
+SinOsc.ar(406, 0, 0.1)
+SinOsc.ar(500.5, 0, 0.1)
+SinOsc.ar(600, 0, 0.1)
+SinOsc.ar(699, 0, 0.1)
+SinOsc.ar(810, 0, 0.1)
}.plot(1)
)
//play the above examples and listen to the differences
//also change amplitude of the individual oscillators and way of combining (+, -, *, /)
(
{
SinOsc.ar(100, 0, 0.11)
+SinOsc.ar(200, 0, 0.1)
/SinOsc.ar(300, 0, 0.1)
-SinOsc.ar(400, 0, 0.1)
+SinOsc.ar(500, 0, 0.1)
*SinOsc.ar(600, 0, 0.1)
+SinOsc.ar(700, 0, 0.06)
+SinOsc.ar(800, 0, 0.1)
+SinOsc.ar(801, 0, 0.1)
}.plot(1)
)
//kr vs ar. kr= control rate, ar= audio rate
//rule of thumb >20 ar, <20 kr
//--multichannel expansion
{SinOsc.ar([400, 500], 0, [0.1, 0.5])}.scope //stereo sound
//--mixing n number of channels to mono
{Mix(SinOsc.ar([400, 404], 0, [0.5, 0.5]))}.play
{Mix(SinOsc.ar([400, 404], 0, [0.5, 0.5]))}.plot(1)
{Mix(SinOsc.ar([400, 404, 600, 700, 505, 2000, 5], 0, 0.1))}.play
{Mix(SinOsc.ar([400, 404, 600, 700, 505, 2000, 5], 0, 0.1))}.plot(1)
//--loops
(
r= Routine({
inf.do{
0.1.wait;
a= {SinOsc.ar(500.rand+500)}.play;
0.2.wait;
a.release(0.1);
};
});
)
r.play
r.stop
a.free
//--different waveforms
{Saw.ar(400)}.plot
{LFSaw.ar(400)}.plot
{LFTri.ar(400)}.plot
{Pulse.ar(400)}.plot
{LFPulse.ar(400)}.plot
{WhiteNoise.ar(0.1)}.plot
{PinkNoise.ar(1)}.plot
{GrayNoise.ar(0.5)}.plot
{BrownNoise.ar(0.5)}.plot
{ClipNoise.ar(0.5)}.plot
//see Tour_of_UGens helpfile
//run the following examples in phase mode: shift+s
{SinOsc.ar([400, 404], 0, 0.7)}.scope
s.scope
//'s' to toggle combined channel mode
//'shift+s' to switch to phase mode
//for phase mode must use stereo
{SinOsc.ar([400, 404], 0, [MouseX.kr(0, 1), MouseY.kr(0, 1)])}.scope
{SinOsc.ar([MouseX.kr(4, 400), MouseY.kr(2, 200)], 0, 0.9)}.scope
{SinOsc.ar([MouseX.kr(4, 400), MouseY.kr(2, 200)], 0, 0.9)*LFSaw.ar(1)}.scope(bufsize:256)
//try different buffersize: 64, 128, 256, 512, 1024, 2048, 4096, 8192
//0.25 is circle width, 1 is height
{SinOsc.ar([500, 500.7], [0, pi*0.5], [0.25, 1])}.scope(bufsize:128)
{Saw.ar([500, 500.7], [0.25, 1])}.scope(bufsize:128)
{LFTri.ar([MouseX.kr(1, 100), MouseY.kr(1, 100)])}.scope(bufsize:2048)
{LFTri.ar([MouseX.kr(1, 100), MouseY.kr(1, 100)]+SinOsc.ar([4, 4.4]))}.scope(bufsize:2048)
//--recording sound
//click record button to start and stop. the resulting file will be in this directory:
thisProcess.platform.recordingsDir //run this line to post filepath
//--screenshots
//mac osx: press cmd+shift+4 and the <space>, click in the window you want to grab
//windows: click window and press Alt+Print Screen (psc) to take a screenshot under win xp
// open Accessories/Paint and Paste
//--inspiration:
//cymatics - "the study of visible sound and vibration"
http://en.wikipedia.org/wiki/Cymatics
http://www.youtube.com/watch?v=sY6z2hLgYuY
http://www.youtube.com/watch?v=3csi-2Hrzhg
http://www.youtube.com/watch?v=3zoTKXXNQIU
http://www.youtube.com/watch?v=uQWBA8q8JEs
http://www.youtube.com/watch?v=NIoFJibzww8
//using oscilloscopes
http://robinfox.net/projects/oscilloscope/
http://www.youtube.com/watch?v=GFBHVDMUgXM
//spectrograms
aphex twin http://www.youtube.com/watch?v=M9xMuPWAZW8
metasynth http://www.uisoftware.com/MetaSynth/index.php
xenakis upic http://www.youtube.com/watch?v=yztoaNakKok
/////////////////////////////////////////////
//day2
//--Stethoscope
(
w= Window.new("Stethoscope test", Rect(20, 20, 400, 500));
c= Stethoscope(s, view:w.view, bufsize: 1024);
w.front;
CmdPeriod.doOnce({c.free; if(w.isClosed.not, {w.close})});
//i, o //busses
//s, S //styles
//k //audio/control rate
//j, l //inc/dec offset
//+, - //inc/dec x zoom
//*, _ //inc/dec y zoom
//<space>, . //start/stop
)
//now make some sounds. e.g.
a= {SinOsc.ar([4, 5]*MouseX.kr(1, 100), MouseY.kr(0, 2pi), 0.9)}.play
a.free
a= {Splay.ar(SinOsc.ar([1, 2, 3, 4, 5]*MouseX.kr(1, 100), MouseY.kr(0, 2pi), 0.7))}.play
a.free
//press cmd+. / alt+. to close window and free buffer
//--ScopeView
(
b= Buffer.alloc(s, 2048, 2); //buffersize
w= Window("ScopeView test", Rect(20, 20, 640, 480), false).front;
c= ScopeView(w.view, w.view.bounds);
c.bufnum= b.bufnum;
SynthDef(\scope, {|buf|
var z= In.ar(0, 2);
ScopeOut.ar(z, buf);
}).play(RootNode(s), [\buf, b], \addToTail);
CmdPeriod.doOnce({b.free; if(w.isClosed.not, {w.close})});
)
a= {SinOsc.ar([400, 500]*SinOsc.ar(MouseX.kr(1, 100)), MouseY.kr(0, 8pi), 0.5)}.play
c.style= 0
c.style= 1
c.style= 2
c.xZoom= 2
c.yZoom= 0.5
c.waveColors= [Color.red, Color.green]
c.style= 0
a.free
//press cmd+. / alt+. to close window and free buffer
//--Spectrogram (NOTE: this example is mac osx only - also need to install the quark 'Spectrogram')
(
w= Window("Spectrogram test", Rect(10, 10, 1200, 900), false).front;
v= Spectrogram(w, Rect(0, 0, 1200, 900), 4096, Color.red, Color.black, 25, 5000); //buffersize, colours, min and max frequencies
v.start;
v.intensity= 2; //colour
v.rate= 50; //speed
CmdPeriod.doOnce({v.free; if(w.isClosed.not, {w.close})});
)
a= {SinOsc.ar([400, 500]+SinOsc.ar(MouseY.kr(0, 100), 0, MouseX.kr(1, 100)), 0, 0.5)}.play
a.free
a= {Saw.ar(100*SinOsc.ar(SinOsc.ar(SinOsc.ar(0.1)*0.2)).abs+50, 0.5)}.play
a.free
v.stop
v.free
w.close
/////////////////////////////////////////////
//amptrack
(
s.latency= 0.05;
s.waitForBoot{
//--window setup
var width= 500, height= 500;
var w= Window("amptrack", Rect(99, 99, width, height), false, false);
var u= UserView(w, Rect(0, 0, width, height));
//--variables
var fps= 60;
var num= 100; //number of tuned filter synths
var cnt= 0; //vertical drawing position
var amps= 0.dup(num); //array of current amplitudes
var o= OSCresponder(s.addr, '/tr', {|t, r, m| amps= amps.put(m[2], m[3])}).add;
var syns;
var wn= width/num;
var w2= width*0.5;
var h2= height*0.5;
//--interface
~freqMin= 200;
~freqMax= 6000;
~width= 120;
~speed= 1;
~extra= 1;
~version= 1;
//--synths
SynthDef(\avTrk, {|in, t_trig, time= 0.01, cutoff= 400, index= 0|
var z= In.ar(in, 1);
var val= Amplitude.kr(BPF.ar(BPF.ar(z, cutoff, 0.1, 5), cutoff, 0.1, 5), time, time);
SendTrig.kr(t_trig, index, val);
}).send(s);
s.sync;
syns= {|i| //each tracker with an unique peakfilter
Synth(\avTrk, [\in, 0, \index, i, \cutoff, i.linexp(0, num-1, ~freqMin, ~freqMax)]).play;
}.dup(num);
s.sync;
//--main loop
u.drawFunc= {
//--first frame clear to black
if(cnt==0, {
Pen.fillColor= Color.black;
Pen.fillRect(Rect(0, 0, width, height));
});
//--optional oval mask
/*
Pen.moveTo(Point(w2, 0));
Pen.arcTo(Point(width, 0), Point(width, h2), w2);
Pen.arcTo(Point(width, height), Point(w2, height), w2);
Pen.arcTo(Point(0, height), Point(0, h2), w2);
Pen.arcTo(Point(0, 0), Point(w2, 0), w2);
Pen.clip;
*/
switch(~version,
0, {
Pen.translate(wn*0.5, cnt%height);
amps.do{|amp, i|
Pen.fillColor= Color.grey((amp*~extra).clip(0, 1));
Pen.fillRect(Rect.aboutPoint(Point(wn*i, 0), wn*0.5, ~width));
};
cnt= cnt+~speed;
},
1, {
amps.do{|amp, i|
Pen.rotate(cnt, w2, h2);
Pen.strokeColor= Color.grey((amp*~extra).clip(0, 1));
Pen.strokeRect(Rect.aboutPoint(Point(wn*i, 0), ~width*0.1, ~width));
cnt= cnt+(~speed*0.000001);
};
},
2, {
Pen.rotate(cnt, w2, h2);
Pen.translate(w2, h2);
amps.do{|amp, i|
Pen.strokeColor= Color.grey((amp*~extra).clip(0, 1));
Pen.strokeOval(Rect.aboutPoint(Point(i*~speed, 0), ~width*0.5, ~width*0.5));
};
cnt= cnt+(~speed*0.01);
}
);
syns.do{|x| x.set(\t_trig, 1)}; //request amp data
};
//--window management
u.clearOnRefresh= false; //do not erase - just draw on top of
w.onClose= {
syns.do{|x| x.free};
o.remove;
};
w.front;
CmdPeriod.doOnce({if(w.isClosed.not, {w.close})});
//Routine({while({w.isClosed.not}, {u.refresh; (1/fps).wait})}).play(AppClock);
u.animate= true;
};
)
//change these while the program is running
~width= 50;
~speed= 2;
~speed= -0.1;
~speed= pi;
~version= 0;
~width= 20;
~speed= 1;
~width= 10;
~speed= 10;
~width= 250;
~width= 500;
~version= 2;
~width= 100;
~speed= 1;
~speed= -1.5;
~width= 20;
~extra= 7;
//--tweet0014
play{a=SinOscFB;sum({|i|a.ar(a.ar(a.ar(a.ar(i+1,1/9,999),1/9,a.ar(1/9,1,1/9)),a.ar(0.1,3),i+2*999),a.ar(1/9,1/9),1/9)}!9)!2}//#SuperCollider
/////////////////////////////////////////////
//fft
(
s.latency= 0.05;
s.waitForBoot{
//--variables
var l= 512; //try with 512 if you have a fast machine
var scale= if(l<=256, {2}, {1}); //scale can be 1 or 2
var cnt= 0; //horizontal drawing position
var fftArray= Array.fill(l, 0);
var o= OSCresponder(s.addr, '/tr', {|t, r, m|
var v= m[3].min(1); //brutal clip of mags here
var i= l.div(2);
fftArray= fftArray.put([i-m[2], i+m[2]], v); //mirror middle of array
}).add;
var trk;
//--window setup
var width= 512+10, height= 512+10;
var w= Window("fft", Rect(99, 99, width, height), false);
var u= UserView(w, Rect(0, 0, width, height));
//--interface
~speed= 1;
~version= 0;
~radius= 2/scale;
~depth= 0.01;
~trails= 0.5;
~fps= 60;
//--synths
b= Buffer.alloc(s, l, 1);
SynthDef(\avTrk, {|in= 0, t_trig= 0|
var z= In.ar(in, 1);
var chain= FFT(b, z);
Array.fill(l.div(2), {|i|
var a= Unpack1FFT(chain, l, i, 0);
var b= Demand.kr(chain>=0, 0, a);
SendTrig.kr(t_trig, i, b);
});
}).send(s);
s.sync;
trk= Synth(\avTrk, [\in, 0], RootNode(s), \addToTail);
s.sync;
//--main loop
u.drawFunc= {
switch(~version,
0, { //rectangles
Pen.translate(5, 5);
fftArray.do{|a, y|
var p= Point(cnt*scale, height-10-(y*scale));
Pen.fillColor= Color.grey((1-a).clip(0, 1));
Pen.fillRect(Rect.aboutPoint(p, scale*~radius, scale*~radius));
};
cnt= cnt+~speed%fftArray.size;
Pen.strokeColor= Color.red;
Pen.line(Point(cnt*scale, 0), Point(cnt*scale, l*scale));
Pen.stroke;
},
1, { //ovals with a little transparency
Pen.translate(5, 5);
fftArray.do{|a, y|
var p= Point(cnt*scale, height-10-(y*scale));
Pen.fillColor= Color.grey((1-a).clip(0, 1), 0.5);
Pen.fillOval(Rect.aboutPoint(p, scale*~radius, scale*~radius));
};
cnt= cnt+~speed%fftArray.size;
},
2, { //rotation
Pen.fillColor= Color.grey(1, ~trails);//clear color with some alpha
Pen.fillRect(Rect(0, 0, width, height));//manually clear with rect
fftArray.do{|a, y|
var p= Point(cnt*scale, height-(y*scale));
Pen.rotate(y/l*0.25*scale*2pi*~depth, width*0.5, height*0.5);
Pen.fillColor= Color.grey((1-a).clip(0, 1));
Pen.fillOval(Rect.aboutPoint(p, scale*~radius, scale*~radius));
};
cnt= cnt+~speed%fftArray.size;
},
3, { //lines
Pen.fillColor= Color.grey(1, ~trails);
Pen.fillRect(Rect(0, 0, width, height));
fftArray.do{|a, y|
var p= Point(cnt.fold(0, l*scale), height-y);
Pen.strokeColor= Color.grey(1-(a+0.5).clip(0, 1), 0.5);
Pen.moveTo(p*a); //move to before rotation special here
Pen.rotate(y/l*pi*0.5*scale*~depth, width*0.5, height*0.5);
Pen.lineTo(p);
Pen.stroke;
};
cnt= cnt+~speed;
}
);
trk.set(\t_trig, 1); //to all send trigs
};
//--window management
u.clearOnRefresh= false; //do not erase - just draw on top of
w.onClose= {
trk.free;
o.remove;
b.free;
};
w.front;
//Routine({while({w.isClosed.not}, {u.refresh; (1/~fps).wait})}).play(AppClock);
u.animate= true;
CmdPeriod.doOnce({if(w.isClosed.not, {w.close})});
};
)
//change these while the program is running
~version= 1;
~radius= 4;
~speed= 8;
~speed= 256/6;
~radius= 256/25;
~speed= -1;
~version= 2;
~trails= 0.01;
~radius= 5;
~speed= 2;
~radius= 0.5;
~depth= -0.002;
~depth= 3;
~radius= 1;
~trails= 0.5;
~version= 3;
~depth= 0.5;
~speed= 5;
~depth= -0.1;
~depth= -0.01
~trails= 0.01;
//waveform
(
s.latency= 0.05;
s.waitForBoot{
//--variables
var l= 750; //global window size
var theta= 0;
var fps= 60;
var arr= Array.fill(l, 0); //same as half windowsize above
var o= OSCresponder(s.addr, '/tr', {|t, r, m|
if(m[2]==0, { //redraw once for each cycle of amps
b.getn(0, l-1, {|data| {arr= data; u.refresh}.defer});
});
}).add;
var trk, cnt= 0;
//--window setup
var width= l, height= l;
var w= Window("waveform", Rect(99, 99, width, height), false);
var u= UserView(w, Rect(0, 0, width, height));
var w2= width*0.5;
var h2= height*0.5;
//--interface
~trails= 1;
~speed= 0;
~sample= 1;
~amp= 0.5;
~version= 0;
//--synths
b= Buffer.alloc(s, l, 1);
SynthDef(\avTrk, {|in= 0, bufnum, rate= 40, sample= 1|
var z= In.ar(in, 1);
var trig= Impulse.kr(rate);
var index= Phasor.ar(trig, sample, 0, BufFrames.ir(bufnum)-1);
BufWr.ar(z, bufnum, index, 0);
SendTrig.kr(trig, 0, bufnum);
}).send(s);
s.sync;
trk= Synth(\avTrk, [\in, 0, \bufnum, b, \rate, fps], RootNode(s), \addToTail);
s.sync;
//--main loop
u.drawFunc= {
if(cnt==0, {
Pen.fillColor= Color.black; //erase first time
Pen.fillRect(Rect(0, 0, width, height));
});
trk.set(\sample, ~sample);
Pen.fillColor= Color.grey(0, ~trails);
Pen.fillRect(u.bounds); //manually clear
Pen.strokeColor= Color.green;
switch(~version,
0, { //line
Pen.rotate(theta, w2, h2);
Pen.translate(0, h2);
arr.do{|y, x|
var p= Point(x, y*(height*~amp));
if(x==0, {Pen.moveTo(p)}, {Pen.lineTo(p)});
};
Pen.stroke;
},
1, { //warp
Pen.rotate(theta, w2, h2);
Pen.translate(w2, h2);
arr.do{|y, x|
var p= Point(x*~amp, y*~amp).rotate(y*2pi);
if(x==0, {Pen.moveTo(p)}, {Pen.lineTo(p)});
};
Pen.stroke;
},
2, { //flower
Pen.translate(w2, h2);
Pen.moveTo(Point(arr[0], 0)*arr[0]);
arr.do{|y, x|
var p= Point(y, x)*y;
var a= x%width/width*2pi+theta;
Pen.lineTo(p.rotate(a));
};
Pen.stroke;
}
);
theta= theta+~speed;
cnt= cnt+1;
};
//--window management
u.clearOnRefresh= false; //do not erase - just draw on top of
w.onClose= {
trk.free;
o.remove;
b.free;
};
w.front;
CmdPeriod.doOnce({if(w.isClosed.not, {w.close})});
//note no routine here. the responder is driving the animation
};
)
//change these while the program is running
~sample= 2;
~sample= 10;
~sample= 1;
~trails= 0.2;
~speed= 0.1;
~speed= -0.05;
~trails= 0.01;
~amp= 0.02;
~speed= pi*0.25;
~amp= 0.2;
~sample= 2;
~version= 1;
~trails= 0.2;
~version= 2;
~sample= 1;
~version= 1;
~speed= 2pi*1.001;
~amp= 0.5;
/////////////////////////////////////////////
//day3+4
//own project and exhibition