Sistemi circolari
Un sistema di diffusione del suono che rappresenta ormai uno degli standard multicanale è quello composto da un numero variabile di altoparlanti disposti in modo circolare attorno al pubblico. Può essere un particolare tipo di quadrifonia oppure se gli altoparlanti sono cinque pentafonia, sei esafonia, sette eptafonia, otto ottofonia e via dicendo fino a a 128 canali. Questi tipi di sistemi possono essere controllati in SuperCollider attraverso una sola UGen: PanAz.ar() indipendentemente dal numero di altoparlanti impiegato e sottointende principalmente il modo di pensare lo spazio descritto nel punto 2 tra quelli esposti all'inizio del paragrafo precedente ovvero pensando il sistema di diffusione sonora come un vero e proprio strumento musicale con le sue caratteristiche e le sue tecniche strumentali. In seguito esamineremo le tipologie di diffusione più utilizzate, ma i principi espressi valgono anche per tutte le altre configurazioni da 3 a 128 canali.
Pentafonia, esafonia e ottofonia
Utilizziamo queste tre configurazioni ampiamente diffuse per descrivere nel dettaglio i parametri espressi come argomenti di PanAz.ar(). In questo caso la posizione della sorgente non è specificata attraverso coordinate cartesiane (x/y) come per Pan4.ar() ma segue un sistema derivato dalle coordinate polari dove lo spazio non è delimitato da un quadrato ma da un cerchio e sia gli altoparlanti che i bus audio non seguono una disposizione a coppie stereofoniche come nel caso della quadrifonia appena descritta ma circolare:
Nel dettaglio gli argomenti sono:
PanAz.ar(n_chans, sig, pos, level, width, orientation)
n_chans è il numero di altoparlanti che utilizziamo nella configurazione (da 3 a 128).
sig è il segnale mono da posizionare nello spazio virtuale.
pos è la posizione di sig sul cerchio ideale disegnato nella figura precedente e può essere specificato in valori compresi tra 0.0 e 2.0. Il valore 0 corrisponde al punto centrale frontale ma la sua posizione esatta dipende dall'ultimo argomento (orientation):
- Se orientation = 0.5, pos = 0.0 è al centro tra i due altoparlanti sul fondo (bus 0 e bus 1) e i valori aumentano in
senso antiorario fino a +2.0:
s.options.numOutputBusChannels_(6); s.reboot; s.plotTree; s.scope; s.meter; ( SynthDef(\esaf, {arg pos= 0,lev=1,dist=1,orient=0.5,dur=0.2; var nchans=6,sig,env,spos,slev,sdist,pan; sig = SinOsc.ar(Rand(400,2000)); env = EnvGen.kr(Env.perc(0.01,0.19),Impulse.kr(5)); spos= VarLag.kr(pos,dur); slev= Lag.kr(lev,dur); sdist=VarLag.kr(dist.linexp(0,1,nchans,2),dur); pan = PanAz.ar(nchans, sig*env, spos, slev, sdist, orient); Out.ar(0,pan) } ).add; {~synth = Synth(\esaf,[\orient,0.5])}.defer(0.1); ) ~synth.set(\pos,0); // centro fondo (bus 0/1) ~synth.set(\pos,0.5); // laterale destro (bus 2) ~synth.set(\pos,1); // centro frontale (bus 3/4) ~synth.set(\pos,1.5); // laterale sinistro (bus 5) ~synth.set(\pos,2); // centro fondo (bus 0/1)
- Se invece cambiamo nel codice precedente orientation = 0.0, la pos = 0 corrisponde esattamente all'altoparlante che
diffonde il bus 0:
~synth= Synth(\esaf,[\orient,0.0]); ~synth.set(\pos,0); // bus 0 ~synth.set(\pos,0.33333); // bus 1 ~synth.set(\pos,0.66666); // bus 2 ~synth.set(\pos,1); // bus 3 ~synth.set(\pos,1.33333); // bus 4 ~synth.set(\pos,1.66666); // bus 5 ~synth.set(\pos,2); // bus 0
- Se orientation = 0.5, pos = 0.0 è al centro tra i due altoparlanti sul fondo (bus 0 e bus 1) e i valori aumentano in
senso antiorario fino a +2.0:
level è un fattore di moltiplicazione dell'ampiezza del segnale monofonico compreso tra 0.0 e 1.0 e come per Pan4.ar() non lo utilizzeremo per simulare la distanza ma per effettuare bilanciamenti e correzioni di ampiezza tra sorgenti diverse presenti nello stesso spazio acustico virtuale.
width è un fattore di distanza per posizionare idealmente la sorgente all'interno dello spazio virtuale delimitato dal bordo del cerchio formato dagli altoparlanti. In questo tipo di sistemi infatti dobbiamo pensare la posizione delle sorgenti in relazione ad un punto di ascolto ideale che corrisponde al centro dello spazio virtuale dove tutti gli altoparlanti diffondono il segnale alla stessa ampiezza:
L'argomento width distribuisce il segnale per il numero di altoparlanti specificato e accetta valori compresi tra 2 e il numero di altoparlanti utilizzato. Un valore di 2.0 definisce una posizione della sorgente a livello del bordo del cerchio ideale (il segnale è distribuito tra due altoparlanti contigui) mentre un valore corrispondente al numero di altoparlanti impiegati definisce una posizione al centro del cerchio (posizione di ascolto ideale) mentre i valori intermedi definiscono posizioni concentriche comprese tra il centro e il bordo. Per comodità decidiamo di uniformare il range tra 0.0 e 1.0 rendendo necessario un riscalaggio esponenziale dei valori attraverso il metodo n.linexp(0,1,nchans,2) dove 1.0 corrisponde al bordo mentre 0.0 al centro del cerchio. Utilizzando questo argomento in combinazione con pos possiamo così definire qualsiasi punto nello spazio delimitato dagli altoparlanti:~synth = Synth(\esaf,[\orient,0.5]); ~synth.set(\pos,0,\dist,1.00); // centro fondo ~synth.set(\pos,0,\dist,0.75); ~synth.set(\pos,0,\dist,0.50); ~synth.set(\pos,0,\dist,0.25); ~synth.set(\pos,0,\dist,0.00); // centro ~synth.set(\pos,1,\dist,0.00); // centro ~synth.set(\pos,1,\dist,0.25); ~synth.set(\pos,1,\dist,0.50); ~synth.set(\pos,1,\dist,0.75); ~synth.set(\pos,1,\dist,1.00); // centro frontale
Per quanto riguarda l'ottofonia che è diventato ormai il sistema di diffusione standard per la musica elettroacustica vale tutto quanto riportato per pentafonia ed esafonia, basterà solo cambiare il primo argomento di PanAz.ar() definendo correttamente il numero di altoparlanti utilizzato. Nelle immagini seguenti alcune configurazioni tipiche con i codici più propri per controllare lo spazio:
s.options.numOutputBusChannels_(68; s.reboot; s.plotTree; s.scope; s.meter; ( SynthDef(\ottof, {arg pos= 0,lev=1,dist=1,orient=0.5,dur=0.2; var nchans=8,sig,env,spos,slev,sdist,pan; sig = SinOsc.ar(Rand(400,2000)); env = EnvGen.kr(Env.perc(0.01,0.19),Impulse.kr(5)); spos= VarLag.kr(pos,dur); slev= Lag.kr(lev,dur); sdist=VarLag.kr(dist.linexp(0,1,nchans,2),dur); pan = PanAz.ar(nchans, sig*env, spos, slev, sdist, orient); Out.ar(0,pan) } ).add; {~synth = Synth(\ottof,[\orient,0.5])}.defer(0.1); ) ~synth.set(\pos,0); // fondo centro (bus 0/1) ~synth.set(\pos,0.25);// fondo destra (bus 1/2) ~synth.set(\pos,0.5); // laterale destra (bus 2/3) ~synth.set(\pos,0.75);// frontale destra (bus 3/4) ~synth.set(\pos,1); // frontale centro (bus 4/5) ~synth.set(\pos,1.25);// frontale sinistra (bus 5/6) ~synth.set(\pos,1.5); // laterale sinistra (bus 6/7) ~synth.set(\pos,1.75);// fondo sinistra (bus 7/0) ~synth.set(\pos,2); // fondo centro (bus 0/1)
~synth = Synth(\ottof,[\orient,0.0]); ~synth.set(\pos,0.00, \dist, 1); // bus 0 ~synth.set(\pos,0.25, \dist, 1); // bus 1 ~synth.set(\pos,0.50 ,\dist, 1); // bus 2 ~synth.set(\pos,0.75, \dist, 1); // bus 3 ~synth.set(\pos,1.00, \dist, 1); // bus 4 ~synth.set(\pos,1.25, \dist, 1); // bus 5 ~synth.set(\pos,1.50, \dist, 1); // bus 6 ~synth.set(\pos,1.75, \dist, 1); // bus 7 ~synth.set(\pos,2.00, \dist, 1); // bus 0
GUI per sistemi circolari
Per visualizzare la sorgente nei sistemi di diffusione circolare come quelli appena esposti possiamo utilizzare Slider2D.ar(). Partiamo da alcune considerazioni:
Abbiamo visto che PanAz.ar() accetta posizioni espresse in un sistema simile a quello delle coordinate polari dove un valore indica il raggio di un ipotetico cerchio (ρ - rho) mentre un altro valore l'angolo in gradi tra 0° e 360° (θ - theta) e che in un sistema esafonico preso come esempio può essere rappresentato nel modo seguente:
Slider2D.ar() invece accetta valori espressi in coordinate cartesiane (x/y) e la differenza tra i due sistemi ci costringe a:
- Specificare le posizioni in valori compresi tra 0 e 2 per quanto riguarda il θ (theta) e tra 0 e 1 per quanto riguarda il ρ (rho) come stabilito in precedenza.
- Inviare questi valori a PanAz.ar() come illustrato in precedenza.
- Convertire i valori che ritornano via OSC da coordinate polari a cartesiane.
- Inviare a Slider2D.ar() i valori convertiti.
Per convertire le coordinate da polari a cartesiane dobbiamo esprimere le posizioni (θ - theta) di un punto sull'angolo giro (la circonferenza nell'immagine precedente) in valori compresi tra 0π e 2π:
In SuperCollider dobbiamo allora:
- aggiungere il π ai valori di θ (theta) che già utilizziamo per PanAz.ar()
- traslare il punto 0π di -90° sottraendo 0.5 in quanto abbiamo stabilito che la posizione 0.0
deve coincidere con il punto centrale al fondo (a metà tra bus 0 e bus 1 o coincidente con bus 0 a seconda dell'argomento
orientation di PanAz.ar())
- rendere negativo il π per invertire il verso dell'incremento dei valori da antiorario a orario.
( var theta,radianti; theta = 0; // testare con valori compresi tra 0 e 2 radianti = theta - 0.5 * -pi )
- applicare le formule di conversione da coordinate polari a cartesiane:
x = ρ*cos(θ) y = ρ*sin(θ)
( a = {arg theta=0,rho=1; var x,y; x = rho * cos(theta -0.5* -pi); // tra +/- 1 y = rho * sin(theta -0.5* -pi); // tra +/- 1 ["x: "++x.round(0.01),"y: "++y.round(0.01)] // stampa } ) a.value(0,1);
ottenendo i valori x/y che possiamo inviare a Slider2D.ar().
Il codice per generare l'interfaccia è lo stesso usato in precedenza per la quadrifonia, l'unica differenza consiste nel fatto che in questo caso a causa delle peculiarità del tipo di panning non è possibile interagire con il mouse sullo Slider2D ma solo visualizzare la posizione della sorgente. Possiamo invece utilizzare una combinazione di 2 GUI: Knob (per il theta) e Slider (per il rho) sia per l'interazione con il mouse che per la visualizzazione dei valori provenienti da controller MIDI o OSC esterni:
( // ------------------------------ SynthDef e Synth SynthDef(\ottof, {arg pos= 0,lev=1,dist=1,orient=0.5,dur=0.2; var nchans=8,sig,env,spos,slev,sdist,pan; sig = SinOsc.ar(Rand(400,2000)); env = EnvGen.kr(Env.perc(0.01,0.19),Impulse.kr(5)); spos= VarLag.kr(pos,dur); slev= Lag.kr(lev,dur); sdist=VarLag.kr(dist.linexp(0,1,nchans,2),dur); pan = PanAz.ar(nchans, sig*env, spos, slev, sdist, orient); SendReply.kr(Impulse.kr(50), '/pos', [spos,1-sdist.linlin(2,nchans,0,1)]); // theta (0-2) e rho (0-1) Out.ar(0,pan) } ).add; {~synth = Synth(\ottof)}.defer(0.1); // ------------------------------ GUI w = Window.new("Pan", 250@220); w.alwaysOnTop; w.front; w.onClose_({~synth.free;w.free;b.free;c.free;d.free;e.free;f.free;g.free}); b = NumberBox.new(w, Rect(48, 195, 50, 20)).value_(0); // NumberBox theta c = NumberBox.new(w, Rect(140, 195, 50, 20)).value_(0); // NumberBox rho d = StaticText.new(w, Rect(10, 198, 180, 15)) // Static text .string_("theta: rho:"); e = Slider2D.new(w, Rect(10, 10, 180, 180)) // Slider2D .x_(0.5).y_(1); f = Knob.new(w, Rect.new(195,10,50,50)); g = Slider.new(w, Rect.new(195,90,50,100)); OSCdef.new(\vedi, {arg msg; var theta,rho,x,y; theta = msg[3]; rho = msg[4]; x = rho * cos(theta -0.5* -pi); // tra +/- 1 y = rho * sin(theta -0.5* -pi); // tra +/- 1 {b.value_(theta); // alle diverse GUI c.value_(rho); e.x_((rho * cos(theta -0.5* -pi)).linlin(-1,1,0,1)); e.y_((rho * sin(theta -0.5* -pi)).linlin(-1,1,0,1)); f.value_(theta.linlin(-1,1,0,1)); g.value_(rho); }.defer(0); },'/pos', s.addr); // ------------------------------ Operazioni di basso livello f.action_({arg val; ~synth.set(\pos, val.value * 2 - 1, \dur, 0.02)}); g.action_({arg val; ~synth.set(\dist, val.value, \dur, 0.02)}); MIDIIn.disconnectAll; MIDIIn.connectAll; MIDIdef.cc(\knob, {arg val; ~synth.set(\pos, val/127 * 2 - 1, \dur, 0.02)}, 16); MIDIdef.cc(\slid, {arg val; ~synth.set(\dist, val/127, \dur, 0.02)}, 0); ) // ------------------------------ Sequencing a = SystemClock.sched(0, {~synth.set(\pos,rand2(1.0),\dist,rand(1.0),\dur,0.3); 0.3}); a.clear;
Movimenti dinamici
Client Side
Il codice appena illustrato può essere utile per tutte le modalità di controllo da Interprete (dal codice, da GUI e da devices), utilizziamolo allora per illustrare alcune tecniche di sequencing.
Singolo valore. Per quanto riguarda l'invio di singoli valori vale quanto detto nel paragrafo corrispondente riguardante il panning stereo, ovvero possiamo specificare movimenti in qualsiasi punto nello spazio attraverso tre valori (theta, rho, durata):
(theta:tra +/- 1.0,rho:tra 0 e 1, dur:tempo)
~synth.set(\pos,0.00,\dist,1,\dur,1); ~synth.set(\pos,0.25,\dist,1,\dur,1); ~synth.set(\pos,0.50,\dist,1,\dur,1); ~synth.set(\pos,0.75,\dist,1,\dur,1); ~synth.set(\pos,1.00,\dist,1,\dur,1); ~synth.set(\pos,1.25,\dist,1,\dur,1); ~synth.set(\pos,1.50,\dist,1,\dur,1); ~synth.set(\pos,1.75,\dist,1,\dur,1); ~synth.set(\pos,2.00,\dist,1,\dur,1); ~synth.set(\pos,0,\dist,0,\dur,1); ~synth.set(\pos,rand2(1.0),\dist,rand(1.0),\dur,rrand(0.1,2))
( var pos; pos = [[0,1,1],[-0.4,0.4,0.3],[-0.6,0.3,2],[0,0.4,1],[1,0,3],[-1,1,5]]; // [theta,rho,time] h = Routine.new({ pos.do({arg i; ~synth.set(\pos,i[0],\dist,i[1],\dur,i[2]); i[2].wait }) }).reset.play; ) h.stop;
( var theta,rho,dur; h = Routine.new({ inf.do({theta = rand2(1.0); rho = rand(1.0); dur = rrand(0.01,3); ~synth.set(\pos,theta,\dist,rho,\dur,dur); dur.wait }) }).reset.play ) h.stop;
Rota. In questo caso rappresenta una rotazione sequenziale tra gli altoparlanti che può avvenire sia in senso orario che antiorario e rappresenta una delle prime tecniche di spazializzazione utilizzate nella musica elettronica:
// Senso orario ( var dur, valO; dur = 1; // tempo di giro in secondi valO = 0; // posizione iniziale h = Routine.new({ inf.do({ valO = valO+2; ~synth.set(\pos,valO,\dist,1,\dur,dur); dur.wait; }) }).reset.play; ) h.stop; h.reset.play; // Senso antiorario ( var dur, valO; dur = 1; // tempo di giro in secondi valO = 0; // posizione iniziale h = Routine.new({ inf.do({ valO = valO-2; ~synth.set(\pos,valO,\dist,1,\dur,dur); dur.wait; }) }).reset.play; ) h.stop; h.reset.play;
Spreading. Anche in questo caso basta aggiungere la coppia di valori che definisce l'asse delle x.
( var rth,rrh,dur,smt; rth = [0,0.5]; rrh = [1,0.5]; dur = 0.1; smt = 0.1; // prova a cambiare ~ksynth.value(smt:smt); h = Routine.new({ inf.do({var theta,rho; theta = rrand(rth[0],rth[1]); rho = rrand(rrh[0],rrh[1]); ~synth.set(\pos,theta,\dist,rho,\dur,dur); dur.wait }) }).reset.play ) h.stop;
Server side
Segnali
Se vogliamo utilizzare segnali di controllo possiamo anche in questo caso modificare il codice realizzato per il panning stereofonico e quadrifonico, specificando un segnale di controllo bipolare (+/- 1.0) per il parametro pos e valori fissi o un secondo segnale di controllo riscalato in un ambito compreso tra il numero di canali e 2 per il parametro dist:
( // ------------------------------ SynthDef e Synth SynthDef(\circ_sig, {arg tipoS=0,velS=1,sprdS=1,initpoS= 0, lev = 1, tipoD=0,velD=1,sprdD=1,initpoD=1, orient=0.5, smt=0.2; var nchans=8,sig,env,pos,dist,pan; sig = SinOsc.ar(Rand(400,2000)); env = EnvGen.kr(Env.perc(0.01,0.19),Impulse.kr(5)); pos = Select.kr(tipoS,[LFSaw.kr(velS,initpoS,sprdS), // orario LFSaw.kr(velS,initpoS,sprdS)* -1,// antiorario SinOsc.kr(velS,initpoS,sprdS), LFNoise1.kr(velS,sprdS), LFNoise2.kr(velS,sprdS) ]); dist = Select.kr(tipoD,[initpoD.lag(smt), // fissa LFTri.kr(velD,initpoD,sprdD), SinOsc.kr(velD,initpoD,sprdD), LFNoise1.kr(velD,sprdD), LFNoise2.kr(velD,sprdD), ]); SendReply.kr(Impulse.kr(50), '/pos', [pos,dist]); pan = PanAz.ar(nchans, sig*env, pos, lev.lag(smt), dist.linexp(0,1,nchans,2), // riscalato orient); Out.ar(0,pan) } ).add; {~synth = Synth(\circ_sig)}.defer(0.1); // ------------------------------ GUI w = Window.new("Pan", 270@210); w.alwaysOnTop; w.front; w.onClose_({~synth.free;w.free;b.free;c.free;d.free}); b = Knob.new(w, Rect(210, 5, 50, 50)); // Knob c = Slider.new(w, Rect(210, 55, 50, 150)); // Slider d = Slider2D.new(w, Rect(5, 5, 200, 200)); // Slider2D // ------------------------------ Operazioni di basso livello b.action_({arg i; ~synth.set(\velS,i.value.linlin(0,1,0.1,5), // solo al Synth \velD,i.value.linlin(0,1,0.1,5))}); c.action_({arg i; ~synth.set(\tipoD,0,\initpoS,i.value,\initpoD,i.value)}); OSCdef.new(\vedi, {arg msg; {b.value_(msg[3]); // alle diverse GUI c.value_(msg[4]); d.x_((msg[4] * cos(msg[3] -0.5* -pi)).linlin(-1,1,0,1)); d.y_((msg[4] * sin(msg[3] -0.5* -pi)).linlin(-1,1,0,1)) }.defer(0) },'/pos', s.addr); MIDIIn.disconnectAll; MIDIIn.connectAll; MIDIdef.cc(\knob, {arg val; ~synth.set(\velS,val.linlin(0,127,0.1,5), \velD,val.linlin(0,127,0.1,5)); }, 16); // dal cc 16 MIDIdef.cc(\slid, {arg val; ~synth.set(\tipoD,0, // al Synth \initpoS,val.linlin(0,127,0,1), \initpoD,val.linlin(0,127,0,1)); }, 0); // dal cc 0 ) ~synth.set(\tipoS,3,\tipoD,3);
Da notare che quando interagiamo sullo slider dedicato alla distanza sia con il mouse che con un controller esterno, automaticamente viene selezionato il tipo 0 (\tipoD o singolo valore).
Inviluppi
Se vogliamo utilizzare un qualsiasi tipo di inviluppo, basterà sostituire Select.kr() dei parametri pos e dist con un'istanza di inviluppo così come già fatto per il panning stereofonico e quadrifonico:
( // ------------------------------ SynthDef e Synth SynthDef(\circ_sig, {arg t_gate, lev = 1, orient=0.5, smt=0.2; var nchans=8,sig, env, pos,posbpf,posEnv, dist,distbpf,distEnv, pan; sig = SinOsc.ar(Rand(400,2000)); env = EnvGen.kr(Env.perc(0.01,0.19),Impulse.kr(5)); pos = Env.newClear(4); // Crea un inviluppo vuoto di 4 nodi posbpf= \pos.kr(pos.asArray); // Crea un controllo dell'inviluppo posEnv= EnvGen.kr(posbpf, t_gate); // Genera l'inviluppo dist = Env.newClear(4); distbpf= \dist.kr(dist.asArray); distEnv= EnvGen.kr(distbpf, t_gate); SendReply.kr(Impulse.kr(50), '/pos', [posEnv,distEnv]); pan = PanAz.ar(nchans, sig*env, posEnv, lev.lag(smt), distEnv.linexp(0,1,nchans,2), orient); Out.ar(0,pan) } ).add; {~synth = Synth(\circ_sig)}.defer(0.1); // ------------------------------ GUI w = Window.new("Pan", 210@210); w.alwaysOnTop; w.front; w.onClose_({~synth.free;w.free;d.free;h.free;p.free;u.free}); d = Slider2D.new(w, Rect(5, 5, 200, 200)); // Slider2D // ------------------------------ Operazioni di basso livello OSCdef.new(\vedi, {arg msg; {d.x_((msg[4] * cos(msg[3] -0.5* -pi)).linlin(-1,1,0,1)); d.y_((msg[4] * sin(msg[3] -0.5* -pi)).linlin(-1,1,0,1))}.defer(0) },'/pos', s.addr) ) // ------------------------------ Sequencing ( var pos,dist; h = 2; // Definiamo una durata p = [{rand2(1.0)}!4, {rand(0.5)}!3]; // generiamo i valori di pos u = [{rand2(1.0)}!4, {rand(0.5)}!3]; // generiamo i valori di dist pos = Env.new(p[0],p[1]).duration_(h); dist = Env.new(u[0],u[1]).duration_(h); [pos,dist].plot; // Plotter ~synth.set(\pos, pos, \dist, dist, \t_gate,1); // li inviamo al Synth )
Mouse
Se infine vogliamo utilizzare il mouse per controllare i movimenti, possiamo mappare l'asse delle x sulle rotazioni di pos definendo un numero di giri per range e l'asse delle y sulla distanza, riscalata come sempre tra il numero di canali e 2:
( // ------------------------------ SynthDef e Synth SynthDef(\pmouse, {var nchans=8,sig,env,pos,dist,pan; sig = SinOsc.ar; env = EnvGen.kr(Env.perc(0.01,0.19),Impulse.kr(5)); pos = MouseX.kr(-5,5); // numero di giri sull'asse x dist = MouseY.kr(0,1); SendReply.kr(Impulse.kr(50), '/pos', [pos,dist]); pan = PanAz.ar(nchans,sig*env,pos,1,dist.linexp(0,1,nchans,2)); Out.ar(0,pan) } ).add; {a = Synth(\pmouse)}.defer(0.1); // ------------------------------ GUI w = Window.new("Pan", 210@210); w.alwaysOnTop; w.front; w.onClose_({~synth.free;w.free;d.free}); d = Slider2D.new(w, Rect(5, 5, 200, 200)); // Slider2D // ------------------------------ Operazioni di basso livello OSCdef.new(\vedi, {arg msg; {d.x_((msg[4] * cos(msg[3] -0.5* -pi)).linlin(-1,1,0,1)); d.y_((msg[4] * sin(msg[3] -0.5* -pi)).linlin(-1,1,0,1))}.defer(0) },'/pos', s.addr) )
SplayAz
Infne possiamo utilizzare la SplayAz.kr() per posizionare un'Array di segnali in punti equidistanti all'interno di un sistema di diffusione multicanale. I suoi argomenti sono simili al "fratello minore" Splay e può essere utilizzato efficacemente per aprire o chiudere dinamicamente la disposizione di segnali nel sistema (suono diffuso/suono direzionale):
( SynthDef(\stereo, {arg sprd,ctr=0; var nchans=8,sig,env,width,lev=1,orient=0.5,pan; sig = [SinOsc.ar(800),Saw.ar(1000),WhiteNoise.ar]; env = EnvGen.kr(Env.perc(0.01,0.19),Impulse.kr(5)); width = MouseX.kr(2,6); sprd = MouseY.kr(0,1); pan = SplayAz.ar(nchans, // numero di canali del sistema sig*env, // Array di canali mono sprd, // spread 0 = tutti al centro lev, // ampiezza globale (0. / 1.) width, // su quanti canali i segnali sono distribuiti ctr, // shift dal centro orient, // orientazione true); // limiter... Out.ar(0,pan) } ).add; {~synth = Synth(\stereo)}.defer(0.1); )