• Non ci sono risultati.

Sinusoidi & sinusoid

Nel documento Introduzione a SuperCollider (pagine 180-188)

Introduzione a SuperCollider

1. direttamente In altre parole, sclang-interprete è un buon posto per l’utente da dove parlare al server al livello di quest’ultimo (da dove spedire messag-

6.3 Sinusoidi & sinusoid

L’ipotesi alla base dell’utilizzo di un inviluppo d’ampiezza sta nel control- lo di un segnale da parte di un altro segnale in modo da ottenere un risultato più “complesso”, “naturale”, “interessante” etc. Un segnale di inviluppo è un segnale unipolare, ma è evidentemente possibile utilizzare segnali bipolari. In particolare la sinusoide non è soltanto la forma d’onda che produce lo spettro più semplice ma è anche la forma tipica che assume una variazione regolare intorno ad un punto di equilibrio. Dunque, una sinusoide descrive opportuna- mente un fenomeno di oscillazione intorno ad un valore medio. Si considerino due casi:

i_out:0 Out SinOsc 200 0 Abs - 0.5 i_out:0 Out SinOsc 200 0 Round 0.5 0 0.001 0.002 0.003 0.004 0.005 0.006 0.007 0.008 0.009 0.01 -1 -0.8 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.8 1 0 0.001 0.002 0.003 0.004 0.005 0.006 0.007 0.008 0.009 0.01 -1 -0.8 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.8 1

1 // tremolo minimale

2 { SinOsc.ar(mul: 0.5+SinOsc.kr(5, mul: 0.1)) }.play ; 3 // vibrato minimale

4 { SinOsc.ar(freq: 440+SinOsc.kr(5, mul: 5)) }.play ; 6 // con MouseX/Y

7 // tremolo

8 { SinOsc.ar(mul: 0.5 + SinOsc.kr( 9 freq: MouseX.kr(0, 10),

10 mul: MouseY.kr(0.0, 0.5))) }.play ; 11 // vibrato

12 { SinOsc.ar(freq: 440 + SinOsc.kr( 13 freq: MouseX.kr(0, 10),

14 mul: MouseY.kr(0, 10))) }.play ;

• tremolo: in musica un tremolo è una variazione periodica della dinamica, ovvero dell’ampiezza come dimensione percettiva. L’implementazione di un tremolo è evidentemente semplice. È sufficiente sommare all’ampiezza del primo oscillatore il segnale prodotto da un oscillatore di controllo: l’in- cremento varierà periodicamente con frequenza pari a quella dell’oscilla- tore di controllo da 0 fino al massimo (l’ampiezza dello stesso oscillatore), discenderà a 0 e al massimo negativo per ritornare infine a 0. Nell’esem- pio l’argomento mul contiene una costante (0.5) a cui viene sommato il se-

gnale in uscita da un oscillatore che varia5 volte al secondo nell’escursio-

ne [−0.1, 0.1]. Dunque, con la stessa frequenza l’ampiezza dell’oscillatore

portante (audio) varierà nell’intervallo[0.4, 0.6]. Il contributo dell’oscillato-

re di controllo è allora una variazione periodica dell’ampiezza del segnale controllato, variazione il cui periodo è specificato dalla frequenza dell’oscil- latore di controllo. Il suono sintetico acquista così una caratteristica tipica dell’esecuzione strumentale (per esempio, dei fiati). Nell’esempio alle righe 8-10 viene utilizzato il mouse per controllare i due parametri del tremolo; • vibrato: se si applica il ragionamento svolto per il tremolo questa volta non

all’ampiezza ma alla frequenza, si ottiene un vibrato. Un oscillatore di con- trollo controlla un incremento (minimo) della frequenza dell’oscillatore prin-

cipale. Supponendo che 𝑓1, 𝑎𝑚𝑝1, 𝑓2, 𝑎𝑚𝑝2 siano frequenza e ampiezza ri-

dell’oscillatore audio (𝑓1, finora costante) dopo l’incremento varia periodi-

camente (secondo la frequenza dell’oscillatore𝑓2di controllo) tra𝑓1–𝑎𝑚𝑝2e

𝑓1+ 𝑎𝑚𝑝2. Si ricordi che l’uscita del segnale di controllo è infatti sempre una

variazione d’ampiezza±𝑎𝑚𝑝2: questa variazione si somma in questo caso

alla frequenza dell’oscillatore controllato𝑓1. Nell’esempio (5), la variazione

di frequenza è data dalla somma alla costante440 del segnale di un oscilla-

tore che5 volte al secondo oscilla tra [−5, 5] (si veda mul): dunque per 5 volte

al secondo la frequenza audio varierà tra[435, 445]. Analogamente a quanto

visto per il tremolo, è fornito anche un esempio in cui frequenza e ampiezza del vibrato sono controllabili via mouse. Il risultato musicale di una simile variazione periodica dell’altezza di una nota viene definito vibrato. Il perio- do del vibrato dipende dal periodo dell’oscillatore di controllo: si pensi a un violinista che sposta di poco ma continuamente il suo dito attorno alla po- sizione della nota prescelta. Ancora: nella tipica tecnica del canto operistico si mettono in atto simultaneamente tremolo e vibrato.

Un esempio riassuntivo è il seguente: 1 SynthDef("tremVibr",

2 { arg freq = 440, mul = 0.15,

3 tremoloFreq = 5 , tremoloMulPercent = 5, 4 vibratoFreq = 10, vibratoMulPercent = 5 ; 5 var tremoloMul = mul*tremoloMulPercent*0.01 ; 6 var vibratoMul = freq*vibratoMulPercent*0.01 ; 7 var tremolo = SinOsc.kr(tremoloFreq, 0, tremoloMul) ; 8 var vibrato = SinOsc.kr(vibratoFreq, 0, vibratoMul) ; 9 var sinOsc = SinOsc.ar(freq+vibrato, 0, mul+tremolo) ; 10 Out.ar(0, sinOsc) ;

12 }).add ;

La synthDef "tremVibr" prevede argomenti per il controllo dell’oscillatore audio e di tremolo e vibrato (si noti come tutti abbiano valori predefiniti). Sia del tremolo che del vibrato è possibile controllare la frequenza e l’incidenza. Le prima è in entrambi i casi descritta in termini assoluti, cioè attraverso i cicli al secondo (insomma, in Hz). Come si vede alle righe 8 e 9, tremolo e vibra- to sono due segnali in uscita da due oscillatori sinusoidali che hanno appunto come freq rispettivamente tremoloFreq e vibratoFreq. Più interessante la de- scrizione dell’incidenza dei due parametri (ovvero di quanto variano il segnale

lare. in altre parole, l’incidenza è proporzionale ed è descritta percentualmente. Si consideri la riga 5: tremoloMul è calcolato assumendo che tremoloMulPercent, il parametro controllabile dal synth, corrisponda ad una percentuale dell’am- piezza del segnale. Se mul = 0.5 e tremoloMulPercent = 10 allora tremoloMul

sarà pari al10 di mul, cioè a 0.05. Dunque, il segnale assegnato a tremolo sarà

compreso nell’escursione[−0.05, 0.05] e l’ampiezza del segnale audio assegna-

to alla variabile sinOsc oscillerà nell’intorno[0.45, 0.55]. Si ricordi che sinOsc è

appunto una variabile, da non confondere con la UGen SinOsc): attraverso la variabile sinOsc il segnale viene passato come argomento ad Out. Un discorso analogo al calcolo del tremolo vale per il vibrato con il calcolo di vibratoMul. Nell’esempio, viene quindi creato un synth aSynth in cui viene annullato in fase

di creazione il contributo di tremolo e vibrato assegnando un valore pari a0 ai

due argomenti tremoloMulPercent e vibratoMulPercent (non c’è incidenza di tremolo e vibrato poiché i due segnali di controllo hanno ampiezza nulla).

A partire da una simile synthDef è possibile costruire una interfaccia grafi- ca per controllare gli argomenti di un synth. L’idea di partenza è avere per ogni parametro un cursore e di visualizzarne il nome e il valore. Dato il numero esi- guo di parametri si potrebbe semplicemente costruire l’interfaccia “artigianal- mente”, definendo cioè singolarmente tutti i componenti GUI necessari. Vale la pena però sperimentare un altro approccio, più automatizzato. Le classi di elementi necessarie sono due:

1. Slider: permette di costruire un cursore (in inglese, uno slider): la sintassi del costruttore è piuttosto ovvia, giacché richiede una finestra di riferimento

(qui window1) e un rettangolo in cui viene disegnato il cursore.

2. StaticText: è un campo di testo per la visualizzazione, che però non preve- de riconoscimento dell’input (scrive testo sullo schermo ma non serve per immetterlo). La sintassi del costruttore è quella consueta, e definisce un ri- quadro ove verrà stampato sullo schermo il testo. In più viene chiamato sull’istanza così ottenuta il metodo string_ che definisce il testo da stampa- re.

Si tratta di definire un array che contenga i nomi degli argomenti, e a partire da questo di

1 Si noti come window sia resa più alta del richiesto aggiungendovi30 × 2 pixel

1. generare gli oggetti grafici

2. definire le azioni associate a questi ultimi

L’unica difficoltà sta nel fatto che nel connettere valori in uscita dal cursore e argomenti del synth è necessario tenere in conto di un mapping disomogeneo.

Ogni cursore ha un’estensione compresa in[0, 1]. La frequenza freq dell’oscilla-

tore audio può avere ad esempio un’escursione compresa in[50, 10000], mentre

la sua ampiezza mul è invece compresa in[0, 1], le frequenze di tremolo e vibra-

to sono tipicamente comprese in un registro sub-audio,[0, 15], mentre le due

incidenze devono essere espresse percentualmente, dunque in[0, 100]. È perciò

necessario un lavoro di scalatura dei valori espressi da ogni cursore in quanto relativo ad un preciso parametro. Non è opportuno passare i valori non scala- ti al synth includendo dentro la synthDef le operazioni necessarie allo scaling: in questo modo si verrebbe infatti a definire una dipendenza della synthDef dall’interfaccia grafica. Nel lavoro con le interfacce grafiche è invece bene assu- mere che il modello e i dati siano indipendenti dall’interfaccia di controllo/di- splay. In questo modo, si può buttare via l’interfaccia senza dover modificare il modello (qui, la synthDef). D’altra parte, nella definizione di un algoritmo di sintesi quale previsto dalla synthDef è del tutto fuori luogo prevedere elemen- ti che invece concernono la GUI. La soluzione proposta dell’esempio prevede l’uso di un IdentityDictionary, ovvero di una struttura dati che associa in ma- niera univoca ad una chiave un valore, secondo il modello del dizionario che prevede per ogni lemma (la chiave) una definizione (il valore): controlDict as- socia ad ogni stringa-argomento un array che ne definisce l’escursione (secondo il modello [minVal, maxVal]). La parte di codice compresa tra 14 e 24 defini- sce tre array che contengono un numero pari alla dimensione di controlDict rispettivamente di blocchi di testo per la visualizzazione del nome del para- metro, cursori e blocchi di testo deputati alla visualizzazione del valore degli argomenti. Quindi viene istanziato un synth. Infine si tratta di definire la fun- zionalità della GUI attraverso un ciclo su ognuno degli elementi contenuti in controlDict. Nel ciclo, value indica l’elemento di destra (l’escursione), ed a partire da questa si ottiene la chiave name attraverso l’invocazione del metodo controlDict.findKeyForValue(value). Ad esempio, se value è [50, 1000], la chiave associata a name sarà "freq". Quindi viene calcolata un’escursione ran-

ge tra[0, 1] come differenza tra gli estremi (nell’esempio, 1000 − 50 = 950) e il

minimo viene considerato come scarto di partenza (offset). L’espressione la- belArr[index].string_(name) assegna all’elemento di testo index in labelArr la stringa name che vi risulta visibile (nell’esempio, "freq"). Infine, slidArr[in-

gistra una variazione nello stato del cursore (un movimento della leva grafica). Nella funzione l’argomento (qui: theSlid) è come di consueto l’istanza dell’ele- mento grafico su cui è definita la funzione. L’azione realizza quattro comporta- menti, ognuno descritto da un’espressione alle righe 32-35.

1. dichiarando la variabile paramValue se ne calcola il valore. Nell’esempio pre- cedentemente relativo a "freq", poiché theSlid.value è sempre compreso

in[0, 1] ed essendo 𝑟𝑎𝑛𝑔𝑒 = 50, l’escursione varia appunto tra [0, 950]+50 =

[50, 1000];

2. la seconda azione consiste nello stampare sullo schermo il nome del para- metro ed il suo valore;

3. si tratta di controllare il synth impostando per l’argomento name il valore paramValue (ovvero: aSynth.set("freq", 123.4567890);

4. infine, il valore paramValue viene scritto nell’elemento di testo index in va- lueArr.

1 var aSynth = Synth.new("tremVibr");

2 var controlDict = IdentityDictionary[

3 "freq" -> [50, 1000], 4 "mul" -> [0,1], 5 "tremoloFreq" -> [0, 15], 6 "tremoloMulPercent" -> [0, 50], 7 "vibratoFreq" -> [0, 15], 8 "vibratoMulPercent" -> [0, 50] 9 ];

11 var window = Window.new("Vibrato + tremolo",

12 Rect(30,30, 900, controlDict.size+2*30)) ;

14 var labelArr = Array.fill(controlDict.size, { arg index ;

15 StaticText( window, Rect( 20, index+1*30, 200, 30 )).string_( 0 ) ;

16 }) ;

18 var slidArr = Array.fill(controlDict.size,

19 { |index| Slider( window, Rect( 240, index+1*30, 340, 30 )) }) ;

21 var valueArr = Array.fill(controlDict.size,

22 { |index| StaticText( window, Rect( 600, index+1*30, 200, 30 )) 23 .string_( 0 ) ;

24 }) ;

26 controlDict.do({ arg value, index ;

27 var name = controlDict.findKeyForValue(value) ;

28 var range = value[1]-value[0] ;

29 var offset = value[0] ;

30 labelArr[index].string_(name) ;

31 slidArr[index].action = { arg theSlid ;

32 var paramValue = theSlid.value*range + offset ; 33 [name, paramValue].postln ;

34 aSynth.set(name, paramValue) ;

35 valueArr[index].string_(paramValue.trunc(0.001) ) ; 36 }

37 }) ;

Nel documento Introduzione a SuperCollider (pagine 180-188)