//-- switch-from-max!
//-- workshop 080308 @pickledfeet berlin
//-- intermediate level
//--0. intro
//we will mainly cover 5 areas...
//1. envelopes
//2. patterns
//3. grains
//4. networked servers
//5. generated synthdefs
//this to show how supercollider is fundamentally different:
//max/msp, pd etc builds a static synthesis network
//ie generators and effects have their specific place in a patch
//and each change requires a rebuild of the complete signal chain
//with sc one can more dynamically add and re-order the network
//sound objects (synths, nodes) can be created on the fly
//sc pros: more cpu efficient, promotes unique sound objects
//sc cons: harder to manage nodes and trace the signal chain
//--1. envelopes
//jumping in at the deep end...
s= Server.default //keep server in variable s
s.boot //wait for the server to start
( //send the synth definition to the server s
//need to be done once after booting server
SynthDef(\pickle, {
var e= EnvGen.kr(Env.linen(1, 2, 0.1), doneAction:2);
var z= SinOsc.ar(e*1000, 0, 0.3);
Out.ar(0, z);
}).send(s);
)
Synth(\pickle) //play the synth. 3.1 seconds long
//once for each new synth object we create
//notice:
//synths will all sound the same
//see counter in server gui window going up and down 0..1..0
//doneAction:2 frees the synth when the envelope finished
//we do not need to keep track of nor stop it manually (.free it)
//Env.linen specifies our envelope: attack, sustain, release times
//EnvGen is an ugen that 'plays' or 'performs' our envelope
//e*1000 scales the envelope ugen and is then used as frequency
Env.linen(1, 2, 0.1).plot
//we make another definition and send it to s under a new name
//now it has a random sustain time and random max frequency
//so sustain and freq will be unique for each new synth created
(
SynthDef(\pickle2, {
var e= EnvGen.kr(Env.linen(1, Rand(0, 2), 0.1), doneAction:2);
var z= SinOsc.ar(e*Rand(100, 5000), 0, 0.3);
Out.ar(0, z);
}).send(s);
)
Synth(\pickle2)
Synth(\pickle2)
Synth(\pickle2)
//these can play at the same time - signals mixed on output bus 0
//again, notice the synth counter in the server gui window
//so an envelope with doneAction:2 forces its synth to free itself
//see [UGen-doneActions] overview file
//but normally only the default 0 and 2 is used
//this synth definition has 3 arguments for the envelope segments
(
SynthDef(\pickle3, {|atk= 0.1, sus= 1, rel= 0.1| //3 arguments
var e= EnvGen.kr(Env.linen(atk, sus, rel), doneAction:2);
var z= SinOsc.ar(e*Rand(100, 5000), 0, 0.3);
Out.ar(0, z);
}).send(s);
)
Synth(\pickle3, [\atk, 0.5, \sus, 0, \rel, 0.5])
//results in a triangular envelope for frequency
Synth(\pickle3, [\atk, 0.01, \sus, 1, \rel, 0.05])
//same synthdef - different arguments gives another shape
//Env's linen method is one of the simpler but there are more
//see [Env] helpfile
//it's also possible to specify type of curve and set loop points
Env.linen(0.1, 0.1, 0.1, 1, 0).plot //without curve
Env.linen(0.1, 0.1, 0.1, 1, 2).plot //curve value 2
Env.perc(0.2, 1).plot //defaults to curve -4
Env.adsr(0.01, 0.1, 0.5, 0.05).plot
Env.sine(0.4).plot //note: plot normalises
//levels, times
Env([0.4, 0.5, 0.9], [1, 2]).plot
Env([0.4, 0.5, 0.9, 0, 1, 0], [1, 2, 1, 2, 0.5]).plot
//different curves for different segments
Env([0.4, 0.5, 0.9, 0, 1, 0], [1, 2, 1, 2], [0, 3, 0, -3]).plot
//using levels and times arrays as arguments
(
SynthDef(\pickle4, {|levels= #[0, 1, 0], times= #[0.1, 0.1]|
var e= EnvGen.kr(Env(levels, times), doneAction:2);
var z= SinOsc.ar(e*Rand(100, 5000), 0, 0.3);
Out.ar(0, z);
}).send(s);
)
//array length must match and setn used
Synth(\pickle4).setn(\levels, [0, 1, 0.9], \times, [1, 1])
//using the gate argument to trigger the envelope
//see how with doneAction:0 the synth is kept alive
(
SynthDef(\pickle5, {|gate= 0|
var e= EnvGen.kr(Env([0, 1, 0], [0.1, 0.1], 0, 1), gate, doneAction:0);
var z= SinOsc.ar(e*Rand(100, 5000), 0, 0.3);
Out.ar(0, z);
}).send(s);
)
a= Synth(\pickle5) //create the synth - notice server gui window
a.set(\gate, 1) //trigger the envelope
a.set(\gate, 0) //trigger the release
a.set(\gate, 1) //trigger the envelope again
a.set(\gate, 0) //trigger the release
a.free //force the synth to deallocate/disappear
//looping envelopes
(
SynthDef(\pickle6, {|gate= 0|
var e= EnvGen.kr(Env([0, 1, 0.8, 0], [0.1, 0.1, 0.1], 0, 2, 0), gate, doneAction:0);
var z= SinOsc.ar(e*Rand(100, 5000), 0, 0.3);
Out.ar(0, z);
}).send(s);
)
a= Synth(\pickle6)
a.set(\gate, 1) //trigger envelope with loop
a.set(\gate, 0) //trigger release
a.free
//(.free clicks because we're not applying an envelope to the amp)
//(frequency is 0hz = silence when the envelope reached its end)
//(not the best solution but used here for simplicity)
//one more technique before we leave these dull examples
//playing envelopes on busses
(
SynthDef(\pickle7a, {|gate= 0, out= 10|
var e= EnvGen.kr(Env([0, 1, 0.8, 0], [0.1, 0.1, 0.1], 0, 2, 0), gate, doneAction:0);
Out.kr(out, e); //notice Out.kr= control rate output synth
}).send(s);
SynthDef(\pickle7b, {|in= 10|
var e= In.kr(in, 1); //read control signal from bus
var z= SinOsc.ar(e*Rand(100, 5000), 0, 0.3);
Out.ar(0, z);
}).send(s);
)
a= Synth(\pickle7a) //this outputs envelope on bus 10
b= Synth(\pickle7b) //this reads envelope from bus 10
a.set(\gate, 1)
a.set(\gate, 0)
a.free
b.free
//and now using envelopes to control more interesting things
(
SynthDef(\pickle8, {|freq= 100, timescale= 1|
var e= EnvGen.kr(Env.perc(0.01, 2), 1, 1, 0, timescale, doneAction:2);
var z= SinOsc.ar(0, SinOsc.ar(e*freq+freq, 0, 2pi), e*0.3);
Out.ar(0, z);
}).store; //.store here for use with patterns later
)
//although very primitive synth definition we get some variation
//we can only control base frequency and envelope duration
Synth(\pickle8, [\freq, 200])
Synth(\pickle8, [\freq, 1000])
Synth(\pickle8, [\freq, 500, \timescale, 0.1])
Synth(\pickle8, [\freq, 50, \timescale, 0.05])
//--2. patterns
//Pbind used here for sequencing synth objects defined above
a= Pbind(\instrument, \pickle8, \dur, 1, \freq, 100).play
a.stop
//see [Pattern] and [Event] helpfile
//Pseq sets the freq to 100 and 200 in an endless pattern
a= Pbind(\instrument, \pickle8, \dur, 1, \freq, Pseq([100, 200], inf)).play
a.stop
//any arguments from the synth definition can be used
//note: still the same synthdef from above for all tracks
//we create synths at a rate defined by \dur
a= Pbind(\instrument, \pickle8, \dur, 0.125, \freq, Pseq([10, 20, 30, 0, 0, 40, 20, 30, 30, 60, 0, 0], inf), \timescale, 0.05).play(quant:1)
b= Pbind(\instrument, \pickle8, \dur, Pseq([Pn(0.125, 4), 0.25, 0.5], inf), \freq, Pseq([200, 200, 300], inf), \timescale, 0.01).play(quant:1)
c= Pbind(\instrument, \pickle8, \dur, 16, \freq, 50, \timescale, 10).play(quant:1)
a.stop
b.stop
c.stop
//new but very similar definition - just some more arguments added
(
SynthDef(\pickle9, {|freq= 100, timescale= 1, modfreq= 0, atk= 0.01, rel= 1, amp= 0.3|
var e= EnvGen.kr(Env.perc(atk, rel), 1, 1, 0, timescale, doneAction:2);
var z= SinOsc.ar(modfreq, SinOsc.ar(e*freq+freq, 0, 2pi), e*amp);
Out.ar(0, z);
}).store;
)
//notice: .store instead of .send. this so pbind know about args
//more involved example
a= Pbind(\instrument, \pickle9, \dur, 0.125, \freq, Pseq([10, 20, 30, 0, 0, 40, 20, 30, 30, 60, 0, 0], inf), \timescale, 0.05, \modfreq, Pn(Pseries(128, 2, 256), inf), \amp, 0.4).play(quant:1)
b= Pbind(\instrument, \pickle9, \dur, Pseq([1, 1, 4], inf), \degree, Pseq([1, 12, 3, 0, 0, 2], inf), \timescale, Pseq([1, 1, 4], inf), \amp, 0.2, \atk, 0.1, \rel, 0.1, \modfreq, Pstutter(3, Pseq([100, 50, 5], inf))).play(quant:1)
c= Pbind(\instrument, \pickle9, \dur, Pseq([Pn(0.125, 6), 0.25], inf), \freq, Pseq([200, 200, 1300], inf), \timescale, 0.01, \amp, 0.2, \modfreq, Pseq([0, 0, 0, 100, 0, 5000, 0], inf)).play(quant:1)
d= Pbind(\instrument, \pickle9, \dur, 16, \freq, 4, \timescale, 6, \modfreq, Pseq([5000, 4000, 2000], inf), \amp, 0.1, \atk, 1, \rel, 1).play(quant:1)
e= Pbind(\instrument, \pickle9, \dur, 0.5, \freq, 5, \timescale, 1, \atk, 0.01, \rel, 0.1, \modfreq, 50, \amp, 0.6).play(quant:1)
a.stop
b.stop
c.stop
d.stop
e.stop
//--3. grains
//if we now play fast enough... (same definition as above)
a= Pbind(\instrument, \pickle9, \amp, 0.1, \dur, 0.01, \rel, 0.2).play
a.stop
//a more varied 'cloud' of sound objects
//Pwhite means pick random value between lo and hi
a= Pbind(\instrument, \pickle9, \amp, Pwhite(0, 0.2, inf), \freq, Pwhite(100, 1000, inf), \dur, Pwhite(0, 0.01, inf), \rel, Pwhite(0, 0.2, inf), \atk, Pwhite(0, 0.01, inf), \modfreq, Pwhite(100, 1000, inf)).play
a.stop
//start again
a.play
//define an effect for bus 0
(
SynthDef(\reverb, {|mix= 0|
var i= In.ar(0, 2);
var z= FreeVerb.ar(i, Lag.kr(mix, 2));
ReplaceOut.ar(0, z);
}).send(s);
)
//add effect on the fly
b= Synth(\reverb)
b.set(\mix, 0.5)
b.set(\mix, 1)
a.stop
b.free
//another odd example...
//using pbind to create clouds of effect objects
//simple band-pass filter with an envelope
//the example takes sound input from the microphone / line in
(
SynthDef(\grainEfx, {|atk= 0.01, rel= 1, amp= 0.3, freq= 400|
var i= BPF.ar(SoundIn.ar(0), freq, 0.1);
var e= EnvGen.kr(Env.perc(atk, rel), doneAction:2);
var z= i*e;
Out.ar(0, z);
}).store;
)
b= Pbind(\instrument, \grainEfx, \amp, Pwhite(0, 0.5, inf), \dur, Pwhite(0.01, 0.05, inf), \rel, Pwhite(0.01, 0.2, inf), \atk, Pwhite(0.01, 0.1, inf), \freq, Pwhite(100, 1000, inf), \mix, Pwhite(0, 1, inf)).play
b.stop
//these things are pretty hard to do in max/pd
//--4. networked servers
//now we try to play grains on a remote computer
//make sure the IP is correct, no firewall blocking and
//that the localhost server is booted on the remote computer
r= Server(\localhost, NetAddr("192.168.1.101", 57110))
//send over a synth definition
(
SynthDef(\pickle9, {|freq= 100, timescale= 1, modfreq= 0, atk= 0.01, rel= 1, amp= 0.3|
var e= EnvGen.kr(Env.perc(atk, rel), 1, 1, 0, timescale, doneAction:2);
var z= SinOsc.ar(modfreq, SinOsc.ar(e*freq+freq, 0, 2pi), e*amp);
Out.ar(0, z);
}).send(r);
)
Synth(\pickle9, nil, r) //arguments= nil, target= r
Synth(\pickle9, [\freq, 200, \timescale, 0.2], r)
//first we play every other grain in a slow tempo
a= Pbind(\instrument, \pickle9, \amp, 0.2, \freq, 100, \dur, 0.2, \modfreq, Pwhite(100, 1000, inf), \server, Pseq([r, s], inf)).play
a.stop
//then pick a server at random for each grain
a= Pbind(\instrument, \pickle9, \amp, Pwhite(0, 0.2, inf), \freq, Pwhite(100, 1000, inf), \dur, Pwhite(0, 0.01, inf), \rel, Pwhite(0, 0.2, inf), \atk, Pwhite(0, 0.01, inf), \modfreq, Pwhite(100, 1000, inf), \server, Prand([r, s], inf)).play
a.stop
//--5. generated synthdefs (if time permits)
//using genetic algorithms to breed sounds
//show n_noises and n_fmsynths
//see [RedGAPhenome] from the RedGA package
//http://www.fredrikolofsson.com/pages/code-sc.html