Esercizi 4

Determinismo

Nella prima parte di questo scritto (Esercizi 2) abbiamo visto cosa è una procedura deterministica e come possiamo impiegarla per la composizione di suoni nel tempo (rimando al Capitolo in oggetto anche per la dissertazione teorica). In questo Capitolo invece restringiamo il campo e vediamo come realizzare sequenze di eventi nel tempo in SuperCollider. Teniamo presente un concetto che ripeterò più volte: organizzeremo sequenze di quei parametri del suono che più sono legati alla tradizione musicale occidentale: altezze, intensità, tempi delta e durate ma le stesse tecniche possono essere applicate a qualsiasi altro parametro di qualsiasi tipo di sintesi o elaborazione del suono come vedremo nelle prossime sezioni. Infine sottolineo che in questo Capitolo scopriremo solo come programmare sequenze nel tempo di valori assoluti senza entrare nel merito di regole, linguaggi e sintassi musicali o sonore che saranno anch'esse affrontate nelle prossime Sezioni. Tutti gli esempi e lo sviluppo degli esercizi sono eseguiti dal Synth \sperc che dovremo inviare al Server valutando il seguente codice.

// frequenze in Hz
// ampiezze tra 0.0 e 1.0
// durate in secondi

(
s = Server.local.boot;
SynthDef(\sperc, {|freq =440, amp=0.5, dur=0.5, atk=10, gate=1.0|
                  var durms = dur*1000,
                      env   = EnvGen.ar(Env.perc(0.01, dur-0.01),gate,doneAction: 2),
                      osc   = SinOsc.ar(freq, 0, amp);
                  Out.ar(0, env *osc ! 2)
                  }).add;
)

Sincronizzazione

Alla fine del Capitolo precedente abbiamo visto come realizzare una sequenza musicale qualsiasi in libero arbitrio semplicemente aggiungendo nuovi Array, uno per ogni parametro desiderato (in questo caso frequenze e ampiezze sono espresse in midi pitch e velocity ovvero da 0 a 127):

(
var bpm,t,tsec,note,vels,durs,dt,r;
bpm  = 150;
t    = TempoClock(bpm/60);
tsec = 60/bpm;                
note = [90,      100,  98,   97,    96, 93].midicps;
vels = [60,       90, 120,   60,    40, 10]/127;
durs = [0.125, 0.125, 0.5, 0.25, 0.125,  1]*tsec;
dt   = [ 0.25,  0.25,   1,    1,     1,  1];

r = Routine({dt.do{arg delta, id;

                   Synth("sperc",[\freq, note.at(id), // frequenze richiamate come indici
                                  \amp,  vels.at(id), // ampiezze richiamate come indici
                                  \dur,  durs.at(id)  // durate richiamate come indici
                                 ]
                        );
                   delta.wait}                        // tempi delta richiamati come items
             }).reset.play(t);
)

image not found

Analizziamo il codice. Come prima cosa vediamo come inviare i parametri al Synth. La sintassi è la seguente:

Synth("nome_del_synth", [\nome_parametro, valore_da_inviare,\nome_parametro, valore_da_inviare,...]);

Anche in questo caso assumiamo questa sintassi senza porci troppe domande (la affronteremo in dettaglio nella prossima Sezione), ricordandoci che all'interno dell'Array ci saranno sempre coppie di parametri: \etichetta, valore_da_inviare e che per quanto riguarda il Synth "sperc" possiamo modificare dinamicamente solo: frequenze (in Hertz), ampiezze (tra 0.0 e 1.0) e durate (assolute in secondi). Ognuno di questi parametri è rappresentato come sequenza di valori contenuta in un Literal Array e assegnato a una variabile locale il cui nome ricorda il contenuto:

note = [90,      100,  98,   97,    96, 93];
vels = [60,       90, 120,   60,    40, 10];
durs = [0.125, 0.125, 0.5, 0.25, 0.125,  1];
dt   = [ 0.25,  0.25,   1,    1,     1,  1];

Per comodità abbiamo specificato questi parametri in unità di misura facilmente interpretabili sia da altri esseri umani che da altri software o interfacce esterne a SuperCollider: frequenze in midinote, ampiezze in velocity (entrambe in valori compresi tra 0 e 127), durate e tempi delta in tempo relativo (frazioni di beat). A questo punto si pone un problema. Infatti come abbiamo appurato qualche riga sopra il Synth "sperc" accetta valori espressi unità di misura differenti da queste. Si rendono necessarie alcune conversioni.

Conversioni di tempo

Come possiamo notare, gli items dell'Array in cui sono specificati i tempi delta sono richiamati uno alla volta all'interno della Routine che genera la sequenza di eventi e che "lavora" al tempo relativo espresso in bpm con TempoClock. Di fatto sono già frazioni del beat. Gli items dell'Array in cui sono specificate le durate invece sono "inviati" al Synth che accetta valori espressi in tempo assoluto (secondi). Si rende dunque necessaria una conversione:

In questo modo al Synth arriveranno i valori in tempo assoluto calcolati di volta in volta al tempo relativo che esprimeremo in bpm.

Conversioni di altezze o frequenze

Come abbiamo già visto nel Capitolo precedente possiamo esprimere le altezze attraverso differenti unità di misura:

Vediamo i principali metodi di SuperCollider che possiamo utilizzare per le conversioni di frequenza:

60.midicps;               // midi --> Hz
440.cpsmidi;              // Hz   --> midi

2.degreeToKey([0,2,5,7]); // il ricevente è l'indice (degree) della scala che spefichiamo come argomento
6.keyToDegree([0,2,3,6]); // il ricevente èil grado relativo tra gli indici

2.midiratio;              // midi --> fattore di moltiplicazione
1.6.ratiomidi;            // fatt.--> midi

440 * 1.midiratio;

Conversioni di intensità o ampiezze

Compiamo le stesse operazioni per quanto riguarda le intensità:

Vediamo i principali metodi di SuperCollider che possiamo utilizzare per le conversioni delle ampiezze:

64/127;       // velocity --> lineare
0.5*127;      // lineare  --> velocity
0.5.pow(4);   // lineare  --> quartica
0.0001.ampdb; // lineare  --> decibel
-80.dbamp;    // decibel  --> lineare

Pause

Fino a questo punto abbiamo realizzato sequenze di eventi nel tempo ma nessuno di questi era una pausa. In generale, indipendentemente dal software musicale che usiamo ci sono due modi differenti per ottenere pause all'interno di una sequenza di suoni:

Sequenze e insiemi

Nella pratica musicale un Array o una qualunqe collezione di dati può essere pensata e utilizzata in due modi differenti:

Questa differenza, nel linguaggio musicale se applicata alle altezze invece che alle durate può coincidere con quella che esiste in generale tra pensiero contrappuntistico e pensiero armonico, orizzontalità vs verticalità:

Accordi

Secondo quanto appena enunciato una sequenza di valori numerici può rappresentare:

Esercizio 4.1

Realizzare in SuperCollider:

Alea

Nella prima parte di questo scritto (Esercizi 2) abbiamo visto anche cosa è una procedura non deterministica e come possiamo impiegarla per la composizione di suoni nel tempo (rimando al Capitolo in oggetto anche per la dissertazione teorica). In questo Paragrafo vedremo quali sono gli oggetti di SuperCollider che restituiscono valori pseudocasuali seguendo le diverse modalità già illustrate nel Capitolo sopracitato. Tutti gli oggetti seguenti operano secondo una distribuzione linerare delle probabilità, le tecniche che seguono procedure stocastiche saranno affrontate in un altro Capitolo.

Infine soffermiamoci su una possibile esigenza musicale comune a tutti gli algoritmi di scelta pseudocasuale appena illustrati: il voler richiamare tutti i valori in un ambito o appartenenti ad un insieme una volta sola, senza ripetizioni come ad esempio la generazione pseudo-casuale delle altezze di una serie dodecafonica. In questo caso siamo obbligati ad utilizzare l'ultima tecnica illustrata in quanto dobbiamo:

Se invece della scelta pseudocasuale tra due limiti volessimo compiere la stessa operazione tra i valori di un insieme dovremmo compiere le stesse operazioni non sull'Array degli items ma su un Array degli indici sottointesi:

(
var items, idx, bpm;
items   = [98,100,76,73,89,103];          // altezze
idx     = [0, 1,  2, 3, 4, 5  ].scramble; // indici
bpm = 120;

t = TempoClock.new(bpm/60);
r = Routine.new({
                 idx.do({arg id;
                         Synth(\sperc,[freq:items.at(id).midicps,
                                        amp: 0.5]);
                         0.25.wait
                         })
}).play(t)
)

Esercizio 4.2