• Non ci sono risultati.

Ancora un esempio GU

Nel documento Introduzione a SuperCollider (pagine 73-80)

L’esempio seguente presenta il codice di creazione di una GUI per la scelta del colore, Simple Color Selector. Il controllo di elementi GUI è particolarmente

processo è di aiuto alla comprensione.

La GUI progettata si compone di tre cursori. Ogni cursore controlla uno dei tre parametri che definiscono il colore (il quarto, qui non considerato, è la tra- sparenza, alpha). Il colore viene visualizzato come sfondo della finestra. Inoltre, il valore ottenuto per ogni componente è visualizzato in forma numerica a la- to del cursore relativo. La GUI permette di scegliere tra due tipici spazi colore. Come si è visto, il colore viene definito in un calcolatore attraverso il modello RGB. Tuttavia, questo modello è poco intuitivo rispetto alla percezione del colo- re: viene così anche utilizzato (tra gli altri) un modello che descrive il colore nei termini di tinta (“hue”, circolarmente dal rosso al giallo al verde al blu al viola, al rosso), saturazione (“saturation”, che esprimi la quantità di tinta, dall’assen- za al massimo), brillantezza (“brightness” o “value”, che indica la luminosità, dal bianco al nero). Il modello è perciò definito “HSB” o “HSV”. Una codifica HSV può essere convertita con una formula in RGB (l’unico formato realmente implementato su un calcolatore). Così, in SC Color prevede un costruttore hsv che permette di definire un colore attraverso appunto tinta, saturazione, valore

(sempre in escursione normalizzata[0, 1]). Nella GUI un pulsante permette di

scegliere tra RGB e HSV. Chiaramente, esistono più modi di organizzare la GUI, e di nuovo più modi per implementarla in SC.

cursore 1 cursore 2 cursore 3 pulsante flag color Func window v1 v2 v3 2 3 1 4

Fig. 3.3 Struttura di Simple Color Selector.

Un diagramma del progetto è rappresentato in Figura 3.3. In Figura si ricono- scono due dati: un flag, cioè una variabile che può avere valori discreti (qui due: nel codice, rgb o hsv, e un array di tre valori (in Figura, v1-v3) per la specifica- zione del colore. Un pulsante permette di scegliere quale dei due valori del flag è attivo (e quindi lo spazio colore selezionato). I tre cursori svolgono ognuno quattro azioni (numerate e sottolineate in Figura). Ognuno seleziona un valore

relativo nell’array (1), quindi chiama la funzione colore a cui passa l’array e il flag (2), la funzione restituisce un colore (3), e si procede al cambiamento dello sfondo (4). La funzione ha un ruolo importante perché è il nucleo computa- zione del tutto, che va mantenuto separato dall’interfaccia utente per garantire incapsulamento. Dunque, la funzione non farà riferimento a variabili esterne, ma riceverà le stesse come argomenti. Analogamente, non sarà la funzione a modificare lo sfondo, ma semplicemente restituirà il colore risultante.

1 (

2 /*

3 Simple Color Selector 4 RGB-HSV

5 */

7 var window = Window("Color Selector", Rect(100, 100, 300, 270) ).front ;

8 var guiArr, step = 50 ;

9 var flag = \rgb , colorFunc ;

10 var colorArr = [0,0,0] ;

12 colorFunc = { arg flag, cls;

13 var color, v1, v2, v3 ;

14 # v1, v2, v3 = cls ; 15 if(flag == \rgb ){

16 color = Color(v1, v2, v3) 17 }{

18 color = Color.hsv(v1.min(0.999), v2, v3) 19 } ;

20 color ;

21 } ;

23 Button(window, Rect(10, 200, 100, 50))

24 .states_([["RGB", Color.white, Color.red], ["HSV", Color.white, Color.black]])

25 .action_({ arg me; if (me.value == 0) {flag = \rgb } {flag = \hsv } });

27 guiArr = Array.fill(3, { arg i ;

28 [

29 Slider(window, Rect(10, (step+10*i+10), 100, step)), 30 StaticText(window, Rect(120, (step+10*i+10), 120, step)) 31 ]

32 }) ;

34 guiArr.do{|item, index|

35 item[0].action_{|me|

36 item[1].string_(me.value) ; 37 colorArr[index] = me.value ;

38 window.background_(colorFunc.value(flag, colorArr));

39 }} ;

• 7-10: dichiarazione delle variabili. Si noti che window è immediatamente as- sociata alla creazione della finestra che viene visualizzata. Altre variabili vengono inizializzate con valori “sensati”;

• 12-21: il blocco è dedicato alla definizione della funzione colorFunc. La fun- zione prende come argomenti in ingresso flag e cls. Il primo è il flag, il secondo l’array dei tre valori colore. La riga 14 introduce un’altra abbrevia- zione utile: # v1, v2, v3 = cls equivale a v1 = cls[0]; v1 = cls[1]; v3 = cls[2]. Il blocco condizionale (15) opera in relazione alla verifica del flag. Se flag è rgb (letteralmente, se flag == rgb è vero), allora a color viene assegnato un colore costruito secondo il modello RGB (default). Altrimenti (i casi sono solo due, RGB o HSV, dunque non ci si può sbagliare), gli stessi valori definiscono un colore costruito in riferimento a HSV. In quest’ultimo caso, si noti che per definizione (si veda l’help file di Color.hsv) l’argomento

hue può valere al massimo0.999. Se il primo cursore viene mosso a 1, allora

ci sarà un problema. La soluzione è il metodo min definito sui numeri, che restituisce il minore tra il numero su cui è chiamato e il valore di soglia. Dun-

que, per tutti i valori minori di0.999, restituirà gli stessi, mentre restituirà

0.999 se il numero è maggiore. Si noti che l’ultima espressione della funzio- ne è semplicemente una invocazione di color, che verrà restituito in uscita. Infine, da notare la sintassi “zuccherina” nella definizione del condizionale, che elimina un po’ di parentesi;

• 23-25: viene costruito un pulsante. Si noti che non essendo più modificato, non viene associato a variabile (l’oggetto semplicemente funziona). In SC un pulsante si costruisce come le altre GUI. In più è dotato di una proprietà states che definisce attraverso un insieme di array ogni stato del pulsante

(possono essere costruiti pulsanti a𝑛 stati). I parametri sono agevolmente

inferibili dalla GUI in funzione. L’argomento action associa un’azione al

pulsante. Il valore di un pulsante è l’indice dello stato: cioè, il pulsante vale0

nel primo stato,1 nel secondo, e così via. Diventa quindi possibile ragionare

condizionalmente in funzione dello stato in cui si trova il pulsante stesso. Qui i valori sono solo due (si tratta di definire il valore del flag, attraverso un simbolo), e un if è sufficiente, che determina in funzione dello stato il valore del flag;

• 27-32: si tratta ora di costruire i cursori e le etichette che ne visualizzano il valore. L’approccio presentato è “sovradimensionato” per il caso, ma è utile in generale. Invece di costruire uno per uno i 6 elementi necessari, viene uti- lizzato un approccio procedurale. Il costruttore fill su Array costruisce un

array di𝑛 posti, per ognuno dei quali calcola la funzione passata per argo-

di un cursore e di un’etichetta. Ogni elemento dell’array guiArr sarà perciò a sua volta un array di due elementi. La costruzione del cursore Slider e dell’etichetta StaticText è del tutto analoga a quanto visto per Knob. Si noti che la posizione degli elementi grafici dipende da un parametro step co- mune per tutti e modificato dal contatore. L’idea è “fai tre volte un cursore e un’etichetta, uguali ogni volta e ogni volta scendendo di un po’”. Quando i

= 0, allora il cursore in ascissa è nel punto10, quando i = 1 è nel punto 70,

e così via. Un simile approccio si rivela estremamente utile nei casi in cui: gli elementi non siano tre ma molti di più; il numero degli elementi possa non essere conosciuto in anticipo ma possa dipendere da qualche variabile. Il costo della programmazione della GUI è allora compensato dalla grande flessibilità ottenuta;

• 34-39: il blocco definisce l’azione di ogni cursore. L’azione è assegnato ite- rando sull’array che contiene gli elementi. Ogni elemento è accessibile at- traverso item e la sua posizione attraverso index (si ricordi che i nomi degli argomenti sono arbitrari, ma la loro posizione è vincolante). Ora, ogni ele- mento dell’array guiArr è un array composto da cursore e etichetta. Dunque, item[0] restituirà il cursore, e item[1] l’etichetta relativa. Ecco che allora l’azione associata ad ogni cursore (35, la funzione associata a ogni movi- mento del cursore) consisterà nell’aggiornare il valore dell’etichetta associa- ta (attraverso il suo attributo string) (36); nell’aggiornare l’array colorArr nella posizione index col valore del cursore (37); nel modificare lo sfondo di window con il risultato della chiamata alla funzione colorFunc a cui sono passati flag e colorArr (38). Si noti che questo passaggio include i passi 2-4 di Figura 3.3 (in cui non sono peraltro raffigurate le etichette).

Quanto visto nel capitolo permette di iniziare a muoversi agevolmente nel linguaggio SC. C’è molto di più, chiaramente. Ma con i riferimenti generali è possibile esplorare il linguaggio stesso attraverso il sistema di help file interat- tivi e gli esempi che questi ultimi provvedono. Vale la pena chiudere ricordan- do o introducendo alcune abbreviazioni (un po’ di “zucchero sintattico”) molto usate e utili, ma che possono indurre il lettore in confusione:

1 // omissione di new in Object 2 a = Qualcosa.new(argomento) ; 3 a = Qualcosa(argomento) ;

5 // omissione di value in Function

6 funzione.value(valore) ;

7 funzione.(valore) ;

9 // assegnazione multipla a Array

10 # a,b,c = array ;

11 a = array[0]; b = array[1]; c = array[2] ;

13 // meno parentesi

14 if (a, {fai}, {fai altro}) ;

15 if (a) {fai} {fai altro} ;

17 Qualcosa.metodoConFunzione_({}) ; 18 Qualcosa.metodoConFunzione_{} ; 20 // argomento abbreviato attraverso |

21 { arg argomento, unAltroArgomento ; } ;

22 { |argomento, unAltroArgomento| } ;

SuperCollider è indubbiamente specializzato nella sintesi del suono, e tipi- camente in tempo reale, attraverso scsynth, il server audio. Obiettivo di questo capitolo non è però introdurre il server audio e le sue funzioni. La discussio- ne seguente si propone invece di fornire una introduzione rapidissima ad al- cuni aspetti della rappresentazione e della sintesi del segnale, ma proseguendo nell’interazione esclusivamente con il linguaggio SuperCollider. Il lettore che sia già minimamente esperto o sia impaziente di vedere come funziona la sintesi at- traverso il server può tranquillamente saltare al capitolo successivo. La discus- sione sui fondamenti della sintesi permette comunque di approfondire nella pratica alcuni aspetti linguistici già discussi. In ogni caso, per una discussione introduttiva ma dettagliata sul suono e sulla sua rappresentazione si rimanda a AeM, soprattutto capitoli 1, 2, 3.

Nel documento Introduzione a SuperCollider (pagine 73-80)