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