Slicing

Una tecnica classica di elaborazione di un segnale memorizzato in un Buffer consiste nel destrutturare la sequenza temporale lineare "spezzettandolo" in frammenti (window) di varia lunghezza per poi ricomporli adottando diverse strategie.

I parametri che possiamo controllare sono:

La tipologia di Synth che meglio si presta a questa tecnica è quella del Sample player oscillattor.

Singolo evento

In questo caso possiamo specificare tutti i parametri come argomenti e controllarli attraverso la semplice valutazione del codice come negli esempi seguenti, attraverso GUI o comandi del mouse, utilizzando i tasti del computer oppure messaggi MIDI o OSC.

s.boot;
s.plotTree;
s.meter;

(
Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\sli1, {arg buf=0,pos=0,dur=0.2,amp=0,trsp=1,dir=1,pan=0,t_gate=0,done=2;
                 var sig,bpf,env,pann;
                     sig  = PlayBuf.ar(1, buf,
                                       BufRateScale.kr(buf) * trsp.midiratio // Trasposizione...
                                                            * dir,           // ...e direzione
                                       t_gate,                               // Trigger
                                       BufSampleRate.kr(buf)* pos            // Posizione
	                                   );
                     bpf  = Env.linen(0.01,dur-0.02,0.01);
                     env  = EnvGen.kr(bpf,t_gate,doneAction:done);
                     pann = Pan2.ar(sig*env*amp,pan);
                 Out.ar(0,pann)
                }).add;
)

La trasposizione è resa dinamica specificando la deviazione del pitch in semitoni MIDI convertiti automaticamente in velocità di lettura con il metodo .midiratio e riscalati su eventuali differenze tra la rata di campionamento con la quale sono stati registrati i suoni memorizzati nel Buffer e quella utilizzata dal sistema in fase di rilettura moltipicandoli per un valore ottenuto con la UGen BufRateScale.kr(buf).

La direzione della lettura è specificata attraverso due fattori di moltiplicazione della velocità di lettura: 1 = recto, -1 = verso.

Il trigger è sincronizzato con l'inviluppo.

La posizione (onset) in questo caso è specificata in secondi e poi convertita in frames moltiplicandone il valore per la rata di campionamento del Buffer ottenuta attraverso la UGen BufSampleRate.kr(buf). Se non conosciamo la lunghezza del Buffer in secondi possiamo specificare le posizioni in fattori di moltiplicazione compresi tra 0.0 e 1.0 per poi riscalarli sulla durata del Buffer (0 = inizio, 1 = fine) ottenuta automaticamente con BufDur.kr()

Flusso dinamico

Per ottenere flussi dinamici di eventi possiamo impiegare sia tecniche di sequencing (Client side) che segnali di controllo (Server side).

Client side

In questo caso possiamo utilizzare lo stesso Synth che abbiamo programmato in precedenza.

(
Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\sli1, {arg buf=0,pos=0,dur=0.2,amp=0,trsp=1,dir=1,pan=0,t_gate=0,done=2;
                 var sig,bpf,env,pann;
                     sig  = PlayBuf.ar(1, buf,
                                       BufRateScale.kr(buf) * trsp.midiratio // Trasposizione...
                                                            * dir,           // ...e direzione
                                       t_gate,                               // Trigger
                                       BufSampleRate.kr(buf)* pos            // Posizione
	                                   );
                     bpf  = Env.linen(0.01,dur-0.02,0.01);
                     env  = EnvGen.kr(bpf,t_gate,doneAction:done);
                     pann = Pan2.ar(sig*env*amp,pan);
                 Out.ar(0,pann)
                }).add;
)

Server side

Un altro approccio consiste nel programmare non le singole finestre (o singoli eventi musicali) ma interi processi o sequenze controllando dall'Interprete solamente parametri di alto livello. Per farlo dobbiamo programmare un nuovo Synth che può essere solo monofonico.

s.boot;
s.plotTree;
s.meter;

(
Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\sli2, {arg buf=0,
                     freq=1,         // Frequenza del trigger
                     pos=#[0,0.98],  // Valori limite
                     pausa=#[0,0],
                     amp=#[1,1],
                     trsp=#[0,0],
                     dir=#[1,1],
                     pan=#[-1.0,1],
                     gate=0;         // Fade in/out dell'intera sequenza
					 
                 var trig,pau,dur,onset,tsp,dire,sig,bpf,env,amps,fade,out,pann;
                     trig  = Impulse.kr(freq.lag(0.2));                    // Trigger
                     pau   = 1-TRand.kr(pausa[0],pausa[1],trig);           // Pause
                     dur   = 1/freq * pau;                                 // Durata
                     onset = TRand.kr(pos[0],pos[1],trig) * BufFrames.kr(buf); // Provare con Stepper.ar
                     tsp   = TRand.kr(trsp[0],trsp[1],trig);
                     dire  = TRand.kr(dir[0],dir[1],trig);
                     sig   = PlayBuf.ar(1, buf,
                                        BufRateScale.kr(buf) * tsp.midiratio  // Trasposizione...
                                                             * dire,          // ...e direzione
                                        trig,                                 // Trigger
                                        onset                                 // Posizione
	                                    );
                     bpf  = Env.linen(0.01,dur-0.02,0.01);
                     env  = EnvGen.kr(bpf,trig);
                     amps = TRand.kr(amp[0],amp[1],trig);
                     fade = Linen.kr(gate,0.01,1,2,doneAction:2);
                     out  = sig * env * amps.lag(0.2) * fade;
                     pann = Pan2.ar(out, TRand.kr(pan[0],pan[1],trig).varlag(1/freq)); // Panning lento
	             Out.ar(0,pann)
                 }).add;
)

a = Synth(\sli2,[\buf,b,\gate,1]);

a.set(\pausa,#[0.1,0.5]);
a.set(\trsp,#[-4, 4]);
a.set(\pos,#[0.4,0.4]);  // Specificando due valori uguali sarà deterministico...
a.set(\freq,2);
a.set(\amp, #[0.2,1]);
a.set(\pan, #[-1,1]);
a.set(\gate,0);

b = Synth(\sli2,[\buf,b,\gate,1]); // Eventuale seconda voce...

Nell'esempio precedenti i parametri sono specificati in modo non deterministico ma possiamo ad esempio sostituire le UGens TRand.kr con altre che generano valori deterministici sottocampionate attraverso tecniche di sample and hold oppure adottare una qualsiasi tecnica di triggering illustrata nel paragrafo dedicato ai segnali di controllo.

Enveloping

Questa tecnica consiste nel modificare l'inviluppo d'ampiezza originale di un suono applicandone uno nuovo in fase di riproduzione stravolgendone le caratteristiche dinamiche.

Singolo evento

Flusso dinamico

Per ottenere flussi dinamici di eventi possiamo anche in questo caso impiegare sia tecniche di sequencing (Client side) che segnali di controllo (Server side).

Client side

In questo caso possiamo utilizzare lo stesso Synth che abbiamo programmato in precedenza.

(
w = Window("EnvelopeView", 250@250);
e = EnvelopeView(w, 250@250).drawLines_(true).drawRects_(true);
w.front;
w.onClose = {e.free;w.free;d.free;y.free;x.free};

Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\sli1, {arg buf=0,pos=0,amp=0,trsp=0,dir=1,pan=0,t_gate=0,done=2;
                 var sig,envi,bpf,env,pann;
                     sig  = PlayBuf.ar(1, buf,
                                       BufRateScale.kr(buf) * trsp.midiratio
                                                            * dir,
                                       t_gate,
                                       BufSampleRate.kr(buf)* pos
	                                   );
                     envi = Env.newClear(4);        // Crea un inviluppo vuoto di 4 nodi
                     bpf  = \env.kr(envi.asArray);   // Crea un controllo dell'inviluppo
                     env  = EnvGen.kr(bpf,t_gate,doneAction:done);
                     pann = Pan2.ar(sig*env*amp,pan);
                 Out.ar(0,pann)
                }).add;
)

Server side

Attraverso questa tecnica possiamo generare flussi sonori composti da finestre con inviluppi d'ampiezza che cambiano ad ogni trigger. Due esempi.

Looping

Le diverse tecniche di looping permettono di iterare la lettura del contenuto di un intero Buffer o di una o più parti di esso in modo continuo. Per questa loro peculiarità dobbiamo programmare un segnale di controllo che gestisca il flusso sonoro risultante all'interno del Synth. A seconda delle diverse esigenze musicali ne sceglieremo una o l'altra.

Looping di un intero Buffer

Per eseguire in loop il contenuto di un intero Buffer abbiamo due possibilità:

Senza inviluppo

Se il sound file caricato nel Buffer non ha discontinuità all'inizio e alla fine applichiamo un fade in e un fade out all'inizio e alla fine dell'intero flusso sonoro ma non tra le singole riletture. L'iterazione è controllata dall'argomento loop:1 di PlayBuf.ar().

s.boot;
s.plotTree;
s.meter;

(
Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\looper_1, {arg buf=0,amp=0,trsp=0,dir=1,fIn=0.2,fOut=1,pan=0,done=2;
                     var sig,fade,pann;
                         sig  = PlayBuf.ar(1, buf,
                                           BufRateScale.kr(buf)*trsp.midiratio*dir,
                                           1,       // Play 
                                           loop:1   // Aggiungiamo semplicemente questo argomento
                                           );
                         fade = Linen.kr(gate,fIn,amp,fOut,done); // inviluppo generale
                         pann = Pan2.ar(sig*amp*fade,pan);
                 Out.ar(0,pann)
                }).add;
)

a = Synth(\looper_1, [\buf,b,\amp,1,\gate,1]);
a.set(\trsp,rrand(0.5,4));
a.set(\gate,0);
a.release(5);   // Uguale al precedente specifica il tempo di fade out

Con inviluppo

Se il sound file caricato nel Buffer ha discontinuità all'inizio e alla fine oppure se vogliamo scriverne dinamicamente il contenuto (live) dobbiamo aggiungere un inviluppo d'ampiezza trapezoidale ad ogni lettura e sincronizzarne il trigger.

image not found

In questo caso dobbiamo anche recuperare dinamicamente la durata del Buffer ed eventualmente riscalarla sul fattore di trasposizione

(
Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\looper_2, {arg buf=0,amp=0,trsp=0,dir=1,fIn=0.2,fOut=1,pan=0,gate=0,done=2;
                     var dur,trig,sig,bpf,env,fade,pann;
                         dur   = BufDur.kr(buf) * trsp.midiratio;    // durata del Buffer (sec)
                         trig  = Impulse.ar(1/dur * trsp.midiratio); // Trigger automatico (Hz)
                         sig   = PlayBuf.ar(1, buf,
                                            BufRateScale.kr(buf)*trsp.midiratio*dir,
                                            trig,
                                            loop:0                   // loop:0
                                            );
                         bpf  = Env.linen(0.01,dur-0.02,0.01);       // Inviluppo trapezoidale
                         env  = EnvGen.ar(bpf,trig);
                         fade = Linen.kr(gate,fIn,amp,fOut,done);    // Fade inizio/fine flusso
                         pann = Pan2.ar(sig*env*fade,pan);
                 Out.ar(0,pann)
                }).add;
)

a = Synth(\looper_2, [\buf,b,\amp,1,\gate,1]);
a.set(\trsp,rrand(0.5,4));
a.set(\gate,0);

N.B. Se modifichiamo dinamicamente la trasposizione (e quindi la durata) nel corso di una singola ripetizione l'inviluppo non si riscala alla nuova durata ed il fadeout diventerà asincrono. In questi casi dovremo utilizzare la tecnica esposta in seguito per la rilettura di porzioni di Buffer specificando la durata dell'intero Buffer come porzione.

Muting

Una variante della tecnica appena illustrata è il muting che è particolarmente utile nella maggior parte delle situazioni. Consiste nel sostituire l'inviluppo trapezoidale con uno "al contrario":

Env.new([1,0,1,1],[0.01,0.01,0.98]).plot;

image not found

Per poi ritardare del tempo di fade out il trigger che fa partire la lettura del Buffer.

(
Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\looper_3, {arg buf=0,amp=0,trsp=0,dir=1,fIn=0.2,fOut=1,pan=0,gate=0,done=2;
                     var dur,trig,sig,bpf,env,fade,pann;
                         dur   = BufDur.kr(buf) * trsp.midiratio;
                         trig  = Impulse.ar(1/dur * trsp.midiratio);
                         sig    = PlayBuf.ar(1, buf,
                                             BufRateScale.kr(buf)*trsp.midiratio*dir,
                                             DelayN.ar(trig,0.01,0.01), // Ritarda del tempo
                                                                        // di fadeout
                                             loop:0                     
                                             );
                         bpf  = Env.new([1,0,1,1],
                                        [0.01,0.01,dur-0.02]);          // Muting
                         env  = EnvGen.ar(bpf,trig);
                         fade = Linen.kr(gate,fIn,amp,fOut,done);
                         pann = Pan2.ar(sig*env*fade,pan);
                 Out.ar(0,pann)
                }).add;
)

a = Synth(\looper_3, [\buf,b,\amp,1,\gate,1]);
a.set(\trsp,rrand(0.5,4));
a.set(\gate,0);

In questo caso si verifica una latenza iniziale pari a tempo di fade out che, se inferiore ai 20 millisecondi, risulta comunque percettivamente irrilevante.

Esempi

Looping di una o più porzioni di Buffer

Per eseguire in loop porzioni di un Buffer dobbiamo stabilire due nuovi valori da specificare come argomenti:

(
Buffer.freeAll;
b = Buffer.read(s,"Bach.wav".resolveRelative);

SynthDef(\looper_5, {arg buf=0,amp=0,trsp=0,dir=1,fIn=0.2,fOut=1,pan=0,gate=0,done=2,
                         pos=0,dur=0.2;
                     var durs,trig,sig,bpf,env,fade,pann;
                         durs  = dur * trsp.midiratio;          // Durata
                         trig  = Impulse.ar(durs.reciprocal);
                         sig   = PlayBuf.ar(1, buf,
                                            BufRateScale.kr(buf)*trsp.midiratio*dir,
                                            DelayN.ar(trig,0.01,0.01),
                                            BufSampleRate.kr(buf)*pos); // Posizione                            
                         bpf  = Env.new([1,0,1,1],[0.01,0.01,durs-0.02]);
                         env  = EnvGen.ar(bpf,trig);
                         fade = Linen.kr(gate,fIn,amp,fOut,done);
                         pann = Pan2.ar(sig*env*fade,pan);
                 Out.ar(0,pann)
                }).add;
)

a = Synth(\looper_5, [\buf,b,\amp,1,\gate,1]);
a.set(\trsp,rand2(3),\dur,rrand(0.2,1),\pos,rand(3.0),\dir,[-1,1].choose)
a.set(\trsp,0,\dur,rrand(0.1,1),\pos,rand(b.duration-2));
a.set(\gate,0)

Esempi

Scratching

Tutte le tecniche di scratching si basano sull'utilizzo di un segnale di controllo come puntatore per leggere gli indici di un Buffer.

image not found

Le carattteristiche del segnale puntatore (forma d'onda, eventuale frequenza se periodico, interpolazione, etc.) definiscono il tipo di elaborazione.

Singolo evento

In questo tipo di players dobbiamo specificare il punto dove inizia la lettura del Buffer e quello in cui termina (fine). La UGen BufRd.ar() accetta questi valori in frames ma musicalmente credo sia più pratico pensarli in secondi per poi convertirli automaticamente all'interno del Synth. La durata è calcolata anch'essa automaticamente dall'espressione abs(fine-inizio) e la direzione di lettura dipende da questi valori (se inizio < fine = recto, altrimenti verso). Se vogliamo invece eseguire l'intero Buffer basta specificare 0 come inizio e Buffer.duration come fine.

s.boot;
s.plotTree;

(
Buffer.freeAll;
b = Buffer.read(s,"bach.wav".resolveRelative);

SynthDef(\sctch_1, {arg buf=0,amp=0,start=0,end=0.2,trsp=0,pan=0;
                    var dur,ini,fine,punta,sig,bpf,env,pann;
                        dur   = abs(start-end) * trsp.midiratio.reciprocal; // Durata in secondi
                        ini   = SampleRate.ir*start;                        // Inizio in frames
                        fine  = SampleRate.ir*end;                          // Fine in frames
                        punta = Line.ar(ini, fine, dur);
                        sig   = BufRd.ar(1, buf, punta);
                        bpf   = Env.linen(0.01,dur-0.02,0.01);
                        env   = EnvGen.kr(bpf,1,doneAction:2);
                        pann  = Pan2.ar(sig*env*amp.lag(0.2),pan);
                    Out.ar(0, pann)
        }).add;
)

Synth(\sctch_1, [\buf,b,\amp,1,\trsp,0,\pan,0]);
Synth(\sctch_1, [\buf,b,\amp,1,\trsp,4,\start,0,\end,b.duration]); // Intero Buffer
Synth(\sctch_1, [\buf,b,\amp,1,\trsp,rand2(4),\start,rand(b.duration),\end,rand(b.duration)]);

Looper

Per realizzare un looper con questa tecnica dobbiamo utilizzare un fasore come segnale puntatore e applicare la tecnica del muting come inviluppo d'ampiezza. Entrambi sono sincronizzati da un segnale impulsivo (trigger).

(
Buffer.freeAll;
b = Buffer.read(s,"bach.wav".resolveRelative);

SynthDef(\sctch_2, {arg buf=0, amp=0,start=0,end=0.2,trsp=0,fIn=0.2,fOut=1,pan=0,gate=0,done=2;
                    var dur,ini,fine,trig,punta,sig,bpf,env,fade,pann;
                        dur   = abs(start-end) * trsp.midiratio.reciprocal;
                        ini   = SampleRate.ir*start;
                        fine  = SampleRate.ir*end;
                        trig  = Impulse.ar(dur.reciprocal);
                        punta = Phasor.ar(DelayN.ar(trig,0.01,0.01),           // ritardo per Muting
                                          BufRateScale.ir(buf)*trsp.midiratio, // Incremento  
                                          ini,fine,          
                                          ini);                                // Punto di reset 
                        sig   = BufRd.ar(1, buf, punta);
                        bpf   = Env.new([1,0,1,1],[0.01,0.01,dur-0.02]);       // Muting
                        env   = EnvGen.ar(bpf,trig);
                        fade  = Linen.kr(gate,fIn,1,fOut,done);
                        pann  = Pan2.ar(sig*env*amp.lag(0.2)*fade,pan);
                    Out.ar(0, pann)
        }).add;
)

a = Synth(\sctch_2, [\buf,b,\amp,1,\trsp,0,\pan,0]);
a.set(\gate,1);
a.set(\start,1.3,\end,2);
a.set(\start,rand(2.0),\end,rrand(1.5,4.1));
a.set(\trsp,0);
a.set(\start,0,\end,b.duration);              // Intero buffer
a.set(\gate,0);

Surfing

Nelle tecniche appena illustrate il contenuto del Buffer è letto interamente o in parte da un segnale puntatore che generando una rampa lineare si muove da una posizione a un altra in un tempo dato e una velocità costante. Nelle tecniche di surfing invece il puntatore si muove da una posizione ad un'altra controllato attraverso l'interazione con devices fisici (mouse, dispositivi Midi o Osc, Hdi o microcontrollori come Arduino) oppure attraverso segnali di controllo non lineari.

Mouse

Quando utilizziamo devices fisici il parametro più importante nella realizzazione di flussi sonori con caratteristiche morfologiche differenti è il tempo di smoothing o interpolation time ovvero il tempo che il puntatore impiega a passare da un campione al successivo. Più è basso e più lo spostamento (la lettura) sarà pronto ed immediato, più è alto più il movimento avviene lentamente, ritardando la lettura rispetto al gesto che ha prodotto l'azione.

(
Buffer.freeAll;
b = Buffer.read(s,"bach.wav".resolveRelative);

SynthDef(\sctch_3, {arg buf=0, amp=0,smooth=0.2,fIn=0.2,fOut=0.2,pan=0,gate=0,done=2;
                    var punta,sig,fade,pann;
                        punta = MouseX.kr(0,BufFrames.kr(buf),0,smooth); // Puntatore
                        sig   = BufRd.ar(1, buf, K2A.ar(punta));
                        fade  = Linen.kr(gate,fIn,1,fOut,done);
                        pann  = Pan2.ar(sig*amp.lag(0.2)*fade,pan);
                    Out.ar(0, pann)
        }).add;
)

a = Synth(\sctch_3, [\buf,b,\amp,1,\pan,0]);
a.set(\gate,1);
a.set(\smooth,2);
a.set(\smooth,0.02);
a.set(\smooth,rrand(0.1,2).postln);
a.set(\gate,0);

GUI

Nell'esempio precedente abbiamo controllato la posizione del puntatore impiegando la UGen MouseX.kr() che recupera i valori direttamente all'interno del Synth (Server side). Se invece vogliamo recuperarli da una GUI (Client side) dobbiamo farlo programmando questo parametro come argomento per poi realizzare lo smoothing all'interno del Synth e riscalare i valori a seconda dei diversi range accettati dalle UGens. In questo caso utilizziamo il metodo ..mouseOverAction_() ma qualunque altro tra quelli a disposizione può essere impiegato.

(
Buffer.freeAll;

~path = "bach.wav".resolveRelative;

b = Buffer.read(s,~path);
f = SoundFile.openRead(~path);  // Crea un'istanza di Soundfile

SynthDef(\sctch_4, {arg buf=0, amp=0,pos=0,smooth=0.2,fIn=0.2,fOut=0.2,pan=0,gate=0,done=2;
                    var punta,sig,fade,pann;
                        punta = SampleRate.ir * pos.varlag(smooth); // Puntatore
                        sig   = BufRd.ar(1, buf, K2A.ar(punta));
                        fade  = Linen.kr(gate,fIn,1,fOut,done);
                        pann  = Pan2.ar(sig*amp.lag(0.2)*fade,pan);
                    Out.ar(0, pann)
        }).add;

//---------------------------------------------------------------
// Synth

{a = Synth(\sctch_4, [\buf,b,\amp,1,\pan,0])}.defer(0.5);

//---------------------------------------------------------------
// GUI

w = Window.new("SoundFileView", Rect.new(0,0,550,210));
w.acceptsMouseOver_(true);
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free});

t = SoundFileView.new(w, Rect(5,5, 540, 200));
t.soundfile_(f);
t.read(0, f.numFrames); // N.B. Per file molto lunghi è meglio usare: t.readWithTask;
t.timeCursorOn_(true);
t.timeCursorColor_(Color.red);
t.gridOn_(false);
t.refresh;

//---------------------------------------------------------------
// Azioni

t.mouseOverAction_({arg ...args;
                    a.set(\pos,args[1].linlin(0,536,0,b.duration));
                    });
)

a.set(\gate,1);
a.set(\smooth,1);
a.set(\smooth,0.02);
a.set(\smooth,0.5);
a.set(\gate,0);

Midi/Osc

Se vogliamo controllare la posizione del puntatore attraverso un device MIDI oppure OSC possiamo utilizzare lo stesso Synth appena illustrato modificando solamente la modalità di invio dei valori a seconda del protocollo utilizzato. La posizione è visualizzata su una GUI.

(
Buffer.freeAll;

~path = "bach.wav".resolveRelative;
b = Buffer.read(s,~path);
f = SoundFile.openRead(~path);  // Crea un'istanza di Soundfile

SynthDef(\sctch_4, {arg buf=0, amp=0,pos=0,smooth=0.2,fIn=0.2,fOut=0.2,pan=0,gate=0,done=2;
                    var punta,sig,fade,pann;
                        punta = SampleRate.ir * pos.varlag(smooth); // Puntatore
                        sig   = BufRd.ar(1, buf, K2A.ar(punta));
                        fade  = Linen.kr(gate,fIn,1,fOut,done);
                        pann  = Pan2.ar(sig*amp.lag(0.2)*fade,pan);
                    Out.ar(0, pann)
        }).add;

//---------------------------------------------------------------
// Synth

{a = Synth(\sctch_4, [\buf,b,\amp,1,\pan,0])}.defer(0.5);

//---------------------------------------------------------------
// GUI (solo visualizzazione)

w = Window.new("SoundFileView", Rect.new(0,0,550,210));
w.acceptsMouseOver_(true);
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free});

t = SoundFileView.new(w, Rect(5,5, 540, 200));
t.soundfile_(f);
t.read(0, f.numFrames); // N.B. Per file molto lunghi è meglio usare: t.readWithTask;
t.timeCursorOn_(true);
t.timeCursorColor_(Color.red);
t.gridOn_(false);
t.refresh;

//---------------------------------------------------------------
// Azioni
// MIDI

MIDIClient.init;
MIDIIn.disconnectAll;
MIDIIn.connectAll;

m = MIDIdef.cc(\midi,{arg pos;

                      a.set(\pos, pos.linlin(0,127,0,b.duration)); // al Synth
                     {t.timeCursorPosition_(                       // Alla GUI
                                            pos.linlin(0,127,0,b.numFrames))
                      }.defer(0)                                   // AppClock
                });

// OSC

OSCdef.freeAll;

o = OSCdef.new(\osc, {arg pos; pos.postln;

                      a.set(\pos, pos[1].linlin(0,127,0,b.duration)); // al Synth
                     {t.timeCursorPosition_(                          // Alla GUI
                                            pos[1].linlin(0,127,0,b.numFrames))
                      }.defer(0)                                      // AppClock
					 
                     }, '/pos', recvPort:8001)
)

a.set(\gate,1);
a.set(\smooth,1);
a.set(\smooth,0.02);
a.set(\smooth,0.5);
a.set(\gate,0);	

KSig

Nell'esempio di interazione con il mouse abbiamo utilizzato un segnale di controllo della posizione interno al Synth (MouseX.kr()). Possiamo sostituirlo con qualsiasi tipo di segnale debitamente riscalato. In questo caso l'argomento principale non è il tempo di smoothing ma la frequenza del segnale utilizzato.

(
Buffer.freeAll;

~path = "bach.wav".resolveRelative;
b = Buffer.read(s,~path);
f = SoundFile.openRead(~path);  // Crea un'istanza di Soundfile

SynthDef(\sctch_5, {arg buf=0, amp=0,ksig=0,frq=0.1,fIn=0.2,fOut=0.2,pan=0,gate=0,done=2;
                    var punta,sig,fade,pann;
                        punta = Select.ar(ksig,                              // Puntatore
                                          [LFNoise1.ar(frq).range(0,BufFrames.kr(buf)), // 0
                                           LFNoise2.ar(frq).range(0,BufFrames.kr(buf)), // 1
                                           LFTri.ar(frq).range(0,BufFrames.kr(buf)),    // 2
                                           LFCub.ar(frq).range(0,BufFrames.kr(buf))     // 3
                                          ]);
                        sig   = BufRd.ar(1, buf, K2A.ar(punta));
                        fade  = Linen.kr(gate,fIn,1,fOut,done);
                        pann  = Pan2.ar(sig*amp.lag(0.2)*fade,pan);
                    Out.ar(0, pann);

// ------------------------------ Server --> Interprete x visualizzazione

		SendReply.kr(Impulse.kr(50),  // rata di campionamento
		             '/posa',         // indirizzo o nome
		             punta);          // segnale da campionare
        }).add;

//---------------------------------------------------------------
// Synth

{a = Synth(\sctch_4, [\buf,b,\amp,1,\pan,0])}.defer(0.5);

//---------------------------------------------------------------
// Visualizzazione su GUI

w = Window.new("SoundFileView", Rect.new(0,0,550,210));
w.acceptsMouseOver_(true);
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;a.free});

t = SoundFileView.new(w, Rect(5,5, 540, 200));
t.soundfile_(f);
t.read(0, f.numFrames); // N.B. Per file molto lunghi è meglio usare: t.readWithTask;
t.timeCursorOn_(true);
t.timeCursorColor_(Color.red);
t.gridOn_(false);
t.refresh;

OSCdef.freeAll;
o = OSCdef.new(\pos, {arg pos; {t.timeCursorPosition_(pos[3])}.defer(0)},'/posa')
)

a.set(\gate,1);
a.set(\ksig,rand(4).postln);
a.set(\frq,1);
a.set(\gate,0);