// doneActions and envelopes

b = Buffer.loadDialog(s); 

(
SynthDef("sndtest2", { arg buf=0, rate=1, loop=1, amp=1.0; 
	var sig;
	sig = PlayBuf.ar(1, buf, rate * BufRateScale.ir(buf), loop: loop);  
	FreeSelfWhenDone.kr(sig);		// <-- receives the 'done' flag directly from PlayBuf
	sig = sig * amp;
	Out.ar(0, sig);
	}).play(s, [buf: b.bufnum, loop: 0]);
)

(
SynthDef("sndtest2b", { arg buf=0, rate=1, loop=1, amp=1.0; 
	var sig;
	sig = PlayBuf.ar(1, buf, rate * BufRateScale.ir(buf), loop: loop);  
	sig = sig * amp;
	FreeSelfWhenDone.kr(sig);		// <-- can't receive the \done flag from PlayBuf anymore!!!
	Out.ar(0, sig);
	}).play(s, [buf: b.bufnum, loop: 0]);
)

// DetectSilence:
(
SynthDef("detect", { arg freq=300;
	var env, trig, sig, thresh;
	thresh = 0.1;
	env = LFNoise2.kr(Rand(1, 2)).range(0, 1);
	DetectSilence.kr(env, thresh, 0, doneAction: 2);
	trig = Impulse.ar(freq * 0.01);
	trig = Decay.ar(trig, (freq * 0.03).reciprocal);
	sig = Resonz.ar(PinkNoise.ar(trig * 20), freq, 0.01) ;
	sig = Pan2.ar(sig * env, Rand(-1.0, 1.0));
	Out.ar(0, sig);
}).store
)

Synth("detect", [\freq, 300])

// { Decay.ar(Impulse.ar(1), 0.1) }.play
// which one will win the race ?
10.do { arg i;  Synth("detect", [\freq, (i+1) * 300]) }

(
SynthDef("test1", { arg gate=1;
	var env, sig;
	// a simple envelope:
	env = Linen.kr(gate, 0.1, 1, 1, doneAction: 2); 	// doneAction=2  ->  free the synth when done
	sig = BrownNoise.ar(0.1);
	sig = CombN.ar(sig, 0.1, LFTri.kr([0.2, 0.21]).range(0.015, 0.03), 2.0);
	Out.ar(0, sig * env);
}).send(s)
)

y = Synth("test1")
y.set(\gate, 0)		// 1 sec fade out
y.release( 5)

(
SynthDef("test1", { arg gate=1;
	var env, sig;
	// a simple envelope:
	env = Linen.kr(gate, 0, 1, 0, doneAction: 2); 	// doneAction=2  ->  free the synth when done
	sig = SinOsc.ar(100, pi/2, 0.5);
	Out.ar(0, sig * env);
}).send(s)
)

y = Synth("test1")
y.set(\gate, 0)	

(
SynthDef("test2", { arg dur=5;
	var env, sig;
	env = EnvGen.kr(Env([0, 0.5, 0], [ dur*0.8, dur*0.2], \sine), doneAction: 2);
	sig = BrownNoise.ar(0.1);
	sig = CombN.ar(sig, 0.1, LFNoise0.kr([17, 23]).range(0.005, 0.03), 4.0);
	Out.ar(0, sig * env);
}).send(s)
)

y = Synth("test2", [\dur, 10])  // -> the synth kills itself when the envelope ends

(
SynthDef("test2b", { arg gate=1, dur=5;
	var env, sig;
	env = EnvGen.kr(Env([0, 0.5, 0], [ dur*0.9, dur*0.1], \sine, releaseNode: 1 ), gate, doneAction: 2);
	sig = BrownNoise.ar(0.1);
	sig = CombN.ar(sig, 0.1, LFNoise0.kr([17, 23]).range(0.005, 0.03), 4.0);
	Out.ar(0, sig * env);
}).send(s)
)

y = Synth("test2b", [\dur, 10])  // -> the envelope is waiting at the releaseNode ...
// ... until the gate is set to 0:
y.set(\gate, 0)		

(
SynthDef("test", { arg att=0.1, dec=0.2, sus=0.2, rel=1, gate=1;
	var env, sig;
	env = EnvGen.ar(Env([ 0,1,sus,sus,0], [att, dec, 1, rel], -4, nil), gate, doneAction: 2);
	sig = Saw.ar(300);
	Out.ar(0, sig * env);
}).send(s);
)

y = Synth("test", [\att, 0.02, \dec, 0.5, \sus, 0.1]);
y.set(\gate, 0)
y.release(5)

(
y = SynthDef("test3", { 
	var freq, sig;
	freq = XLine.kr(10, 5000, 10, doneAction: 0); 	sig = Dust.ar(freq * [1, 1.1]);
	Out.ar(0, sig * 0.3);
}).play(s)
)

b = Buffer.loadDialog(s); // speech or drum loop
b.play
// the Amplitude UGen is an envelope follower:
(
SynthDef("sndtest2", { arg buf=0, rate=1, loop=1, amp=1.0; 
	var sig, env, mod;
	mod = PlayBuf.ar(1, buf, rate * BufRateScale.ir(buf), loop: loop);  
	env = Amplitude.ar(mod, 0.001, 0.05);
	sig = Pulse.ar([100, 101.7]) * env;
	Out.ar(0, sig);
	}).play(s, [buf: b.bufnum, loop: 1]);
)

(
t = Task({
	10.do { arg i;
		i.postln;
		1.wait;
	}
})
)

t.start

(
t = Task({
		"hallo".postln;
		1.wait;
		12.postln;
		3.sqrt.postln;
		1.5.wait;
		rrand(3, 100).postln;
		rrand(1, 4).wait;
		\ende.postln;
})
)

t.start
Routine

// 1. send the synthdefs to the server
// all tasks in this document use these 2 synthdefs !!

(
// sine grain SynthDef:
SynthDef("sinegrain", { arg freq=400, amp=0.2, dur=0.3, pan=0;
	var env, sig;

	// envelope with 2 s-like segments of the same length:
	env = EnvGen.ar(Env([0, amp, 0], [dur*0.5, dur*0.5], \sine), doneAction: 2);
	sig = SinOsc.ar(freq, 0, env);
	sig = Pan2.ar(sig, pan);
	// OffsetOut for a more precise timing
	OffsetOut.ar(0, sig); 
}).send(s);

// Synthdef for buffer granulation:
SynthDef("bufgrain", { arg buf=0, rate=1, start=0, amp=0.2, dur=0.3, pan=0;
	var env, sig;

	env = EnvGen.ar(Env([0, amp, 0], [dur*0.5, dur*0.5], \sine), doneAction: 2); 
	sig = PlayBuf.ar(1, buf, rate * BufRateScale.ir(buf), 1, start * BufSamples.ir(buf), 0);
	sig = Pan2.ar(sig * env, pan);
	OffsetOut.ar(0, sig);
}).send(s);
)	

// just a test with the defaults:
Synth("sinegrain"); 

// ... and also other values
Synth("sinegrain", [\freq, 2000, \dur, 0.01, \amp, 0.9, \pan, 0]); 

// 2. write and start a Task :

// example 1: harmonic series sucessively
(
t = Task({
	var next, fr, vol, pos, dur;
	20.do({ arg i;
		dur = 0.2;  
		next = dur * 2; // calculation of the inter-onset time
		vol 	= 0.1;
		pos 	= rrand(-1.0, 1.0);  // 1.0.rand2
		fr	= (i+1) * 200;
		Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]);
		next.wait;	// wait before doing the next iteration of the do-loop
	});
})
)

// start the Task:
t.start;

// stop it (if not already stopped by itself):
t.stop;

(
t = Task({
	var next, fr, vol, pos, dur;
	50.do({ arg i;
		dur = 0.1;  
		next = 0.1; // calculation of the inter-onset time
		vol 	= 0.1;
		pos 	= rrand(-1.0, 1.0);  // 1.0.rand2
		fr	= (i+1) * 100;
		Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]);
		next.wait;	// wait before doing the next iteration of the do-loop
	});
})
)

// start the Task:
t.start;

// stop it (if not already stopped by itself):
t.stop;
t.resume

// example 3: dense random texture:
(
t = Task({
	var next, fr, vol, pos, dur;
	inf.do({ arg i;
		dur 	= 0.02;
		next = dur * rrand(0.1, 0.4);	// variable overlap
		vol 	= 0.1;
		pos 	= rrand(-1.0, 1.0);
		fr	= exprand(1000.0, 7000.0);
		Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]);
		next.wait;	
	});
})
)

t.start;

t.stop;

(
t = Task({
	var next, fr, vol, pos, dur;
	inf.do({ arg i;
		dur 	= 0.4;
		next = dur * rrand(0.02, 0.04);
		vol 	= 0.1;
		pos 	= rrand(-1.0, 1.0);
		fr	= rrand(1, 20) * 100;	// only integer multiples of 100
		Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]);
		next.wait;	
	});
})
)

t.start;

t.stop;

b = Buffer.loadDialog(s);   // only mono soundfiles!

// alternative way (adapt it to your path names) :
b = Buffer.readChannel(s, "/Users/abart/snd/tam-tam.aif", channels: [0]);

// check it if needed:
b.query
b.play

// the duration of the soundfile:
b.duration
// or
b.numFrames / b.sampleRate

// example 5: randomized starting positions (try it with speech sounds):
(
t = Task({
	var next, rate, st, vol, pos, dur;
	inf.do({ arg i;
		dur 	= rrand(0.3, 2.0);
		next = dur * 2;
		vol 	= 0.8;
		pos 	= rrand(-1.0, 1.0);
		rate	= 1;
		st 	= 0.9.rand;	// start position between  0% and 90% of the total duration
		Synth("bufgrain", [\buf, b.bufnum, \dur, dur, \rate, rate, \start, st, \pan, pos, \amp, vol]);
		next.wait;	
	});
})
)

t.start;

t.stop;

// example 8: time stretching with a ratio of 5:2
(
t = Task({
	var next, rate, st, vol, pos, dur, ovlp, n, totaldur, time;
	time = 5 / 2;
	ovlp = 2;
	dur 	= 0.03;
	totaldur = b.numFrames / b.sampleRate;
	n = (totaldur / dur * ovlp * time).asInteger;
	n.do({ arg i;
		next = dur / ovlp;
		// next = dur / ovlp * rrand(0.98, 1.02);		// slightly randomized overlap
		vol 	= 0.7;
		pos 	= 0;
		rate	= 1 ;
	 	// rate = rrand(0.98, 1.02);		// slightly randomized pitch
		st 	= i * next / totaldur / time;
		// st 	= (n-i) * next / totaldur / time;	// backwards!
		s.makeBundle(0.1, { Synth("bufgrain", [\buf, b.bufnum, \dur, dur, \rate, rate, \start, st, \pan, pos, \amp, vol]) });
		next.wait;	
	});
})
)

t.start;

t.stop;

// Streams:

17.next

"hallo".next

SinOsc.next

[1,2,3,4,5,6].next

p = Pseq([20, 30, 10, 45, 2, "Hello", 3.4], 1);
a = p.asStream;

a.next
a.next
10.do { a.next.postln }

p = Pseq([20, 30, 10, 45, 2, \Hello, 3.4], inf);
a = p.asStream;
20.do { a.next.postln }

a.reset
a.next

a.value  // the same as next

b = p.asStream;
a.reset; b.reset

a.next
a.next
a.next
b.next

c = a + b
c.next
"hello".sqrt

d = a.sqrt

a.reset
d.next

Pseq, Pser
Prand, Pxrand, Pwrand
Pshuf
Pslide, Ptuple
Pwalk, Pfsm, 

p = Prand([30, 20, 54, 12, 6, 8], 8).asStream;
p.next

p = Pxrand([30, 20, 54, 12, 6, 8], 8).asStream;

p = Pwrand([30, 20, 54, 12, 6], [0.5, 0.1, 0.2, 0.15, 0.05  ], 50).asStream;

// value pattern:

PFunc.new(nextFunc, resetFunc) 		// compare to Array.fill(n, func)
Pseries.new(start, step, length)		// compare to Array.series(n, start, step)
Pgeom.new(start, grow, length)		// compare to Array.geom(n, start, grow)
Pwhite.new(lo, hi, length)		// compare to Array.rand(n, min, max)
Pbrown.new(lo, hi, step, length)		// brownian motion

p = Pwhite(100, 200, 5).asStream
p.next

p = ( Prand([30, 20, 54, 12, 6, 8], 8) + Pseq([100, 200], inf) ).asStream 
p.next

p = Pseq( [ Pwhite(0, 10, 3), 200 ], inf).asStream
p.next

// -----------------
(
SynthDef("chick", {arg freq, dur;
	var res1, res2, sig;
	res1 = XLine.ar(8*freq, freq, dur * 0.7);
	res2 = XLine.ar(3*freq, freq, dur * 0.5);
	sig =  RLPF.ar(Saw.ar([freq, freq/4]),[res1, res2] , 0.09, 
		EnvGen.kr(Env.perc(0.01, dur, 0.5), doneAction: 2));
	Out.ar(0, sig)
	}).send(s);

SynthDef("rev", {
	var in, rev;
	in = In.ar(0, 2);	
	rev = Mix(in);	
	6.do({ rev = LPF.ar(AllpassN.ar(rev, 1.0, [0.05.rand, 0.05.rand], 2.0), 7000)});
	ReplaceOut.ar(0, (in * 0.6) + (rev * 0.3))
	}).send(s);
)	

( // Chick Corea meets Iannis Xenakis
var pnote, rhythms, weights, pdur, tempo, group, rev;

//pnote = Pbrown(48, 92, 7, inf).asStream; 
pnote = Pseq([60, 62, 64, 65, 67, 69, 71, 72], inf).asStream;
		// Randomwalk zwischen 36 und 86, maximale Schrittweite 7
tempo = 140;								// MM 
rhythms = [1/2, 1/4, 1/8, 1/16, 1/12, 1/6]; 	// verschiedene Dauern
weights = [2, 2, 4, 20, 12, 3];				// unterschiedlich gewichtet
pdur = Pwrand( rhythms * 60 * 4/tempo, weights / weights.sum, inf).asStream; 

group = Group(s);
rev = Synth.after(group, "rev");

t = Task({
	var dur, freq;
	loop({
		freq = pnote.next.midicps;		// neuen Pitch-Wert lesen und in Frequenz konvertieren
		dur = pdur.next;				// neuen Dauernwert lesen
		Synth("chick", [\freq, freq, \dur, dur], group);
		dur.wait;
		})
	})	
)

t.start;
t.stop

d = t.copy;
d.start; t.start; 	// -> 2 tasks reading from the same stream, thus competing!

t.stop; 
d.stop;

( // Kraftwerk ?, Pink Floyd ??, Tangerine Dream ???
SynthDef("blob", {
	var trig, seq, f1, f2, w, sig, amp;
	trig = Impulse.kr(11);	
	seq = Dxrand([0, 2, 5, 7, 9, 12] + 36 , inf);	f1 = Demand.kr(trig, 0, seq ).midicps; 
	w = Pulse.ar([f1, f1*1.01]);
	sig = Decay2.ar(K2A.ar(trig), 0.05, 0.2, w );	amp = Amplitude.kr(sig/10);			f2 = SinOsc.kr(0.1, 3/2 * pi, 800, 1000);	sig = RLPF.ar(sig, f2*(1+amp), 0.2, 0.01);
	Out.ar(0, sig);		 
	}).play(s);
)

// -------------------------

Pbind.new.play

Pbind( \dur, 0.25 ).play

(
Pbind(
	\dur, 0.5,
	\legato, 1,
	\freq, Pwhite(80, 90, inf).midicps,
	\amp, Pseq([0.4, 0.1], inf)
).play
)

(
Pbind(
	\dur, 0.125,
	\legato, 0.2,
	\midinote, Pseq([0,2,4,5,7,9,11,12] + 60, inf)
).play
)

(
Pbind(
	\scale, Pseq( [ Pn([0,2,3,5,6,8,10], 8), Pn([0,2,4,5,7,9,11], 8) ], inf),
	\degree, Pseq([0,2,4,7], inf), 
	\dur, 0.125
).play
)

(
Pbind(
 	// Skalenstufen in (default) Dur-Skala
	\degree, Pseq([ Pxrand([0,2,4,7], 4), Pxrand([0,2,5,-2], 4), Pxrand([0,3,6, 1], 4), Pxrand([-1, 1, 3, 4], 4)], inf),	\dur, 0.125, 
	\octave, [4,5]
).play
)

// chords are arrays of pitches (degrees, midinotes, frequencies etc.)
(
Pbind(
	\dur, 0.8,
	\legato, 1.0,
	\instrument, \sinegrain,
	\scale, Pseq([Pseq([[0,2,4,5,7,9,11]], 5), Pseq([[0,2,3,5,7,8,10]], 5)], inf),
	\degree, Pseq([[-7, 2,4,7 ], [ -4, 3,5,7], [-3, 1,4,7], [-10, 1,4,6], [-7, 2,4,7 ] ], 2)
).play;
)

0.9.coin
0.5.coin