Modalità
In SuperCollider possiamo generare interfacce grafiche (GUI) all'interno delle quali posizionere una o più icone come sliders, knobs, number boxes, etc. Queste interfacce possono essere utilizzate in due diversi modi:
come monitor visivi atti esclusivamente alla visualizzazione dello stato in cui si trovano i diversi parametri di controllo o di informazioni riguardanti segnali audio o sound files.
come oggetti con i quali interagire attraverso il mouse o la tastiera del computer per modificarne dinamicamente i parametri.
Se le utilizziamo solamente come monitor visivi è sempre meglio programmarle in modo che siano indipendenti e rimovibili in qualsiasi momento sia rispetto ai segnali audio che alle diverse modalità di controllo (sequencing, devices esterni, mouse, tastiera, etc.).
Window
Per creare una GUI la prima cosa dobbiamo fare consiste nel generare un'istanza della Classe Window. Questa sarà il contenitore all'interno del quale posizionare tutti gli oggetti grafici desiderati. I primi due argomenti sono un nome sotto forma di stringa e un'istanza di Rect() che specifica il posizionamento sullo schermo del computer e le dimensioni della finestra in pixels:
Rect.new(sinistra: 0, alto: 0, larghezza:0, altezza:0)
L'argomento sinistra:0 corrisponde al lato sinistro dello schermo mentre alto:0 al margine inferiore dello schermo facendo coincidere in questo caso l'angolo inferiore sinistro della finestra con quello del computer.
( w = Window.new("CiaoCiao", // Nome che compare sulla barra in alto Rect(0,0,300,110)); // Posizione e size in pixels )
Assegnamo sempre la finestra a una variabile sia per poter invocare su di essa alcuni metodi molto utili le cui funzioni si possono desumere facilmente dal codice sia per poter in seguito inviare e ricevere valori dall'Interprete in modo dinamico:
w.front; // la fa comparire w.alwaysOnTop_(true); // la fa sempre stare davanti alle altre finestre sullo schermo w.onClose_( {w.free} ); // questo metodo esegue la funzione che è suo argomento quando // chiudiamo la finestra dal pallino rosso
Possiamo inoltre modificare alcuni parametri grafici:
w.name_("urca"); // Cambiare il titolo w.background_(Color.red); // Vedere l'help file di Color w.alpha_(rand(1.0)); // Indice di trasparenza tra 0.0 e 1.0
Oggetti grafici di controllo
All'interno della Window possiamo inserire tutti i principali elementi grafici utili al controllo e alla visualizzazione di parametri (Client side):
( w = Window.new("CiaoCiao", Rect(0,0,300,110)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;n.free;k.free;b.free;p.free;d.free;t.free;z.free}); h = Slider.new(w, Rect(5,5,50,100)); // Crea uno slider x = 0 = sinistra / y = 0 = alto (in pixels) n = NumberBox.new(w, Rect(65,5, 50,20)); // Crea un number box k = Knob.new(w, Rect(65,30, 75,75)); // Crea un knob b = Button.new(w, Rect(120,5, 50,20)); // Crea un number box p = PopUpMenu.new(w, Rect(175,5, 100,20)); // Crea un pop up menu d = Slider2D.new(w, Rect(145,30, 75,75)); // Crea un interfaccia XY t = StaticText.new(w, Rect(225,30, 75,75)); // Crea un testo z = MultiSliderView.new(w,Rect(5,110, 290,75)); // Crea un multislider )
Anche in questo caso dobbiamo assegnare ogni elemento grafico a una variabile per poter successivamente inviare o recuperare valori da esso. I primi due argomenti sono comuni a tutti: la Window all'interno della quale vogliamo posizionare l'elemento e un'istanza di Rect().
Prestiamo attenzione che il valore 0 del punto all'interno della Window che definisce la posizione y non corrisponde al basso ma all'alto. Rect(0,0) definisce quindi l'angolo in alto a sinistra della Window.
Ogni oggetto grafico può sia inviare (nel caso di un'interazione con esso) che ricevere (nel caso di visualizzazione) dati dall'Interprete. Questi poi saranno eventualmente inviati al Server attraverso le consuete modalità:
Le sintassi di questi percorsi sono simili per tutti gli oggetti. Vediamole nel dettaglio
Recuperare valori dalle GUI
Interagendo con il mouse su un oggetto grafico possiamo modificarne i valori in uscita. Questi possono essere recuperati nell'Interprete invocando il metodo .action_({}). La funzione argomento di questo metodo sarà valutata ad ogni azione compiuta sullo slider mentre il valore in uscita sarà recuperato attraverso il mentodo .value specificato al suo interno:
( w = Window.new("Slider", Rect.new(0,0,265,210)); h = Slider.new(w, Rect(5,5,50,200)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free}); h.action_({h.value.postln}); // Recupera valore )
Possiamo assegnare a una variabile il valore recuperato per poterlo riutilizzare in qualsiasi modo all'interno del codice, tipicamente per inviarlo a un Synth:
( s.boot; s.scope(1); s.plotTree; SynthDef(\slider, {arg amp=0; var sig; sig = SinOsc.ar*amp.lag(0.2); Out.ar(0,sig) }).add; w = Window.new("Slider", Rect.new(0,0,60,210)); h = Slider.new(w, Rect(5,5,50,200)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;u.free}); h.action_({var val; // Assegna il valore a una variabile val = h.value; // per poterlo riutilizzare nel codice val.postln; u.set(\amp, val) // Invia il valore al Server }); {u = Synth(\slider)}.defer(0.2); )
Inviare valori alle GUI
Possiamo modificare dinamicamente lo stato dello slider inviando valori dall'Interprete all'oggetto con il metodo .value_(). Se inviamo un valore alla volta non c'è alcun problema, ma se utilizziamo tecniche di sequencing dobbiamo obbligatoriamente lavorare con AppClock oppure includere il comando in {n.value(val)}.defer. Il range di default è compreso tra 0.0 e 1.0 per quasi tutti gli elementi grafici.
h.value_(rand(1.0)); // Singolo valore ( r = Routine.new({ // Sequencing inf.do({ h.value_(rand(1.0)); 0.1.wait }) }).reset.play(AppClock); ) ( r = Routine.new({ inf.do({ {h.value_(rand(1.0))}.defer; 0.1.wait }) }).reset.play; ) r.stop;
Il metodo .value_() invia il valore dall'Interprete alla GUI ma non genera alcuna azione (come avviene quando interagiamo con il mouse). Se necessitiamo di entrambe le cose dobbiamo sostituirlo con .valueAction_()
h.action_({h.value.postln}); // Recupera il valore h.valueAction_(rand(1.0)); // Invia il valore ( // Sequencing r = Routine.new({ inf.do({ {h.value_(rand(1.0))}.defer; 0.1.wait }) }).reset.play; ) r.stop; // Provare ad interagire con il mouse sullo Slider...
In questo modo potremo scegliere indifferentemente la modalità di controllo dei parametri senza dover riscrivere il codice a seconda delle diverse situazioni.
Visualizzazione valori MIDI/OSC
Possiamo visualizzare i valori in ingresso generati dall'interazione con devices esterni che comunicano col computer attraverso il protocollo MIDI o l'OSC. In questo tipo di visualizzazione possiamo adottare due differenti strategie di programmazione:
- gestire un flusso di dati all'interno del quale l'oggetto grafico è parte integrante e non può essere eliminato
in alcun modo (per inviare i valori alla GUI utilizzeremo .valueAction_()).
( s.boot; s.meter(2); s.plotTree; MIDIClient.init; MIDIIn.disconnectAll; MIDIIn.connectAll; // ------------------------------ SynthDef e Synth SynthDef(\sig, {arg amp=0,freq=400; var sig,filt; sig = WhiteNoise.ar; filt = BPF.ar(sig, freq.lag(0.2), 0.001); Out.ar(0,filt*amp.lag(0.2)) }).add; {a = Synth(\sig)}.defer(0.3); // ------------------------------ GUI w = Window.new("Multi", Rect.new(0,0,60,220)); h = Slider.new(w, Rect(5,60,50,150)); i = Knob.new(w, Rect(5,5,50,50)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;i.free;a.free}); // ------------------------------ GUI --> Interprete --> Synth h.action_({a.set(\amp, h.value)}); i.action_({a.set(\freq,i.value.linlin(0,1,300,3000))}); // riscala i valori // ------------------------------ MIDI --> Interprete --> GUI MIDIdef.cc(\amp,{arg val; {h.valueAction_(val/127)}.defer // alla GUI (riscalato) },0); // riceve dal cc 0 MIDIdef.cc(\freq,{arg val; {i.valueAction_(val/127)}.defer // alla GUI (riscalato) },16); // riceve dal cc 16 // ------------------------------ OSC --> Interprete --> GUI OSCdef.new(\amp, {arg msg; {h.valueAction_(msg[1])}.defer}, '/1/fader1',recvPort:8000); OSCdef.new(\freq, {arg msg; {i.valueAction_(msg[1])}.defer}, '/1/rotary1',recvPort:8000) )
- rendere autonomo il controllo del Synth via MIDI/OSC dalla sua visualizzazione grafica in modo da poterla
eventualmente eliminare in qualsiasi momento. (in questo caso per inviare i valori alla GUI utilizzeremo .value_()).
( s.boot; s.meter(2); s.plotTree; MIDIClient.init; MIDIIn.disconnectAll; MIDIIn.connectAll; // ------------------------------ SynthDef e Synth SynthDef(\sig, {arg amp=0,freq=400; var sig,filt; sig = WhiteNoise.ar; filt = BPF.ar(sig, freq.lag(0.2), 0.001); Out.ar(0,filt*amp.lag(0.2)) }).add; {a = Synth(\sig)}.defer(0.3); // ------------------------------ GUI w = Window.new("Multi", Rect.new(0,0,60,220)); h = Slider.new(w, Rect(5,60,50,150)); i = Knob.new(w, Rect(5,5,50,50)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;i.free;a.free}); // ------------------------------ GUI --> Interprete --> Synth h.action_({a.set(\amp, h.value)}); i.action_({a.set(\freq,i.value.linlin(0,1,300,3000))}); // ------------------------------ MIDI --> Interprete --> Synth // MIDI --> Interprete --> GUI MIDIdef.cc(\amp,{arg val; a.set(\amp,val/127); // al Synth (riscalato) {h.value_(val/127)}.defer // alla GUI (riscalato) },0); // riceve dal cc 0 MIDIdef.cc(\freq,{arg val; a.set(\freq,val.linexp(0,127,300,3000)); // al Synth (riscalato) {i.value_(val/127)}.defer // alla GUI (riscalato) },16); // riceve dal cc 16 // ------------------------------ OSC --> Interprete --> Synth // OSC --> Interprete --> GUI OSCdef.new(\amp, {arg msg; {h.value_(msg[1])}.defer; a.set(\amp,msg[1])}, '/1/fader1',recvPort:8000); OSCdef.new(\freq, {arg msg; {i.value_(msg[1])}.defer; a.set(\freq,msg[1].linexp(0,1,300,3000))}, '/1/rotary1',recvPort:8000) )
Visualizzazione elementi di controllo
Slider.new()
Questo oggetto raffigura un classico slider (o fader) e può essere sia verticale che orizzontale. In entrambi i casi i valori sono compresi in un range di default che va da 0.0 a 1.0.
( w = Window.new("Slider", Rect.new(0,0,265,210)); h = Slider.new(w, Rect(5,5,50,200)); v = Slider.new(w, Rect(60,155,200,50)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.orientation_(\horizontal); // Direzione leva h.orientation_(\vertical); h.knobColor_(Color.rand); // Colore leva h.background_(Color.rand); // Colore sfondo
Controllo - abbiamo a disposizione tre modalità:
- Interagire con il mouse recuperando i valori nell'Interprete.
h.action_({h.value.postln}); // Recupera il valore
- Interagire utilizzando abbreviazioni da tastiera recuperando i valori nell'Interprete come sopra.
- Inviare e recuperare i valori dall'Interprete come illustrato precedentemente.
h.action_({h.value.postln}); // Recupera il valore h.value_(rand(1.0)); // Invia il valore (senza output) h.valueAction_(rand(1.0)); // Invia il valore ed esegue l'azione
- Visualizzare valori recuperati dal Server.
( s.boot; s.meter(2); s.plotTree; // ------------------------------ SynthDef SynthDef(\sig, {var sig; sig = SinOsc.ar(2).unipolar; // tra 0 e 1 // ------------------------------ Server --> Interprete SendReply.kr(Impulse.kr(50), // rata di campionamento '/pos', // indirizzo o nome sig); // segnale da campionare // Out.ar(0,sig) }).add; {a = Synth(\sig)}.defer(0.1); // ------------------------------ dal Server OSCFunc.new({arg msg; var val; val = msg[3]; // recupera il valore {h.value_(val)}.defer; // invia allo Slider }, '/pos', // indirizzo o nome s.addr); // eventuale NetAddr) // ------------------------------ GUI w = Window.new("Slider", Rect.new(0,0,60,210)); h = Slider.new(w, Rect(5,5,50,200)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;a.free}); )
- Interagire con il mouse recuperando i valori nell'Interprete.
Esempio audio
( s.boot; s.meter(2,2); s.plotTree; s.freeAllBuffers; f = Buffer.alloc(s,1024,1); // Crea un Buffer w = Window.new("Slider", Rect(0,0,470,210)); // Crea una Window h = Slider.new(w, Rect(5,5,50,200)); // Crea uno Slider c = ScopeView.new(w, Rect(60,5,400,200)); // Crea uno ScopeWiew c.bufnum = f.bufnum; // Assegna il Buffer c.server_(s); // Assegna il Server c.start; // Fa partire la visualizzazione w.front; w.alwaysOnTop_(true); w.onClose_({f.free;w.free;h.free;c.free;a.free}); h.action_({a.set(\amp,h.value)}); // Recupera il valore e lo invia al Synth SynthDef(\sig, {arg amp=0; var sig, toScope; sig = BrownNoise.ar*amp.lag(0.02); toScope = ScopeOut2.ar(sig, f); // Scrive nel Buffer Out.ar(0,sig) // Ouput audio }).add; {a = Synth(\sig)}.defer(0.3); ) h.valueAction_(rand(1.0)); // Invia il valore ed esegue l'azione
Per ulteriori info ed esempi possiamo consultare l'Help file.
Knob.new()
Questo oggetto raffigura un classico knob. I valori sono compresi in un range di default che va da 0.0 a 1.0.
( w = Window.new("Knob", Rect.new(0,0,120,120)); h = Knob.new(w, Rect(5,5,100,100)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.centered_(true); // Posizione leva h.centered_(false); h.color_([Color.red,Color.green,Color.yellow,Color.blue]); // [sfondo, valore, deviazione,indicatore]
Controllo - abbiamo a disposizione tre modalità:
- Interagire con il mouse recuperando i valori nell'Interprete. In questo
caso possiamo impostare l'interazione con il mouse in tre modi differenti:
h.mode_(\round); // segue l'andamento circolare h.mode_(\horiz); // segue l'andamento orizzontale in modo lineare h.mode_(\vert); // segue l'andamento verticale in modo lineare h.action_({h.value.postln}); // Recupera il valore
- Interagire utilizzando abbreviazioni da tastiera recuperando i valori nell'Interprete come sopra.
- Inviare e recuperare i valori dall'Interprete come illustrato precedentemente.
h.action_({h.value.postln}); // Recupera il valore h.value_(rand(1.0)); // Invia il valore (senza output) h.valueAction_(rand(1.0)); // Invia il valore ed esegue l'azione
- Visualizzare valori recuperati dal Server.
( s.boot; s.meter(2); s.plotTree; // ------------------------------ SynthDef SynthDef(\sig, {var sig; sig = LFNoise2.ar(2).unipolar; // tra 0 e 1 // ------------------------------ Server --> Interprete SendReply.kr(Impulse.kr(50), // rata di campionamento '/pos', // indirizzo o nome sig); // segnale da campionare // Out.ar(0,sig) }).add; {a = Synth(\sig)}.defer(0.1); // ------------------------------ dal Server OSCFunc.new({arg msg; var val; val = msg[3]; // recupera il valore {h.value_(val)}.defer; // invia allo Slider }, '/pos', // indirizzo o nome s.addr); // eventuale NetAddr) // ------------------------------ GUI w = Window.new("Knob", Rect.new(0,0,120,120)); h = Knob.new(w, Rect(5,5,100,100)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;a.free}); )
- Interagire con il mouse recuperando i valori nell'Interprete. In questo
caso possiamo impostare l'interazione con il mouse in tre modi differenti:
Esempio audio
( s.boot; s.meter(2,2); s.plotTree; MIDIClient.init; MIDIIn.disconnectAll; MIDIIn.connectAll; w = Window.new("Slider", Rect(0,0,470,265)); // Crea una Window u = Knob.new(w, Rect(5,5, 50,50)); // Crea un Knob h = Slider.new(w, Rect(5,60,50,200)); // Crea uno Slider c = FreqScopeView.new(w, Rect(60,5,400,255));// Crea uno FreqScopeWiew c.active_(true); // Lo attiva ("false" lo disattiva) c.inBus_(0); // Bus del segnale da analizzare (mono) c.freqMode_(1); // 0 = lineare 1 = logaritmico c.dbRange_(90); // Cambia range visualizzazione w.front; w.alwaysOnTop_(true); w.onClose_({w.free;u.free;h.free;c.free;a.free;c.kill}); h.action_({a.set(\amp, h.value)}); // Ampiezza u.action_({a.set(\nharm, (u.value*19+1).postln)}); // Numero armonici MIDIdef.cc(\amp,{arg val; {h.valueAction_(val/127)}.defer},0); MIDIdef.cc(\freq,{arg val;{u.valueAction_(val/127)}.defer},16); SynthDef(\sig, {arg amp=0,nharm=1; Out.ar(0,Blip.ar(400,nharm)*amp.lag(0.02))}).add; {a = Synth(\sig)}.defer(0.3); )
Per ulteriori info ed esempi possiamo consultare l'Help file.
NumberBox.new()
Questo oggetto visualizza valori numerici sia interi (int) che decimali (float).
( w = Window.new("Num", Rect.new(0,0,110,30)); h = NumberBox.new(w, Rect(5,5,100,20)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.align_(\center); // Valore al centro h.align_(\left); // Valore a sinistra h.font_(Font("Times", 20)); // Cambia Font e size h.decimals_(7); // Numero di decimali visualizzato h.stringColor_(Color.rand); // Colore dei numeri h.background_(Color.rand); // Colore sfondo
Controllo - abbiamo a disposizione quattro modalità:
- Interagire con il mouse recuperando i valori nell'Interprete.
h.action_({h.value.postln}); // Recupera il valore
A volte può essere utile clippare i valori minimo e massimo:
h.clipLo_(0); h.clipHi_(10);
Possiamo utilizzare i tasti modificatori:
- Shift: incrementa di multipli di 100
- Ctrl: incrementa di multipli di 10
- Alt: incrementa di multipli di 0.1
- Cliccare sul numero e digitare un nuovo numero sulla tastiera per poi inviarlo col tasto invio.
- Interagire utilizzando abbreviazioni da tastiera recuperando i valori nell'Interprete come sopra.
- Inviare e recuperare i valori dall'Interprete come illustrato precedentemente.
h.action_({h.value.postln}); // Recupera il valore h.value_(rand(1.0)); // Invia il valore (senza output) h.valueAction_(rand(1.0)); // Invia il valore ed esegue l'azione
- Visualizzare valori recuperati dal Server.
( s.boot; s.meter(2); s.plotTree; // ------------------------------ SynthDef SynthDef(\sig, {var sig; sig = LFNoise0.ar(2).unipolar; // tra 0 e 1 // ------------------------------ Server --> Interprete SendReply.kr(Impulse.kr(50), // rata di campionamento '/pos', // indirizzo o nome sig); // segnale da campionare // Out.ar(0,sig) }).add; {a = Synth(\sig)}.defer(0.1); // ------------------------------ dal Server OSCFunc.new({arg msg; var val; val = msg[3]; // recupera il valore {h.value_(val)}.defer; // invia allo Slider }, '/pos', // indirizzo o nome s.addr); // eventuale NetAddr) // ------------------------------ GUI w = Window.new("Num", Rect.new(0,0,110,30)); h = NumberBox.new(w, Rect(5,5,100,20)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;a.free}); )
- Interagire con il mouse recuperando i valori nell'Interprete.
Per ulteriori info ed esempi possiamo consultare l'Help file.
Button.new()
Questo oggetto visualizza un pulsante che può contenere del testo e avere più "stati". Ogni "stato" può contenere un testo differente e avere colori differenti. Per impostarli dobbiamo invocare il metodo .states_([[0],[1],[2]]) ed utilizzare la sintassi illustrata.
( w = Window.new("Num", Rect.new(0,0,160,60)); h = Button.new(w, Rect(5,5,150,50)); h.states_([["stato 0",Color.black, Color.red], ["stato 1",Color.white, Color.black], ["stato 2",Color.red, Color.white]] ); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.string_("ciao ciao"); // Modifica il testo nello stato visualizzato h.font_(Font("Times", 30)); // Cambia Font e size
Controllo - per navigare tra gli stati abbiamo a disposizione tre modalità:
- Cliccare con il mouse sul bottone per poi recuperare i valori (indici degli stati) nell'Interprete.
h.action_({h.value.postln}); // Recupera il valore
- Interagire utilizzando abbreviazioni da tastiera per poi recuperare i valori nell'Interprete come sopra.
- Inviare e recuperare i valori dall'Interprete come illustrato precedentemente (i valori saranno gli indici).
h.action_({h.value.postln}); // Recupera il valore h.value_(rand(3)); // Invia il valore (senza output) h.valueAction_(rand(3)); // Invia il valore ed esegue l'azione
- Cliccare con il mouse sul bottone per poi recuperare i valori (indici degli stati) nell'Interprete.
Esempio audio
( s.boot; s.meter(2,2); s.plotTree; w = Window.new("Noise", Rect.new(0,0,565,265)); h = Button.new(w, Rect(5,5,150,50)); i = Slider.new(w, Rect(5,60,150,200)); c = FreqScopeView.new(w, Rect(160,5,400,255)); // Crea uno FreqScopeWiew c.active_(true); // Lo attiva ("false" lo disattiva) c.inBus_(0); // Bus del segnale da analizzare (mono) c.freqMode_(1); // 0 = lineare 1 = logaritmico c.dbRange_(90); // Cambia range visualizzazione h.states_([["Rumore Bianco",Color.black, Color.white], ["Rumore Rosa", Color.white, Color.magenta], ["Rumore Marrone",Color.red, Color.cyan], ["Rumore Clip",Color.white, Color.black]] ); h.action_({a.set(\in, h.value)}); i.action_({a.set(\amp,i.value)}); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;i.free;a.free;c.kill}); SynthDef(\sig, {arg in=0,amp=0; Out.ar(0, Select.ar(in, [WhiteNoise.ar, PinkNoise.ar, BrownNoise.ar, ClipNoise.ar] )*amp.lag(0.02))} ).add; {a = Synth(\sig)}.defer(0.3); )
Per ulteriori info ed esempi possiamo consultare l'Help file.
PopUpMenu.new()
Questo oggetto visualizza un menù a comparsa che contiene diversi items testuali. Cliccando sull'oggetto compare un menu dal quale li possiamo selezionare singolarmente. Per impostarli dobbiamo invocare il metodo .items_(["","",""]) ed utilizzare la sintassi illustrata.
( w = Window.new("Menu", Rect.new(0,0,160,60)); h = PopUpMenu.new(w, Rect(5,5,150,50)); h.items_(["ciao",\miao,"uao",\bau,\cau]); // Array di simboli o stringhe h.clear; // Rimuove tutti gli items w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.font_(Font("Times", 30)); // Cambia Font e size h.stringColor_(Color.rand); // Colore del testo h.background_(Color.rand); // Colore sfondo
Controllo - per navigare tra gli stati abbiamo a disposizione tre modalità:
- Cliccare con il mouse sul menu per poi recuperare il valore dell'indice della selezione nell'Interprete (il primo in alto
corrisponde allo 0).
h.action_({h.value.postln}); // Recupera il valore
- Interagire utilizzando abbreviazioni da tastiera per poi recuperare i valori nell'Interprete come sopra.
- barra spazio: fa apparire til menù.
- frecce su e giù: naviga tra gli items.
- tasto invio: trigger della selezione.
Inviare e recuperare i valori dall'Interprete come illustrato precedentemente (i valori saranno gli indici).
h.action_({h.value.postln}); // Recupera il valore h.value_(rand(4)); // Invia il valore (senza output) h.valueAction_(rand(4)); // Invia il valore ed esegue l'azione
- Cliccare con il mouse sul menu per poi recuperare il valore dell'indice della selezione nell'Interprete (il primo in alto
corrisponde allo 0).
Esempio audio
( s.boot; s.meter(2,2); s.plotTree; w = Window.new("Noise", Rect.new(0,0,565,265)); h = PopUpMenu.new(w, Rect(5,5,150,50)); i = Slider.new(w, Rect(5,60,150,200)); c = FreqScopeView.new(w, Rect(160,5,400,255)); // Crea uno FreqScopeWiew c.active_(true); // Lo attiva ("false" lo disattiva) c.inBus_(0); // Bus del segnale da analizzare (mono) c.freqMode_(1); // 0 = lineare 1 = logaritmico c.dbRange_(90); // Cambia range visualizzazione h.items_(["Sinusoide","Onda quadra","Dente di sega","Additiva"]); h.action_({a.set(\in, h.value)}); i.action_({a.set(\amp,i.value)}); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;i.free;a.free;c.kill}); SynthDef(\sig, {arg in=0,amp=0; Out.ar(0, Select.ar(in, [SinOsc.ar, Pulse.ar, Saw.ar, Klang.ar(`[ 440*(1..12), nil, nil ])*0.1] )*amp.lag(0.02))} ).add; {a = Synth(\sig)}.defer(0.3); )
Per ulteriori info ed esempi possiamo consultare l'Help file.
StaticText.new()
Questo oggetto visualizza un testo all'interno di una cornice posizionata nella Window. Per scrivere il testo dobbiamo invocare il metodo .string_("...") ed utilizzare la sintassi illustrata.
( w = Window.new("Text", Rect.new(0,0,500,300)); h = StaticText.new(w, Rect(20,20,460,260)); h.string_(" questo è cio che scrivo "); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.font_(Font("Times", 30)); // Cambia Font e size h.stringColor_(Color.rand); // Colore dei numeri h.background_(Color.rand); // Colore sfondo h.align_(\center); // Allineamento h.align_(\left); h.align_(\right);
Controllo - eventualmente è possibile solo una visualizzazione dinamica del testo senza alcun tipo di interazione.
( t = "abcdefg"; w = Window.new("Text", Rect.new(0,0,500,80)); h = StaticText.new(w, Rect(20,20,460,40)); h.background_(Color.black); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;r.free}); r = Routine.new({inf.do({h.string_(t.foldAt(rand(t.size))); h.font_(Font("Times", rand(50))); h.stringColor_(Color.rand); h.align_([\center,\left,\right].choose); 0.2.wait}) }).reset.play(AppClock); ) r.stop;
Slider2D.new()
Questo oggetto visualizza un cursore posizionato all'interno di un piano cartesiano. Sull'asse delle ascisse (x) il valore 0.0 = sinistra mentre 1.0 = destra mentre sull'asse delle ordinate (y) 0.0 = basso e 1.0 = alto.
( w = Window.new("Slider 2D", Rect.new(0,0,300,300)); h = Slider2D.new(w, Rect(5,5,290,290)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;r.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.knobColor_(Color.rand); // Colore cursore h.background_(Color.rand); // Colore sfondo
Controllo - per navigare tra gli stati abbiamo a disposizione tre modalità:
- Cliccare con il mouse sul bottone per poi recuperare i valori nell'Interprete. In questo caso sono due e non si
utilizza il metodo .value ma .x e .y.
h.action_({("x: "++h.x++" y: "++h.y).postln}); // Recupera il valore
- Interagire utilizzando abbreviazioni da tastiera per poi recuperare i valori nell'Interprete come sopra.
- Inviare e recuperare i valori dall'Interprete come illustrato precedentemente (i metodi sono raddoppiati).
h.action_({h.value.postln}); // Recupera il valore h.x_(rand(1.0)); // Invia il valore (senza output) h.y_(rand(1.0)); h.setXY(rand(1.0),rand(1.0)); h.activex_(rand(1.0)); // Invia il valore ed esegue l'azione h.activey_(rand(1.0)); h.setXYActive(rand(1.0),rand(1.0));
- Visualizzare valori recuperati dal Server.
( s.boot; s.meter(2); s.plotTree; // ------------------------------ SynthDef SynthDef(\mouseval, {var x,y,click,sig; x = MouseX.kr(0,1); // Panpot... y = MouseY.kr(0.0001,1,1); // Ampiezza...no 0... click = MouseButton.kr.round; // Note on/off // ------------------------------ Server --> Interprete SendReply.kr(Impulse.kr(50), // rata di campionamento '/pos', // indirizzo o nome [x, y, click]); // segnali da campionare sig = Pan2.ar(SinOsc.ar*y*click,x*2-1); // Audio Out.ar(0,sig) } ).add; {a = Synth(\mouseval)}.defer(0.1); // ------------------------------ dal Server OSCFunc.new({arg msg; {h.x_(msg[3]); // assegna i singoli valori h.y_(msg[4]); if(msg[5] == 1, {h.knobColor_(Color.red)}, {h.knobColor_(Color.green)}) }.defer; // AppCLock per messaggi dinamici alla GUI }, '/pos', // indirizzo o nome s.addr); // eventuale NetAddr // ------------------------------ GUI w = Window.new("mouse", Rect(0,0,210,210)); h = Slider2D.new(w, Rect(5,5,200,200)); w.front; w.alwaysOnTop_(true); w.onClose_({a.free;h.free;w.free}); )
- Cliccare con il mouse sul bottone per poi recuperare i valori nell'Interprete. In questo caso sono due e non si
utilizza il metodo .value ma .x e .y.
Esempio audio
( s.options.numOutputBusChannels_(4); s.reboot; s.scope(4); s.meter(4,4); s.plotTree; ) ( w = Window.new("Pan", Rect.new(0,0,300,335)); h = Slider2D.new(w, Rect(5,40,290,290)); i = PopUpMenu.new(w, Rect(5,5,140,30)); l = PopUpMenu.new(w, Rect(155,5,140,30)); i.items_(["x: Triangolare","x: Sinusoide","x: Noise0","x: Noise1","x: Noise2"]); l.items_(["y: Triangolare","y: Sinusoide","y: Noise0","y: Noise1","y: Noise2"]); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;a.free;i.free;l.free}); i.action_({a.set(\tipoX,i.value)}); l.action_({a.set(\tipoY,l.value)}); OSCFunc.new({arg msg; {h.x_(msg[3].linlin(-1,1,0,1)); // solo all'interfaccia h.y_(msg[4].linlin(-1,1,0,1))}.defer(0); },'/pos', s.addr); SynthDef(\quad_sig, {arg tipoX=1,velX=0.5,sprdX=1,initposX= 0, tipoY=1,velY=0.5,sprdY=1,initposY= 0.5pi, smt=0.2; var sig,ksigs,x,y,pan; sig = SinOsc.ar(Rand(400,2000)); x = Select.kr(tipoX,[LFTri.kr(velX,initposX,sprdX), SinOsc.kr(velX,initposX,sprdX), LFNoise0.kr(velX,sprdX), LFNoise1.kr(velX,sprdX), LFNoise2.kr(velX,sprdX), ]); y = Select.kr(tipoY,[LFTri.kr(velY,initposY,sprdY), SinOsc.kr(velY,initposY,sprdY), LFNoise0.kr(velY,sprdY), LFNoise1.kr(velY,sprdY), LFNoise2.kr(velY,sprdY), ]); SendReply.kr(Impulse.kr(50), '/pos', [x,y]); // Invia Slider2D pan = Pan4.ar(sig,x,y); Out.ar(0,pan) } ).add; {a = Synth(\quad_sig)}.defer(0.1); )
Per ulteriori info ed esempi possiamo consultare l'Help file.
MultiSliderView.new()
Questo oggetto visualizza un insieme di Sliders. Il range di ogni Slider sull'asse delle ordinate (y) è compreso tra 0.0 e 1.0.
A differenza degli altri oggetti grafici in questo caso dobbiamo assegnare la larghezza a una variabile per poter calcolare in modo dinamico la larghezza del singolo slider in base al numero di slider che desideriamo visualizzare. Per visualizzare gli sliders dobbiamo invocare il metodo .value_([]) ed utilizzare la sintassi illustrata.
( ~larg = 300; // Larghezza in Pixels w = Window.new("Multi", Rect.new(0,0,~larg + 10,110)); h = MultiSliderView.new(w, Rect(5,5,~larg,100)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;n.free;v.free}); ) ( n = 20; // Numero di sliders h.elasticMode_(1); // 1 = distribuisce sull'asse delle ascisse h.indexThumbSize_(~larg / n - 2); // Calcola automaticamente la larghezza del singolo slider - 2 pixels v = Array.rand(n,0.0,1.0); // genera un Array di n valori h.value_(v); // Invia i valori alla GUI )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche invocando i seguenti metodi:
h.showIndex_(true); // Visualizza il singolo slider richiamandone l'indice h.index_(rand(n)); h.drawLines_(true); // Visualizza delle linee tra gli slider h.drawLines_(false); h.isFilled_(true); // Colonne h.isFilled_(false); // Punti h.indexIsHorizontal_(true); // Sliders verticali h.indexIsHorizontal_(false); // Sliders orizzontali h.strokeColor_(Color.red); // Colore contorno h.fillColor_(Color.rand); // Colore interno
Controllo - per navigare tra gli stati abbiamo a disposizione tre modalità:
- Cliccare con il mouse sugli Sliders per poi recuperare i valori nell'Interprete sotto forma di Array.
h.action_({h.value.postln}); // Recupera i valori come Array
- Interagire utilizzando abbreviazioni da tastiera per poi recuperare i valori nell'Interprete come sopra.
- Inviare e recuperare i valori dall'Interprete come illustrato precedentemente (i metodi sono raddoppiati).
h.action_({h.value.postln}); // Recupera i valori h.value_(Array.rand(n,0.0,1.0)); // Invia il valore (senza output) h.valueAction_(Array.interpolation(n,0.0,1.0)); // Invia il valore ed esegue l'azione
- Visualizzare valori recuperati dal Server.
( s.boot; s.meter(2); s.plotTree; // ------------------------------ SynthDef SynthDef(\sig, {var sig; sig = {LFNoise2.ar(2).unipolar}!6; // tra 0 e 1 // ------------------------------ Server --> Interprete SendReply.kr(Impulse.kr(50), // rata di campionamento '/pos', // indirizzo o nome [sig[0],sig[1],sig[2],sig[3],sig[4],sig[5]]; // segnali da campionare ) // Out.ar(0,sig) }).add; {a = Synth(\sig)}.defer(0.1); // ------------------------------ dal Server OSCFunc.new({arg msg; var val; val = msg[3]; // recupera il valore {h.value_(msg.drop(3))}.defer; // invia allo Slider }, '/pos', // indirizzo o nome s.addr); // eventuale NetAddr) // ------------------------------ GUI ~larg = 300; // Larghezza in Pixels w = Window.new("Multi", Rect.new(0,0,~larg + 10,110)); h = MultiSliderView.new(w, Rect(5,5,~larg,100)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;n.free;~larg.free;a.free}); n = 6; // Numero di sliders h.elasticMode_(1); // 1 = distribuisce sull'asse delle ascisse h.indexThumbSize_(~larg / n - 2) )
- Cliccare con il mouse sugli Sliders per poi recuperare i valori nell'Interprete sotto forma di Array.
Esempio audio
( s.boot; s.meter(2,2); s.plotTree; w = Window.new("Addi", Rect.new(0,0,630,200)); h = Slider.new(w, Rect(5,5,50,190)); i = Slider.new(w, Rect(60,5,50,190)); l = Slider.new(w, Rect(115,5,50,190)); m = Slider.new(w, Rect(170,5,50,190)); n = FreqScopeView.new(w, Rect(225,5,400,190)); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;i.free;l.free;m.free;n.kill;a.free}); n.active_(true); n.inBus_(0); n.freqMode_(1); n.dbRange_(90); h.action_({a.set(\amp1,h.value)}); i.action_({a.set(\amp2,i.value)}); l.action_({a.set(\amp3,l.value)}); m.action_({a.set(\amp4,m.value)}); SynthDef(\addi, {arg amp1=0,amp2=0,amp3=0,amp4=0; Out.ar(0,Mix(SinOsc.ar(440*(1..4),0,[amp1,amp2,amp3,amp4].lag(0.02))*0.25)) } ).add; {a = Synth(\addi)}.defer(0.1); )
Per ulteriori info ed esempi possiamo consultare l'Help file.
FlowLayout
Se l'interfaccia che vogliamo costruire non è complessa oppure se deve contenere molti diplicati dello stesso oggetto possiamo utilizzare la Classe FlowLayout.new() o la sua abbreviazione sintattica illustrata nel codice seguente.
// Oggetti diversi ( w = Window.new("Multi", Rect.new(0,0,220,250)); w.addFlowLayout( 5@20, // Margini dalla Window x@y 5@5 ); // Spazio tra gli oggetti x@y h = Slider.new(w, 50@100); // Specificare solo le dimensioni n = Slider.new(w, 50@100); d = Slider2D.new(w, 100@100); p = PopUpMenu.new(w, 210@20); z = MultiSliderView.new(w, 210@75); w.front; w.alwaysOnTop_(true); w.onClose_({w.free;h.free;n.free;d.free;p.free;z.free}); ) //========================================================= // Copie multiple dello stesso oggetto ( w = Window.new("Multi", Rect.new(0,0,220,220)); w.addFlowLayout( 20@20, // Margini dalla Window x@y 5@5 ); // Distanze tra gli oggetti x@y 16.do{Slider2D(w.view,40@40).background_(Color.rand)}; w.front; w.alwaysOnTop_(true); w.onClose_({w.free}); )
Per ulteriori info ed esempi possiamo consultare l'Help file.
Visualizzazione segnali
Abbiamo anche la possibilità di generare interfacce grafiche per visualizzare e in alcuni casi interagire con segnali audio sia memorizzati su supporto che generati o elaborati in tempo reale.
SoundFileView.new()
Questo oggetto visualizza un sound file memorizzato su supporto.
Quando utilizziamo questa Classe non visualizziamo il contenuto di un Buffer ma un Sound file che possiamo eventualmente caricare in un buffer.
( f = SoundFile.openRead(Platform.resourceDir +/+ "sounds/a11wlk01.wav"); // Crea un'istanza di Soundfile w = Window.new("SoundFileView", Rect.new(0,0,550,210)); a = SoundFileView.new(w, Rect(5,5, 540, 200)); a.soundfile_(f); // Assegna il Sounfile alla visualizzazione a.read(0, f.numFrames); // Legge il file dal frame 0 all'ultimo, // N.B. Per file molto lunghi è meglio usare: a.readWithTask; a.refresh; // Ripulisce e visualizza w.front; w.alwaysOnTop_(true); w.onClose_({w.free;a.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche:
- Zoom: per ingrandire o rimpicciolire il contenuto: shift + tasto destro + mouse-su/giù
- Cursore: possiamo visualizzare un cursore come linea verticale, modificarne il colore e recuperare la posizione in
frames:
a.timeCursorOn_(true); // Visualizza il cursore a.timeCursorColor_(Color.red); // Colore del cursore
Griglia: quando visualizziamo il Sound file comapre una griglia di default (l'alternanza di viola e nero). Possiamo cancellarla o modificarne i parametri grafici a piacere:
a.gridOn_(false); // Elimina la Griglia a.gridOn_(true); // Crea una Griglia a.gridResolution_(0.1); // Risoluzione della griglia in secondi a.gridColor_(Color.gray); // Colore griglia
Selezioni: possiamo selezionare fino a 64 porzioni di file. Ogni selezione (porzione) è indicizzata e la selezione di default è la numero 0:
a.setSelection(0, [0, 10000]); // Setta la selezione (id, [inizio, durata]) in frames a.setSelectionColor(0, Color.blue); // Setta il colore della selezione (id, colore)
Notiamo come la posizione del cursore e quella della selezione siano indipendenti:
Possiamo creare altre selezioni utilizzando la stessa sintassi:
a.setSelection(1, [5000, 10000]); // Selezione 2 a.setSelectionColor(1, Color.white); a.setSelection(2, [90000, 8000]); // Selezione 3 a.setSelectionColor(2, Color.green);
Controllo - abbiamo a disposizione due modalità:
Interagire con il mouse recuperando i valori nell'Interprete.
- Cursore: cliccando (senza trascinare) sull'interfaccia posizioniamo il cursore in posizioni diverse. Per
recuperare nell'Interprete il valore in frames
( a.mouseDownAction_({var cpos; // anche .mouseUpAction... cpos = a.timeCursorPosition; // posizione del cursore ("posizione: "++cpos).postln }) )
- Selezioni: prima dobbiamo stablire quale è la selezione corrente ed eventualmente modificarla:
a.currentSelection; // Riporta qual'è la selezione attuale a.currentSelection_(1); // Cambia la selezione corrente
Dopodichè possiamo cliccare e trascinare con il mouse sull'interfaccia per compiere la selezione. Notiamo che la posizione del cursore coincide sempre con l'inizio della selezione. Per recuperare nell'Interprete i diversi valori in frames:
( a.mouseUpAction = {var sel, start, cpos, size, end; sel = a.selections[a.currentSelection]; // [start,size] selezione corrrente. start = sel[0]; cpos = a.timeCursorPosition; // posizione del cursore size = sel[1]; // Per permettere l'eventuale lettura al contrario del file dobbiamo aggiungere alcune operazioni: if(cpos==start, // Se posizione corrente è = 'start' {end = start+size; start = start}, // Calcola 'end' altrimenti {end = start; start = cpos}); // inverti 'start' e 'end' [start, end, size].postln } )
Come possiamo osservare i parametri che servono per il controllo di tutte le tecniche di playback sono: inizio, fine e durata della porzione di file siano essi espressi in samples, frames o secondi.
- Cursore: cliccando (senza trascinare) sull'interfaccia posizioniamo il cursore in posizioni diverse. Per
recuperare nell'Interprete il valore in frames
Inviare e recuperare i valori dall'Interprete.
- Cursore: modificare dinamicamente la posizione:
a.timeCursorPosition_(23456); // Setta la posizione dall'Interprete in frames a.timeCursorPosition; // Riporta la posizione del cursore in frames
- Selezioni: modificare dinamicamente inizio e durata e recuperarne i valori:
a.setSelectionStart(0, 12345); // Setta l'inizio della selezione in frames (id, posizione) a.setSelectionSize(0, 10000); // Setta il size della selezione (id, size) a.setSelectionStart(1, 34567); a.setSelectionSize(1, -30000); // al contrario... a.selections.size; // Max 64 selezioni a.selections; // Riporta un array 2D con i valori [start,size] di tutte le selezioni a.selections[0]; // [start, size] della selezione 0 a.selections[1]; // [start, size] della selezione 1 // etc. a.selections[a.currentSelection]; // [start, size] della selezione corrrente.
- Cursore: modificare dinamicamente la posizione:
Esempio audio
( s.boot; s.meter(2,2); s.plotTree; Buffer.freeAll; w = Window.new("SoundFileView", 550@210).front; w.onClose_({w.free;a.free}); f = SoundFileView.new(w, Rect(5,5, 540, 200)); h = SoundFile.new; h.openRead(Platform.resourceDir +/+ "sounds/a11wlk01.wav"); f.soundfile_(h) .readWithTask(0, h.numFrames) // a.read; .refresh .timeCursorOn_(true) .timeCursorColor_(Color.white) .setSelectionColor(0, Color.blue) .gridColor_(Color.gray) .gridOn_(true) .gridResolution_(0.01) .mouseUpAction_( {var sel, cpos, start, size, end, rate; sel = f.selections[f.currentSelection]; cpos = f.timeCursorPosition; start = sel[0]; size = sel[1]; if(cpos==start, {end = start+size; start = start; rate = 1}, {end = start; start = cpos;rate = -1}); [start, end, size].postln; a.set(\start, start, \end, end, \rate, rate) }); ~buffer = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); // carica l'audio file in un buffer a = {arg start=0,end=0,rate=1; BufRd.ar(1,~buffer, Phasor.ar(rate:rate,start:start,end:end))}.play )
EnvelopeView.new()
Questo oggetto visualizza dinamicamente gli inviluppi. Se visualizziamo inviluppi con il metodo .plot() SuperCollider crea automaticamente una nuova Window per ogni inviluppo (o per ogni Array di inviluppi) mentre se utilizziamo questa Classe creiamo una sola Window alla quale inviare i parametri dell'inviluppo.
( w = Window.new("EnvelopeView", 500@400); e = EnvelopeView.new(w, Rect(10,10,480,380)); h = Env.new([0,1,0.4,0],[0.2,0.5,1],[3,-2.3,1.5]); ~dur = h.duration; // recupera la durata dell'inviluppo (può servire) e.setEnv(h); w.front; w.alwaysOnTop_(true); w.onClose_({e.free;w.free;h.free;r.stop}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche:
e.style_(\dots); // Nodi punti e.style_(\rects); // Nodi rettangoli e.drawLines_(true); // Visualizza le linee tra i nodi e.drawRects_(true); // Visualizza i punti dei nodi e.thumbSize_(10); // Cambia il size dei nodi e.strokeColor_(Color.blue); // Colore delle connessioni e.fillColor_(Color.green); // Colore dei nodi e.selectionColor_(Color.red); // Colore della selezione e.gridColor_(Color.black); // Colore della cornice e.resize_(5); // Permette il resize della finestra e.step_(0.01); // Risoluzione in decimali
Una particolarità di EnvelopeView consiste nell'utilizzo del metodo .resize(n) che permette di modificare le dimensioni della finestra con il mouse una volta che è stata creata. Come argomento dobbiamo specificare un numero intero che corrisponde a determinate caratteristiche:
Controllo - abbiamo a disposizione due modalità:
Interagire con il mouse per modificare i nodi e recuperare i valori nell'Interprete.
e.action_({e.value.postln}); // Recupera i valori [[valori x],[valori y]]
In questo caso più che una interazione performativa possiamo sfruttare questa possibilità per scegliere empiricamente l'inviluppo che più ci aggrada modificando i parametri sull'interfaccia:
e.action_({h = Env.new(e.value[1], // Y e.value[0].differentiate.drop(1)*~dur, // X [3,-2.3,1.5]) // Curve }); ( r = Routine({ inf.do({ {SinOsc.ar*EnvGen.ar(h,doneAction:2)}.play; h.duration.wait }) }).reset.play )
Prestiamo attenzione che le durate sono restituite sotto forma di onsets in un range compreso tra 0 e 1 mentre l'Array che definisce i nodi in un'istanza di Env accetta tempi delta espressi in valori assoluti. Dovremo quindi:
- Convertire onsets in tempi delta con il metodo .differentiate
- Eliminare lo 0 iniziale con il metodo .drop(1)
- Moltiplicare i valori per la durata totale recuperata in precedenza
- Inviare e recuperare i valori dall'Interprete:
( h = [Env.perc,Env.triangle,Env.sine;Env.linen].choose; e.setEnv(h); {SinOsc.ar(rrand(500,3000))*EnvGen.ar(h,doneAction:2)}.play; ) ( h = Env.new([0,rand(1.0),rand(1.0),0],[rand(1.0),rand(1.0)],[rand2(1.0),rand2(1.0),rand2(1.0)]); e.setEnv(h); {SinOsc.ar(rrand(500,3000))*EnvGen.ar(h,doneAction:2)}.play; ) ( r = Routine({ inf.do({ h = Env.new([0,rand(1.0),rand(1.0),0],[rand(0.2),rand(0.2),rand(0.5)],[rand2(1.0),rand2(1.0),rand2(1.0)]); e.setEnv(h); {SinOsc.ar(rrand(500,3000))*EnvGen.ar(h,doneAction:2)}.play; (h.duration+0.2).wait }) }).reset.play(AppClock) ) r.stop;
ScopeView.new()
Questo oggetto permette di visualizzare l'oscillogramma di un segnale all'interno di una Window. Per farlo dobbiamo creare un Buffer di piccole dimensioni (1024 samples) nel quale scrivere dinamicamente i valori del segnale che vogliamo visualizzare.
( s.boot; s.meter(2,2); s.plotTree; s.freeAllBuffers; f = Buffer.alloc(s,1024,1); // Crea un Buffer w = Window.new("Oscilloscopio", Rect(0,0,410,210)); // Crea una Window c = ScopeView.new(w, Rect(5,5,400,200)); // Crea uno ScopeWiew c.bufnum = f.bufnum; // Assegna il Buffer c.server_(s); // Assegna il Server c.start; // Fa partire la visualizzazione w.front; w.alwaysOnTop_(true); w.onClose_({f.free;w.free;c.free;d.free;e.free}); )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche:
c.background_(Color.rand(0.1,0.3)); // Assegna un colore allo sfondo c.waveColors_(Color.rand(0.3,0.9)); // Assegna un colore alla forma d'onda c.style_(0); // Se stereo visualizzazione verticale c.style_(1); // Se stereo visualizzazione sovrapposta c.style_(2); // 2D Plotting
Segnale da visualizzare - dobbiamo scrivere dinamicamente nel Buffer il segnale da visualizzare con la Classe ScopeOut2.ar()
SynthDef(\scoping, {arg bus=0, buffy=0; ScopeOut2.ar(In.ar(bus,1), buffy) // Scrive nel Buffer }).add; SynthDef(\suono, {arg bus=0; Out.ar(bus, Blip.ar(200,MouseX.kr(1,50))) }).add; {d = Synth(\scoping, [\buffy, f.bufnum])}.defer(0.1); {e = Synth(\suono)}.defer(0.2)
FreqScopeView.new()
Questo oggetto permette di visualizzare lo spettrogramma di un segnale all'interno di una Window. In questo caso il Buffer utilizzato per l'analisi è il numero 0 di default.
( s.boot; s.meter(2,2); s.plotTree; s.freeAllBuffers; w = Window.new("Spettroscopio", Rect(0,0,410,210)); // Crea una Window g = FreqScopeView.new(w, Rect(5,5,400,200)); // Crea uno FreqScopeWiew g.active_(true); // Lo attiva ("false" lo disattiva) g.inBus_(0); // Bus del segnale da analizzare (mono) g.scope; // finestra di visualizzazione g.scopebuf; // Buffer utilizzato per l'analisi w.front; w.alwaysOnTop_(true); w.onClose_({w.free;g.kill;e.free}); // g.kill --> Importante! )
Apparenza - possiamo modificarne dinamicamente alcune caratteristiche grafiche:
g.freqMode_(1); // 0 = lineare 1 = logaritmico g.dbRange_(90); // Cambia range visualizzazione
Segnale da visualizzare - dobbiamo specificare lo stesso Bus specificato come ingresso dello FreqScopeView.
SynthDef(\suono, {arg bus=0; Out.ar(bus,Blip.ar(200,MouseX.kr(1,50));) }).add; {e = Synth(\suono)}.defer(0.2)
FreqScopeView.new() + ScopeView.new()
Se vogliamo visualizzare entrambi all'interno della stessa finestra dobbiamo specificare il Buffer di ScopeView.ar() per evitare che coincida con quello di FreqScopeView.ar().
( s.boot; s.meter(2,2); s.plotTree; s.freeAllBuffers; f = Buffer.alloc(s,1024,1,bufnum:1); // Specifica il Buffer 1 w = Window.new("Oscilloscopio", Rect(0,0,410,415)); c = ScopeView.new(w, Rect(5,5,400,200)); g = FreqScopeView.new(w, Rect(5,210,400,200)); c.bufnum = f.bufnum; c.server_(s); c.start; g.freqMode_(1); g.dbRange_(90); g.active_(true); g.inBus_(0); w.front; w.alwaysOnTop_(true); w.onClose_({f.free;w.free;c.free;d.free;e.free;g.kill}); SynthDef(\scoping, {arg bus=0, buffy=0; ScopeOut2.ar(In.ar(bus,1), buffy) // Scrive nel Buffer }).add; SynthDef(\suono, {arg bus=0; Out.ar(bus, Blip.ar(200,MouseX.kr(1,50))) }).add; {d = Synth(\scoping, [\buffy, f.bufnum])}.defer(0.1); {e = Synth(\suono)}.defer(0.2) )