Segnali e Buffers
In diverse tecniche di elaborazione del segnale in tempo reale è necessario registrare frammenti di suono più o meno lunghi in uno o più Buffer. Per alcune di queste come quelle legate all'analisi e ri-sintesi FFT basta un Buffer di pochi campioni (512, 1024, 2048) mentre per altre come la sintesi granulare dovremo utilizzare Buffers nell'ordine di millisecondi o secondi.
Allocazione della memoria
La prima cosa che dobbiamo fare è riservare uno spazio di memoria vuoto su di un Server che riempiremo dinamicamente in seguito con i valori di un segnale. Possiamo farlo invocando il metodo .alloc() su un'istanza di Buffer:
( s.boot; s.scope; s.meter; s.plotTree; ) ( Buffer.freeAll; // Prima pulisce tutto... f = Buffer.alloc(s, // server s.sampleRate * 0.5, // lunghezza in frames 1); // numero di canali g = Buffer.alloc(s, s.sampleRate * 0.5, 2); ) f.plot; g.plot;
Scrittura segnali
Possiamo scrivere i valori di uno o più segnali audio in un Buffer con la UGen RecordBuf.ar() che attraverso settaggi combinati dei suoi argomenti ci permette di sfruttare diverse possibilità. Come prima cosa però creiamo due Synths differenti che scrivono il loro output su due Buses diversi (in questo caso 9 e 10). Il primo sintetizza un segnale audio direttamente in SuperCollider mentre il secondo legge un segnale dagli input audio del computer o della soundcard con la UGen SoundIn.ar() che accetta come primo argomento il Bus (o un Array di Buses nel caso di più canali in ingresso) dal quale leggere i valori.
( SynthDef(\noise, {arg busOut=9, gain=0; Out.ar(busOut, PinkNoise.ar*gain.lag(0.2)) }).add; SynthDef(\microf, {arg busIn=0,busOut=10,gain=0; Out.ar(busOut, SoundIn.ar(busIn)*gain.lag(0.2)) }).add; )
Scrittura semplice (oneshot). In questo caso conviene sfruttare l'argomento doneAction:2 creando e distruggendo dinamicamente un Synth ogni volta che vogliamo registrare un Buffer. In tutti i casi conviene specificare un livello di gain-in mentre gli argomenti sono illustrati direttamente nel codice.
( SynthDef(\recBmono1,{arg buf=0, bus=0, gain=0,loop=0,mix=0,done=2; var in; in = In.ar(bus, 1)*gain.lag(0.2); RecordBuf.ar(in, // Bus dal quale leggere il segnale da scrivere buf, // Buffer da scrivere preLevel:mix, // se 1 = aggiunge al contenuto se 0 = sovrascrive loop:loop, // se 1 = loop doneAction:done); // si autodistrugge }).add; ) Synth(\noise,[\gain,1]); // Scrive sul Bus 9 Synth(\microf,[\gain,1]); // Scrive sul Bus 10 // Sempre dopo (.tail) il segnale da registrare Synth.tail(s,\recBmono1, [\buf,f.bufnum,\bus,[9,10].choose.postln,\gain,1]); f.plot; // Visualizza f.play; // Suona
Prestiamo attenzione all'ordine di creazione dei Synth: prima abbiamo creato i segnali da scrivere nel Buffer poi il Synth che li registra in quanto ogni volta si autodistrugge. Su quest'ultimo abbiamo dovuto invocare il metodo .tail() perchè nel Node Tree deve figurare sempre sotto i segnali da registrare essendo il signal flow dall'alto verso il basso. In questo modo potremo ri-crearlo tutte le volte che desideriamo sovrascrivere il Buffer.
Scrittura semplice con mix. Se non vogliamo sovrascrivere il contenuto di un Buffer ma aggiungere un segnale a quelli già memorizzati in un Buffer dobbiamo semplicemente modificare l'argomento preLevel da 0 a un valore > 0.0. Questo sarà anche il gain del nuovo segnale.
Synth(\noise,[\gain,1]); Synth(\microf,[\gain,1]); Synth.tail(s,\recBmono1, [\buf,f.bufnum,\bus,10,\gain,1,\mix,rrand(0.1,1.0).postln]); f.plot; // Visualizza f.play; // Suona
Scrittura in loop. Se vogliamo continuare a sovrascrivere il Buffer in loop (quando ha finito lo spazio di memoria/durata ricomincia a scrivere da capo) basterà modificare l'argomento loop da 0 a 1. Facciamo attenzione che se il loop è attivo l'argomento doneAction viene disabilitato automaticamente e non è possibile distruggere il Synth se non assegnandolo a una variabile.
Synth(\noise,[\gain,1]); Synth(\microf,[\gain,1]); a = Synth.tail(s,\recBmono1, [\buf,f.bufnum,\bus,10,\gain,1,\loop,1]); a.free; f.play; // Suona - ATTENZIONE AI LARSEN
Diverse tecniche di live electronics utilizzano questa modalità con Buffer molto corti che vanno da 512 samples a poche decine di millisecondi. Ricordiamo che la durata del Buffer è anche una eventuale latenza da sommare a quella di sistema nella rilettura del Buffer stesso.
Possiamo anche fermare la registrazione con l'argomento run:0 oppure specificare un offset in samples per non cominciare a scrivere nel Buffer dall'inizio con l'argomento offset:n.
Array di Buffers
Se vogliamo utilizzare più di un Buffer anche in questo caso conviene collezionarli in un Array per poi richiamarli con gli indici come abbiamo fatto per caricare cartelle di soundfiles
~durs = [2,5,0.1,0.02322]*s.sampleRate; ~bufs = ~durs.collect{arg i; Buffer.alloc(s, i.round, 1)}; Synth.tail(s,\recBmono1, [\buf,~bufs[0].bufnum,\bus,10,\gain,1]); Synth.tail(s,\recBmono1, [\buf,~bufs[1].bufnum,\bus,10,\gain,1]); Synth.tail(s,\recBmono1, [\buf,~bufs[2].bufnum,\bus,10,\gain,1]); Synth.tail(s,\recBmono1, [\buf,~bufs[3].bufnum,\bus,10,\gain,1]); ( ~bufs[0].plot; ~bufs[1].plot; ~bufs[2].plot; ~bufs[3].plot; )