// 0. Server starten:
// (einzelne Zeilen nacheinander evaluieren!)

s = Server.local;
s.boot;

// 1. SynthDefs konstruieren und an server schicken:
// (alles in runder Klammer zusammen evaluieren!)
(
SynthDef("sinegrain", { arg freq=400, amp=0.2, dur=0.3, pan=0;
	var env, sig;

	// Huellkurve aus zwei gleichlangen sinusfoermigen Segmenten:
	env = EnvGen.ar(Env([0, amp, 0], [dur*0.5, dur*0.5], \sine), doneAction: 2);
	// Sinusoszillator, dessen Frequenz von aussen und dessen Amplitude durch die Huellkurve gesteuert wird: 
	sig = SinOsc.ar(freq, 0, env);
	// Stereo-Panorama-UGen, die Position wird von aussen (Argument pan) gesteuert:
	sig = Pan2.ar(sig, pan);
	// Schreiben auf die Ausgangsbusse mit OffsetOut (besseres Timing als mit Out !)
	OffsetOut.ar(0, sig); 
}).send(s);

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); 
	// Buffer-Playback mit von aussen gesteuerte Abspielgeschwindigkeit (rate) und Startposition (start):
	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);
)	

// testen mit default-Werten:
Synth("sinegrain"); 

// nun mit anderen Werten:
Synth("sinegrain", [\freq, 2000, \dur, 0.01, \amp, 0.9, \pan, 0]); 

// 2. Task konstruieren und starten:

// Beispiel 1: harmonische Reihe, sukzessiv
(
t = Task({
	// Deklaration einiger lokaler Variablen:
	var next, fr, vol, pos, dur;
	// Einrichten einer Schleife fuer 20 Durchlaeufe:
	20.do({ arg i;
		dur = 0.2;  
		next = dur * 2; // ergibt Pause zwischen Grains
		vol 	= 0.1;
		// Panoramaposition zufaellig zwischen -1 und 1:
		pos 	= rrand(-1.0, 1.0);
		// Frequenz in Abhaengigkeit vom Schleifenzaehler i:
		fr	= (i+1) * 200;
		// Erzeugen eines Grains mit den eben gesetzten Variablen:
		Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]);
		// next Sekunden warten, bis zum naechsten Durchlauf:
		next.wait;	
	});
})
)

// Task starten:
t.start;

// Task stoppen (falls nicht schon von selbst abgelaufen):
t.stop;

// Beispiel 2: granular rekonstruierter Sinuston
(
t = Task({
	var next, fr, vol, pos, dur;
	inf.do({ arg i;
		dur = 0.2;
		next = dur * 0.5;	// ergibt Overlap von 2
		vol 	= 0.5;
		pos 	= 0;
		fr	= 300;
		// praeziseres Starten der Synths:
		s.makeBundle(0.1, { Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]) });
		next.wait;	
	});
})
)

t.start;

t.stop;

// Beispiel 3: dichte zufallsgesteuerte Textur:
(
t = Task({
	var next, fr, vol, pos, dur;
	inf.do({ arg i;
		dur 	= 0.02;
		next = dur * rrand(0.1, 0.4);	// ergibt variablen Overlap
		vol 	= 0.1;
		pos 	= rrand(-1.0, 1.0);
		fr	= rrand(1000.0, 7000.0);
		Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]);
		next.wait;	
	});
})
)

t.start;

t.stop;

// Beispiel 4: dichte harmonische Textur:
(
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;	// nur ganzzahlige Vielfache von 100
		Synth("sinegrain", [\dur, dur, \freq, fr, \pan, pos, \amp, vol]);
		next.wait;	
	});
})
)

t.start;

t.stop;

// 3. Soundfile in Buffer laden:

b = Buffer.loadDialog(s);   // bitte nur Mono-Soundfiles!

// alternativ (Pfadnamen bitte entsprechend aendern!!) :
b = Buffer.readChannel(s, "/Users/abart/snd/tam-tam.aif", channels: [0]);

// Eigenschaften anzeigen:
b.query

// Abspielen:
b.play

// Ermitteln der Dauer des geladenen Soundfiles:
b.duration
// oder
b.numFrames / b.sampleRate

// Beispiel 5: zufaellige Startpositionen (am besten mit Sprachsample probieren):
(
t = Task({
	var next, rate, st, vol, pos, dur;
	inf.do({ arg i;
		dur 	= 0.2;
		next = dur * 0.1;
		vol 	= 0.8;
		pos 	= rrand(-1.0, 1.0);
		rate	= 1;
		st 	= 0.9.rand;	// Startposition zwischen 0% und 90% der Gesamtdauer
		Synth("bufgrain", [\buf, b.bufnum, \dur, dur, \rate, rate, \start, st, \pan, pos, \amp, vol]);
		next.wait;	
	});
})
)

t.start;

t.stop;

// Beispiel 6: Quinten-Textur (dazu einzelnen Ton eines Instruments bzw. Sound mit tonalem Charakter laden)
(
t = Task({
	var next, rate, st, vol, pos, dur;
	inf.do({ arg i;
		dur 	= 0.1;
		next = dur * 0.03;
		vol 	= 0.3;
		pos 	= rrand(-1.0, 1.0);
		// Zufallswahl eines Intervals und Konvertierung in ein Frequenzverhältnis:
		rate	= ([-2, -1, 0, 1, 2, 3].choose * 7).midiratio;
		st 	= 0.9.rand;
		Synth("bufgrain", [\buf, b.bufnum, \dur, dur, \rate, rate, \start, st, \pan, pos, \amp, vol]);
		next.wait;	
	});
})
)

t.start;

t.stop;

// Beispiel 7: granulare Rekonstruktion des Soundfiles im Buffer,
// dazu wird die Startposition st gleichmäßig erhöht:
(
t = Task({
	var next, rate, st, vol, pos, dur, ovlp, n, totaldur;
	// explizite Angabe des Overlap-Faktors:
	ovlp = 2;
	dur 	= 0.06;
	totaldur = b.numFrames / b.sampleRate;
	// Berechnung der Anzahl notwendiger Schleifendurchläufe aus der Soundfiledauer und den Graindauer und Overlap:
	n = ( totaldur / dur * ovlp).asInteger;
	n.do({ arg i;
		next = dur / ovlp;
		vol 	= 0.7;
		pos 	= 0;
		rate	= 1;
		// Startposition in Abhängigkeit vom Schleifenzähler und Normierung auf Gesamtdauer:
		st 	= i * next / totaldur;
		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;

// Beispiel 8: Timestretching um den Faktor 5:2
(
t = Task({
	var next, rate, st, vol, pos, dur, ovlp, n, totaldur, time;
	// das Verhaeltnis der neuen Dauer zum Original:
	time = 5 / 2;
	ovlp = 2;
	dur 	= 0.05;
	totaldur = b.numFrames / b.sampleRate;
	n = (totaldur / dur * ovlp * time).asInteger;
	n.do({ arg i;
		next = dur / ovlp;
		vol 	= 0.7;
		pos 	= 0;
		rate	= 1 ;
		st 	= (n-i) * next / totaldur / time;
		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;