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