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