Articolazioni del tempo

Secondo la teoria greco-gregoriana, il ritmo è concepito come ordine nella successione dei movimenti. Alla base della successione stessa sta l’unità di misura indivisibile: il tempo primo, corrispondente alla naturale durata della pronuncia di una sillaba, di un movimento di danza o di un passo, e che in grafia moderna si può tradurre nella figura di croma (Tactus, battito del cuore).   R.Dionisi

Note e oggetti sonori

...

Beat e griglie temporali

Nel corso dei secoli l’uomo è passato dal ritmo naturale che ha a che fare con la ciclicità degli eventi che accadono in natura al ritmo musicale, basato invece su di una pulsazione regolare finita o infinita:

Nelle diverse epoche storiche questa, è stata chiamata in molteplici modi: Tactus, Tempo Primo, Beat, Click, etc. Da questo punto in avanti, per convenzione sarà sempre utilizzato il termine inglese beat. Il beat, come abbiamo visto nella seconda parte di questo scritto può essere misurato e specificato in due modi diversi:

Tipologie di pulsazioni

Dai greci a oggi il senso del tempo musicale si è modificato enormemente. Dal battito regolare del Tactus (la sillaba breve) alla de-costruzione dello stesso in pulsazioni irregolari e poliritmie strutturali perseguite dai compositori europei e statunitensi nel secolo scorso, fino al ritorno ossessivo del beat nella musica dei compositori minimalisti e nei diversi generi di musica Pop/Rock sia d’ascolto sia da ballo. La Figura seguente illustra uno schema che comprende tutte le possibili tipologie di pulsazioni esistenti siano esse suddivisioni di un beat o meno.

image not found

Osserviamole nel dettaglio.

Regolare

Questo tipo si pulsazione può coincidere con il beat o esserne una suddivisione costante. Può essere specificato in suddivisioni di un tempo metronomico:

image not found

oppure in secondi o millisecondi:

image not found

Può assumere una valenza formale:

oppure può caratterizzare un intero brano o un intera poetica:

Può essere utilizzata nella costruzione di poliritmi polifonici come vedremo più avanti, oppure diventare elemento timbrico in processi di sintesi ed elaborazione del suono (battimenti, ribattuti, tremolo, sintesi granulare, etc.), come vedremo nella Sezione dedicata all’audio di questo scritto. La caratteristica principale che deve assumere per essere percepita come tale è il suo sviluppo in un tempo prolungato, nella costituzione di un flusso ritmico continuo. Altrimenti potrebbe anche essere percepita come una sequenza formata da pulsazioni metriche:

image not found

Generalmente è scritta in notazione tradizionale con o senza indicazioni di misura. Il beat può essere percepito come nella Sonata di Beethoven (1 beat = 1 misura) e in quella di Ravel, oppure non essere strutturale come nel Moto perpetuo di N.Paganini o in Piano phase di S.Reich.

SC Clock

In SuperCollider possiamo realizzare una sequenza finita o infinita di pulsazioni regolari utilizzando diverse sintassi, ma prima di esaminarle nel dettaglio e per facilitarne la comprensione a livello percettivo, definiamo due Synth:

s.boot;
s.scope(1);
s.meter(1,1)

(
SynthDef(\sperc,
               {arg freq=440, amp=0.5, dur=0.5;
                var env, osc;
                    env = Env.perc(0.01, dur-0.01).kr(2,1);        // Inviluppo percussivo
                    osc = SinOsc.ar(freq, 0, amp.lag(0.02));
                Out.ar(0, env*osc)}
        ).add;

b = Buffer.read(s,Platform.resourceDir +/+ "sounds/a11wlk01.wav"); // Audio file

SynthDef(\samp,
               {arg pos=0, dur=1, amp=0.5;		
                var rata,inizio,fine,env,osc;
                    rata   = SampleRate.ir;                         // Sample rate
                    inizio = pos*rata;                              // punto iniziale in frame
                    fine   = dur*rata+inizio;                       // punto finale in frame
                    env    = Env.linen(0.01,dur-0.02,0.01).kr(2,1); // Inviluppo trapezoidale
                    osc    = BufRd.ar(1, b.bufnum, Line.ar(inizio,fine,dur),0);
                Out.ar(0, env*osc*amp.lag(0.02))}
        ).add;
)

Cominciamo con il realizzare una sequenza infinita. La sintassi più semplice e concisa che possiamo utilizzare è quella che abbiamo già incontrato nel paragrafo riguardante la Classe TempoClock:

(
m = 92;
t = TempoClock(m/60)
 .sched(0,               
          {"nota".postln; // posta
           Synth(\sperc); // suona
           1}             // aspetta 1 beat   
        );
)

t.clear;                  // stoppa lo scheduler 

(
m = 92;
d = b.duration;                    // durata del buffer in secondi
t = TempoClock(m/60)
 .sched(0,               
          {var pos, dur;
               pos = rrand(0,d-1); // inizio pseudocasuale (secondi)
               dur = rand(1.0);    // durata pseudocasuale (secondi)

               "suono".postln;     // posta
               Synth(\samp,[\pos,pos,\dur,dur]); // suona
               1}                  // provare a cambiare il valore             
       );
)

t.clear;                           // stoppa lo scheduler 

In questo caso il susseguirsi di pulsazioni regolari a 92 bpm è infinito e termina solamente nel momento in cui eseguiamo il comando scritto alla riga 9 (oppure cmd+.)

Se vogliamo invece realizzare una sequenza finita di pulsazioni attraverso questa sintassi dobbiamo creare un secondo flusso temporale dallo stesso TempoClock che valuti automaticamente il comando di stop dopo un certo numero di beats (la riga di codica che nel caso precedente abbiamo valutato "a mano"):

(
var bpm,n_rep,t;         // Variabili locali
bpm   = 92;
n_rep = 5;
t = TempoClock(m/60)
 .sched(0,               
          {"nota".postln; // posta
           Synth(\sperc); // suona
           1}             // aspetta 1 beat   
        )
 .sched(n_rep, {t.clear; nil}); // stop automatico
)

(
var bpm,n_rep,t,b_dur;      // Variabili locali
bpm   = 92;
n_rep = 5;
b_dur = b.duration;         // durata del buffer in secondi
t = TempoClock(m/60)
 .sched(0,               
          {var pos, dur;
               pos = rrand(0,b_dur); // inizio pseudocasuale (secondi)
               dur = rand(1.0);      // durata pseudocasuale (secondi)

               "suono".postln;       // posta
               Synth(\samp,[\pos,pos,\dur,dur]); // suona
               1}                    // provare a cambiare il valore             
       )
 .sched(n_rep, {t.clear; nil});   // stop automatico
)

In questo esempio a differenza di quello precedente possiamo utilizzare variabili locali in quanto il comando si stop (t.clear) è scritto all’interno dello stesso blocco di codice.

Un altro esempio dell’utilizzo di questa sintassi è quello in cui sono presenti due o più flussi paralleli di pulsazioni regolari, ognuno con tempi delta differenti ma tutti legati a una singola istanza di TempoClock:

(
m = 92;
t = TempoClock(m/60)
 .sched(0,               
          {"beat------------".postln;    // beat
           Synth(\sperc, [\freq,4698]); 1}                         
        )
 .sched(0,               
          {"pulsazione".postln;          // pulsazione
           Synth(\sperc, [\freq,1661]); 0.25}                         
        );
)

t.clear;                 

(
m = 92;
d = b.duration;                    
t = TempoClock(m/60)
 .sched(0,               
          {var pos, dur;
               pos = 1.4; 
               dur = 0.1;

               "suono_acuto".postln; Synth(\samp,[\pos,pos,\dur,dur]); 0.3333}                            
       )
 .sched(0,               
          {var pos, dur;
               pos = 0.3; 
               dur = 0.2;

               "suono_grave-------".postln; Synth(\samp,[\pos,pos,\dur,dur,\amp,rand(1.0)]); 1}                            
       );
)

t.clear;                          

Se volessimo fermare tutte le sequenze automaticamente dopo un numero preciso di beats, basterà specificarne semplicemente il numero:

(
m = 92;
n = 4;
t = TempoClock(m/60)
 .sched(0, {"beat------------".postln;          // beat
            Synth(\sperc, [\freq,92]); 1.00})
 .sched(0, {Synth(\sperc, [\freq,1661]); 0.50}) // pulsazione                         
 .sched(0,               
          {"pulsazione".postln;                 // pulsazione
           Synth(\sperc, [\freq,466]); 0.25})
 .sched(n, {t.clear; nil})                      // stoppa  
)

Musicalmente questa sintassi non offre molti spunti di interesse, se non come vedremo più avanti nella programmazione di texture poliritmiche o meglio polimetriche che però non sono formate dallo scorrere di semplici pulsazioni regolari ma dalla sovrapposizione di più flussi temporali ed esulano dunque dai propositi di questo Paragrafo diventandone un’eventuale estensione. Al momento il miglior utilizzo che ne possiamo fare consiste nel programmare una semplice Click track da inviare in cuffia a esecutori che suonano il proprio strumento dal vivo.

SC Routine

Quanto appena esposto sotto un punto di vista pratico e percettivo funziona, ma nel Paragrafo dedicato alle Routine abbiamo affermato che i Clock dovrebbero essere utilizzati solo per generare il beat generale, il tempo metronomico di un brano, non le sue suddivisioni interne. Per queste infatti è più corretto o meglio più informaticamente proprio utilizzare Routine, Task o una collezione di oggetti dedicati di SuperCollider chiamata Pattern. Ai fini pratici negli esempi seguenti ci riferiremo solo a Routines, sottointendento l’utilizzo dei Tasks.

Uno dei brani più esemplificativi di questo concetto musicale credo sia il Preludio della Sonata in Sol Maggiore per Violoncello solo di J.S.Bach dove il beat musicale cade ogni otto pulsazioni regolari mentre quello metronomico ogni quattro. Ci sono dunque tre flussi di pulsazioni regolari che scorrono paralleli nel tempo.

L'esempio sottostante riassume diverse sintassi che già conosciamo per generare sequenze ritmiche infinite regolari attraverso l'impiego di Routine:

// --------------------------- 1 voce polifonica

(
m = 92;
t = TempoClock(m/60);

r = Routine.new({
                 inf.do({Synth("sperc"); 0.2.wait })
                 }).reset.play(t);
)
r.stop;

// --------

(
m = 92;
t = TempoClock(m/60);

r = Routine.new({
                 inf.do({
                         Synth("sperc",[\freq,rand2(200)+1500,\amp,rand(0.6)]);
                         0.2.wait
                         })
                 }).reset.play(t);
)

r.stop;

// --------------------------- più voci polifoniche

(
m = 52;
t = TempoClock(m/60);

r = Routine.new({ inf.do({Synth("samp",[\pos,1.3,\dur,0.1,\amp,rand(1.0)]); 0.125.wait }) }).reset.play(t);
u = Routine.new({ inf.do({Synth("samp",[\pos,0.2,\dur,0.2,\amp,rand(1.0)]); 0.25.wait })  }).reset.play(t);
v = Routine.new({ inf.do({Synth("samp",[\pos,2.4,\dur,0.45,\amp,rand(1.0)]); 0.5.wait })  }).reset.play(t);
)

r.stop;
u.stop;
v.stop;

Di seguito invece alcuni esempi per generare sequenze ritmiche finite regolari attraverso l'impiego di Routine:

// --------------------------- 1 voce (polifonica) (syncro)

(
var beat,freq,amp,dur,delta,t;

beat  = 92;
freq  = [456,856,2789,987,1345];
amp   = [0.3,0.7, 1.0,0.5, 0.3];
dur   = [0.2,0.5, 1.0,1.5, 2.0]; // durata = fattore di moltiplicazione del delta
delta = 0.2;                     // pulsazione (delta) costante (regolare)

t = TempoClock(beat/60);
r = Routine.new({
                 freq.do({arg i, id;
                          Synth("sperc",[\freq,i,\amp,amp[id],\dur,dur[id]*delta]);
                          delta.wait
                          })
                }).reset.play(t);
)

// --------------------------- più voci polifoniche (syncro)

(
var beat,cue1,cue2,cue3,t;
beat = 52;

cue1 = [[0.6,0.2,0.8],[2.0,0.3,0.5]];  // [pos,delta,dur] Array 2D    
cue2 = [[0.5,0.4,0.1],[1.2,0.3,0.5], [0.8,0.65,1]];         
cue3 = [[2.3,0.1,1.0],[2.3,0.1,0.7], [2.3,0.1,0.5],[2.3,0.1,0.3],[2.3,0.1,0.1]];

t = TempoClock(beat/60);
r = Routine.new({
                 cue1.do({arg i;
                          Synth("samp",[\pos,i[0],
                                        \dur,i[1]*i[2], // durata = fattore di moltiplicazione del delta
                                        \amp,0.3]);
                          i[1].wait })
                 }).reset.play(t);
u = Routine.new({
                 0.1.wait;               // pausa iniziale
                 cue2.do({arg i;
                          Synth("samp",[\pos,i[0],
                                        \dur,i[1]*i[2], 
                                        \amp,0.8]);
                          i[1].wait })
                 }).reset.play(t);
v = Routine.new({
                 0.7.wait;                // pausa iniziale
                 cue3.do({arg i;
                          Synth("samp",[\pos,i[0],
                                        \dur,i[1]*i[2], 
                                        \amp,0.4]);
                          i[1].wait })
                 }).reset.play(t);
)

SC Pattern

In SuperCollider c’è una una specifica collezione di oggetti chiamata Pattern. Questi sono ottimizzati per generare sequenze di eventi contenenti informazioni (streams) che, spesso sono numeri (numeric streams), ma in linea di principio possono essere qualsiasi tipo di data. I Patterns sono un’alternativa di alto livello a Routine e Task e uno dei vantaggi nel loro utilizzo sta nel fatto che le costruzioni sintattiche da utilizzare sono strutturate per pensare solo a ciò che vogliamo accada nel tempo e non a come questo avviene. Tutti gli oggetti appartenenti a questa collezione cominciano con la lettera P maiuscola (Pbind, Pseries, Pwhite, Pfunc, etc.) e per questo motivo facilmente identificabili. Il primo che incontriamo è Pbind(). Per ora pensiamo che serve a generare una sequenza infinita di pulsazioni regolari se invochiamo su di esso il metodo .play:

s.boot;
s.scope(1);
s.meter(1,1);

Pbind().play;

Valutando il codice precedente sentiremo suonare una sequenza di pulsazioni regolari separate tra loro da tempi delta di un secondo, con un’altezza che nel sistema temperato occidentale corrisponde al do centrale. Nel codice valutato però non c’è specificato nulla di tutto ciò: ne il tipo di strumento che esegue la sequenza (come nel caso di Routine e Task), ne i parametri necessari a definirne le caratteristiche. L’arcano si svela nell’apprendere il compito che ha Pbind(), ovvero inviare in modo dinamico uno o più parametri (quasi sempre numerici) a specifici indirizzi esattamente come per gli argomenti di una funzione, e se non specifichiamo nulla, utilizza i parametri di default che invia allo strumento (Synth) di default. Stabiliamo ora un valore in beat per i tempi delta:

Pbind(\delta, 0.5).play; // (\etichetta, valore)

Come possiamo osservare la sintassi è la stessa già utilizzata per inviare parametri a un Synth attraverso il metodo .set():

Comprendiamo ora come Pbind(), più che per generare sequenze di eventi nel tempo come affermato nelle prime righe di questo Paragrafo, serva principalmente per inviare dinamicamente nel tempo valori a specifiche etichette o indirizzi definiti in precedenza. Utilizzando la stessa sintassi infatti, possiamo modificare qualsiasi tipo di parametro come ad esempio frequenze e ampiezze, oppure chiedere a SuperCollider di non utilizzare come strumento il Synth di default ma uno da noi programmato in precedenza (Custom Synth, come quelli già usati per Routine e Task). Spesso nei prossimi esempi riguardanti i Pattern, per similitudine con il suono generato attraverso Routine e per facilitare i paragoni tra le due sintassi, utilizzeremo questi ultimi al posto del Synth di default (le SynthDef sono all'inizio di questo Capitolo):

Synth(\sperc);

(
Pbind(\instrument,\sperc,     // specifica il Synth)
      \freq,      90.midicps, // frequenze (midinote)
      \amp,       64/127,     // ampiezze (velocity)
      \delta,     0.33        // tempo delta (beats)
      ).play;
)

Synth(\samp);

(
Pbind(\instrument,\samp, // specifica il Synth)
      \pos,      0.76,   // posizione puntatore
      \dur,      0.1,    // durata
      \amp,      0.6,    // ampiezza
      \delta,    0.33    // tempo delta (beats)
      ).play;
)

Attraverso la sintassi appena ilustrata possiamo però generare solamente sequenze infinite. Per il momento infatti abbiamo sempre interrotto la computazione con il comando da tastiera (ctrl+.). Se vogliamo invece realizzare sequenze finite di valori che si ripetono uguali nel tempo (come i tempi delta della tipologia di pulsazioni in oggetto) dobbiamo sostituire il singolo valore numerico con l’oggetto Pn().

Pbind(\delta, 0.5      ).play; // (\etichetta, valore)
Pbind(\delta, Pn(0.5,4)).play; // (\etichetta, pattern)

Valutando la seconda riga, dopo quattro pulsazioni la computazione si ferma automaticamente, infatti l’oggetto Pn() serve per ripetere n volte un numero, un altro Pattern o qualsiasi altro tipo di data. I suoi argomenti sono (valore, numero_di_ripetizioni). Così come per n.do({}) possiamo utilizzare la keyword inf per ottenere ripetizioni infinite (sintassi questa più propria rispetto a quella illustrata in precedenza):

Pbind(\dur, Pn(0.5,inf)).play;

Se vogliamo utilizzare valori temporali relativi, possiamo utilizzare i codici del Paragrafo precedente sostituendo le Routine con Pbind(). La sola limitazione al momento consiste nel fatto che se volessimo fermare una sequenza infinita usando il Synth di default lo potremmo fare solo con il comando ctrl+. e non dal codice:

(
b = 92;
t = TempoClock(b/60);
p = Pbind(\delta, Pn(0.5,inf)).play(t);
)

p.stop;  // non funziona...
t.clear; // non funziona...

Mentre se utilizziamo un Synth programmato da noi in modo proprio possiamo fermare la computazione anche dal codice:

(
b = 92;
t = TempoClock(b/60);
p = Pbind(\instrument,\sperc,  // Custom Synth
          \freq, 678,
          \amp, 0.5,
          \delta, Pn(0.5,inf)
).play(t);
)

p.stop; t.clear; // funziona...

Max

Scarica patch di Max

Laboratorio 1

Realizzare in SuperCollider o in Max uno tra i seguenti esercizi compositivi:

Metrica mensurale

Questo tipo di pulsazione corrisponde a una suddivisione del beat. L’insieme di un gruppo di frazioni deve essere contenuto all’interno di un singolo beat o di suoi multipli:

image not found

E’ la pulsazione che ha caratterizzato la storia della musica occidentale dagli esordi fino ai giorni nostri. Su di essa si basa tutta la teoria musicale occidentale, intesa sia come insieme di metodi per analizzare, classificare e comporre la musica e i suoi elementi, sia come relazioni tra la semiografia (notazione musicale) e la sua esecuzione. Il suo percorso storico parte idealmente dalla ritmica greca, passa per il canto gregoriano e viene sviluppata con l’avvento della polifonia e del mensuralismo. Codificata in teoremi di diversa natura, diventa un sistema unitario di organizzazione del tempo musicale che si sviluppa ulteriormente fino alle complesse vicende linguistiche del secondo ‘900. Può essere ulteriormente suddivisa in due sottocategorie: Pulsazione metrica mensurale regolare e Pulsazione metrica mensurale irregolare.

Metrica mensurale regolare

In questa sottocategoria sono presenti figure ritmiche caratterizzanti e riconoscibili all’ascolto (pattern) che si ripetono uguali o variate nel corso dell’intera sequenza:

image not found

Il beat è sempre percepito:

Più in generale tutte le forme di danza dal Rinascimento alla Techno Music. La notazione è quasi sempre tradizionale o proporzionale come ad esempio nella Sequenza VIII per Violino solo di L.Berio (qui un'analisi):

image not found

SC Routine

Così come per la precedente, possiamo realizzare questa tipologia di pulsazioni utilizzando diverse sintassi con una sola differenza: dobbiamo scomporre la programmazione in due distinti passaggi:

A tutti gli effetti possiamo paragonarla a una struttura a matrioske della tipologia precedente dove le pulsazioni regolari finite interne al beat possono cambiare o meno ad ogni beat della sequenza di pulsazioni regolari finite o infinite dei beats metronomici:

image not found

Cominciamo a realizzare il primo passaggio modificando leggermente una sintassi già conosciuta ovvero una Routine che genera un numero di eventi desiderato corrispondente a una suddivisione regolare di un singolo beat:

(
b = 92;
t = TempoClock(b/60);
r = Routine({var div, td;
                 div = rrand(1,6), // suddivisioni del beat
                 td  = 1/div;      // tempo delta corrispondente
             div.postln;
             div.do({              // numero di pulsazioni (ripetizioni)
                     Synth("sperc");
                     td.wait
                     })
}).reset.play(t)
)

r.stop;t.clear;

Come possiamo osservare, i tempi delta sono calcolati automaticamente dividendo l’unità (beat) per il numero di suddivisioni desiderato e, siccome il numero di suddivisioni coincide con il numero di pulsazioni da generare, utilizziamo la variabile che ne contiene il valore come argomento di .do().

Passiamo ora al secondo passaggio che possiamo realizzare attraverso due sintassi differenti:

SC Pattern

Per realizzare questo tipo di pulsazioni con i Patterns è meglio seguire una nuova strada e non elaborare il codice già conosciuto come abbiamo appena fatto con le Routine. Introduciamo un nuovo oggetto: Pstutter():

Pbind(\dur, Pstutter(4, 0.5)).play;

A prima vista potrebbe sembrare del tutto simile a Pn() con gli argomenti invertiti (n_ripetizioni, pattern), invece si differenzia da esso per una caratteristica fondamentale: se utilizzato in coppia con un altro oggetto che ancora non conosciamo (Pkey()), accetta valori in input:

(
Pbind(
      \sudd, 4,                                    // come una variabile locale
      \delta, Pstutter(Pkey(\sudd), 1/Pkey(\sudd)) // (n_puls, td)
      ).play
)

Analizziamo brevemente il codice precedente, alla riga 3, all’interno di Pbind() assegnamo un valore numerico (4) a un’etichetta (\sudd) il cui nome abbiamo scelto a nostro piacere esattamente come se fosse la dichiarazione e l’assegnazione di una variabile locale. Dopodichè, sempre all’interno dello stesso Pbind(), richamiamo due volte il valore assegnato utilizzando l’oggetto Pkey(\nome_etichetta) che diventa sia il numero di ripetizioni di Pstutter() sia il denominatore di 1 nella divisione che calcola automaticamente i tempi delta. Dopo quattro ripetizioni la computazione si ferma automaticamente. Per gli stessi motivi adotti per le Routine nel Paragrafo precedente sostituiamo il valore costante di ripetizioni assegnato a \sudd con l’oggetto Pwhite(). Otterremo così a ogni nuovo beat un numero di suddivisioni scelto pseudocasualmente tra un limite minimo e un limite massimo. Questo oggetto è infatti il corrispondente nei Patterns della funzione astratta rrand() e accetta tre argomenti: (minimo, massimo, numero_di_ripetizioni):

(
Pbind(
      \sudd,  Pwhite(1,8,inf),                     // esegue ogni n_puls
      \delta, Pstutter(Pkey(\sudd), 1/Pkey(\sudd)) // (n_puls, td)
      ).play

Il codice precedente genera sequenze infinite. Se vogliamo generare sequenze finite possiamo sostituire la parola riservata inf con un valore numerico tenendo però in considerazione il fatto che questo corrisponde al numero totale di pulsazioni da generare e non al numero di beats:

(
Pbind(
      \sudd,  Pwhite(1, 8, 10),                    // 10 pulsazioni non beats
      \delta, Pstutter(Pkey(\sudd), 1/Pkey(\sudd))
      ).play
)

Infine se vogliamo aggiungere un metronomo per verificare il codice, dobbiamo utilizzare un secondo Pbind(), realizzando in questo modo due strumenti polifonici separati ma sincronizzati allo stesso Clock:

(
b = 92;
t = TempoClock(b/60);

m = Pbind(\freq,  1480,        // metronomo Synth di default
          \amp,   0.3,
          \delta, Pn(1,inf)
          ).play(t);

p = Pbind(\instrument, \sperc, // pulsazioni Synth \sperc
          \freq,  80,
          \amp,   64,
          \sudd,  Pwhite(1,8,inf),
          \delta, Pstutter(Pkey(\sudd), 1/Pkey(\sudd))
          ).play(t)
)

m.stop;p.stop;t.clear;

Max

Scarica patch di Max

Laboratorio 2

Realizzare in SuperCollider o in Max il seguente esercizio compositivo:

  1. Scaricare l'audio file loop.wav

  2. Stabilire un minimo di quattro tagli ognuno dei quali può essere sia un singolo suono impulsivo che una breve sequenza ritmica.

  3. Realizzare una breve sequenza (un minuto circa) polifonica (tra 2 e 4 voci) costruita attraverso pulsazioni metriche mansurali e con le seguenti caratteristiche:

    • Possiamo impiegare una strategia sia deterministica che non deterministica,
    • Possiamo assegnare ad ogni singola voce un solo taglio (suono) in loop (uno differente per ogni voce) oppure,
    • Possiamo caratterizzare ogni singola voce con micro-sequenze rtmiche formate dalla successione di più tagli oppure,
    • Possiamo realizzare dei canoni ritmici formati da micro-sequenze di più tagli tra le voci,
    • Possiamo separare tra loro con pause più o meno lunghe e più o meno costanti sia i singoli tagli sia le micro-sequenze ritmiche.

Metrica mensurale irregolare

In questa tipologia è perseguito il risultato musicale opposto rispetto a quella precedente: una suddivisione irregolare e continuamente cangiante del beat che annulla la percezione di figure ritmiche riconoscibili all’ascolto e del beat stesso, relegandolo a semplice griglia temporale non espressa in millisecondi o secondi ma in bpm raggruppati o meno in misure musicali:

image not found

La notazione è quasi sempre tradizionale con indicazioni di tempo e misura:

Facciamo attenzione a non confondere i termini regolare e irregolare qui utilizzati per definire degli andamenti ritmici con il significato che assumono nella teoria musicale (gruppi regolari, gruppi irregolari).

SC Routine

Essendo anche in questo caso suddivisioni di una successione regolare di beats singolo beat con pause cominciamo col realizzare quelle di un singolo beat. Una possibile soluzione musicale per generare irregolarità è quella di inserire pause all’interno delle suddivisioni:

// --------------------------- indeterministico

(
b = 92;
t = TempoClock.new(b/60);
r = Routine.new({var div, td;
                     div = rrand(1,8);
                     td  = 1/div;
                 div.do({
                         if(rand(2) == 0,                          // test
                            {Synth("sperc");[div, "nota"].postln}, // vero
                            {[div, "pausa"].postln}                // falso
                            );
                         td.wait;
                         })
                }).reset.play(t)
)

// --------------------------- deterministico

(
b = 120;
t = TempoClock.new(b/60);
r = Routine.new({var seq;
                     seq = [0.25,0.25,-0.25,0.25,-0.5,0.333,-0.333,0.333]; // pause = valore negativo
                 seq.do({arg item;
                         if(item > 0,                       // test
                            {Synth("samp",[\pos,rand(2.0),\dur,rand(0.5)]); "nota".postln}, // vero
                            {"pausa".postln}                // falso
                           );
                 item.abs.wait;                             // valore assoluto (.abs)
                        })
                }).reset.play(t)
)

A questo punto, esattamente come per la tipologia precedente possiamo:

SC Pattern

Per realizzare questo tipo di pulsazione attraverso i Patterns dobbiamo allargare nuovamente il nostro vocabolario dedicato. Nei paragrafi precedenti abbiamo visto che alcuni nomi di parametri usati comunemente come etichette all’interno di un Pbind() sono delle parole riservate o keyword ovvero hanno un significato preciso e non possiamo utilizzarle per altri fini (in realtà a livello strettamente informatico la questione è più complessa e la affronteremo in seguito, ma per gli scopi musicali che perseguiamo in questo Paragrafo possiamo dare per buona questa affermazione). Fino a questo punto abbiamo incontrato: \instrument,\freq,\amp e \dur. Aggiungiamo ora \type. Questa etichetta dice a SuperCollider che sta per essere definito il tipo di evento (Event types) da generare e al posto di valori o pattern accetta diversi simboli, ognuno dei quali specifica appunto il tipo di evento. Quelli che ci interessano in questo frangente sono:

(
Pbind(\dur, 0.5,
      \type, \note // genera note
).play
)

(
Pbind(\dur, 0.5,
      \type, \rest // genera pause...
).play
)

Proseguiamo ora nella programmazione di questa tipologia di pulsazioni seguen- do lo stesso costrutto logico che abbiamo utilizzato per generare pause in modo pseudo-casuale con le Routine. Come abbiamo già intuito molti oggetti di SuperCollider hanno un loro alias nella famiglia dei Patterns. Pif() è l’alias di if() e ha gli stessi argomenti con una sintassi leggermente variata: vero e falso non sono funzioni, ma simboli, valori o altri Patterns. Siccome il test che stabilisce se eseguire una nota o fare una pausa deve essere effettuato a ogni passo (Evento) dobbiamo utilizzare l’oggetto Pfunc(). Questo valuta a ogni nota (Evento) la funzione scritta al suo interno. Dopodichè in questo caso se il test è vero esegue una nota (\note), altrimenti fa una pausa (\rest):

(
Pbind(
      \sudd,  Pwhite(1,8,inf),
      \delta, Pstutter(Pkey(\sudd), 1/Pkey(\sudd)),
      \type,  Pif(Pfunc({rand(2)==0}),\note,\rest), // nota o pausa
).play;
)

Infine così come abbiamo fatto per le tipologie di pulsazioni precedenti confron- tiamolo con un metronomo per monitorare la correttezza del codice:

(
b = 92;
t = TempoClock(b/60);

m = Pbind(\freq, 2637,         // metronomo Synth di default
          \amp,  0.3,
          \delta,  Pn(1,inf)).play(t);

p = Pbind(\instrument, \sperc, // pulsazioni Synth \sperc
          \freq, 90,
          \amp,  60,
          \sudd, Pwhite(1,8,inf),
          \delta,Pstutter(Pkey(\sudd), 1/Pkey(\sudd)),
          \type, Pif( Pfunc({rand(2)==0}), \note, \rest),
          ).play(t)
)

m.stop;p.stop;t.clear;b.free;

Max

Scarica patch di Max

Laboratorio 3

Realizzare il seguente esercizio compositivo:

  1. Scaricare l'audio file bach.wav.

  2. Tagliare il file in sequenze di 2, 3, 4, 5, 6, 7 note.

  3. Stabilire il beat metronomico in bpm.

  4. Modificare alcuni di questi tagli attraverso un time stretching senza alterazione del pitch in modo che la loro durata sia di un singolo beat.

  5. Inserire eventualmente pause a piacere nelle sequenze irregolari ottenute nel punto precedente utilizzando automazioni del gain.

  6. Esportare (salvare) i singoli files ottenuti.

  7. Caricare in un sfplay~ stereofonico di Max tutti i files.

  8. Realizzare un playback iterandoli tra loro in modo deterministico o indeterministico. Tenere presente che possono anche essere separati da pause, ma che queste dovranno avere una durata corrispondente a un multiplo o frazione del beat originale.

Metrica non mensurale

Anche questo tipo di pulsazione corrisponde a una suddivisione del beat. L’insieme di un gruppo di frazioni non deve però poter essere raggruppato all’interno di un beat o di suoi multipli:

E’ simile alla precedente tipologia ma non ”imbrigliata” all’interno di misure. Si presta a operazioni musicali di ritmica polimetrica e a creare brevi sequenze ritmiche percettivamente riconoscibili, ripetute più volte in loop (patterns irregolari). Nel trascriverla in notazione musicale spesso è utilizzata una scrittura in campo aperto o una notazione proporzionale con o senza indicazione di misure:

image not found

Il beat (irregolare) può essere percepito:

oppure meno:

La sua realizzazione nel linguaggio informatico è decisamente più semplice rispetto alla precedente tipologia e può essere facilmente realizzata con le sintassi che già conosciamo.

SC Clock

Possiamo utilizzare il metodo .sched() invocato su un Clock come abbiamo fatto per le pulsazioni regolari sia per generare sequenze finite che infinite. A ogni pulsazione il tempo delta corrisponderà al valore ottenuto come risultato dell’operazione matematica 1/rrand(1,8) come abbiamo fatto per le pulsazioni metriche, infatti questa tipologia di pulsazione è formata anch’essa da suddivisioni del beat ma libere e autonome, ovvero non necessariamente comprese, finite o cadenzate all’interno di ogni singola unità. Percettivamente può risultare simile se non identica alla pulsazione metrica mensurale irregolare ma sotto il punto di vista musicale appartengono a due pensieri differenti.

// Sequenze finite
(
b = 92;
n = 10;
t = TempoClock.new(b/60)
 .sched(0, {"beat--------".postln; 1})
 .sched(0, {"pulsa".postln;
            Synth("sperc"); // pulsazione
            1/rrand(1,8)
            })
 .sched(n, {t.clear; nil})  // stop dopo 10 beats
)

// Sequenze infinite
(
b = 92;
t = TempoClock(b/60)
 .sched(0, {"pulsa".postln;
            Synth("samp",[\pos,1.3,\dur,0.2]); // pulsazione
            rrand(1,8).reciprocal              // 1/n
            })
)

t.clear;

Due cose da notare nei codici precedenti:

SC Routine

Anche nella realizzazione con Routine possiamo utilizzare un codice già conosciuto sia per generare sequenze finite che infinite:

// Sequenze finite
(
b = 92;
n = 10;
t = TempoClock.new(b/60);
r = Routine.new({
                 n.do({var td = 1/rrand(1,8);
                           td.postln;
                       Synth("sperc");
                       td.wait})
                 }).reset.play(t)
)

// Sequenze infinite
(
b = 92;
n = inf;
t = TempoClock(b/60);
r = Routine.new({
                 n.do({var td = rrand(1,8).reciprocal;
                           td.postln;
                       Synth("samp",[\pos,2.8,\dur,td*(60/b)]);
                       td.wait})
                }).reset.play(t)
)

r.stop; t.clear; n.free; b.free;

Notiamo come la durata della sequenza (a differenza della realizzazione con .sched()) in questo caso venga specificato come numero di pulsazioni e non come numero di beats, inoltre la scelta pseudo-randomica dei tempi delta è assegnata a una variabile locale posta nella funzione all’interno di n.do(). Quest’ultima è una semplice variazione didattico-dimostrativa.

SC Pattern

Infine anche con i Patterns il codice non cambia. Notiamo solamente come i Patterns abbiano una sintassi decisamente più pulita rispetto alle Routine.

// Sequenza finite
(
b = 92;
t = TempoClock(b/60);
n = 10;
p = Pbind(\instrument, \sperc,
          \freq, 900,
          \amp,  0.5,
          \delta, 1/Pwhite(1,8,n) // 1/n
          ).play(t)
)
)

// Sequenza infinita
(
b = 92;
t = TempoClock(b/60);
n = inf;
p = Pbind(\instrument, \samp,
          \pos, Pwhite(0,3.0,n),
          \dur, 0.3,
          \delta, 60/b * Pwhite(1,8,n).reciprocal // n.reciprocal
          ).play(t)
)

p.stop; t.clear; n.free; b.free;

Max

Scarica patch di Max

Laboratorio 4

Realizzare il seguente esercizio compositivo:

  1. Scaricare gli audio files contenuti nella cartella voci.

  2. Caricare in due sfplay~ mono di Max tutti i files (o una selezione).

  3. Realizzare un playback iterando tra loro i files in modo deterministico o indeterministico, l'output di un sfplay~ uscirà dal canale sinistro mentre quello del secondo dal destro.

  4. I due altoparlanti devono rappresentare due personaggi immaginari e immaginifici che dialogano tra loro in un idioma incomprensibile.

Irregolare

Questo tipo di pulsazione corrisponde a una suddivisione irregolare assoluta di un tempo misurato in secondi o millisecondi. Tra una pulsazione e l’altra non c’è alcun tipo di relazione e rappresenta in un certo senso l’assenza se non la negazione della pulsazione. In alcuni casi, essendo percettivamente indifferente, possiamo scegliere una procedura randomica o stocastica nell’assegnare i singoli valori ai parametri temporali (tempi delta e durate), impiegando o meno curve di distribuzione delle probabilità (o maschere di tendenza) he definiscono la densità di eventi nel tempo:

La notazione può essere tradizionale in campo aperto:

image not found

oppure grafico/geroglifica come ad esempio nei tre significativi brani October ’52, November ’52, December ’52 di E.Brown:

image not found

Il beat non deve essere in alcun modo percepito, anche in presenza di un ritmo. La realizzazione informatica di questa tipologia di pulsazione è molto semplice e possiamo realizzarla con le sintassi che già conosciamo.

SC Clock

Se scegliamo una misurazione del tempo in secondi possiamo utilizzare lo scheduling di SystemClock() generando a ogni pulsazione i tempi delta con rrand(), senza utilizzare il metodo .reciprocal o l’operazione 1/rrand() in quanto non c’è alcun beat da suddividere:

(
t = SystemClock             // tempo assoluto in secondi
 .sched(0, {Synth("sperc");
            rrand(0.01,0.5) // td tra 0.01 e 0.5 secondi
            })
)

t.clear

Possiamo ottenere sequenze finite con le sintassi già conosciute, mentre se vogliamo misurare il tempo in millisecondi possiamo sostituire SystemClock con TempoClock(1000). La sola variazione al codice (anche in questo caso didattico-dimostrativa) consiste nell’aver assegnato i limiti minimo e massimo a due variabili locali così da visualizzarli meglio nel caso volessimo testare diversi valori.

(
t = TempoClock(1000) // tempo assoluto in ms 3   
 .sched(0, {var min = 50,
                max = 500;   // td tra 50 e 500 ms
            Synth("sperc");
            rrand(min, max)
            })
 .sched(4000, {t.clear;nil}) // stop dopo 4000 ms (finito)
)

SC Routine

Effettuando la stessa piccola modifica del codice già illustrato per la pulsazione metrica non mensurale possiamo realizzare questa tipologia anche con le Routine:

// Sequenze finite
(
n = 10;
t = TempoClock.new(1);
r = Routine.new({
                 n.do({var td = rrand(0.01,0.5);
                           td.postln;
                       Synth("sperc");
                       td.wait})
                 }).reset.play(t)
)

// Sequenze infinite
(
n = inf;
t = TempoClock(1000);
r = Routine.new({
                 n.do({var td = rrand(50,750);
                           td.postln;
                       Synth("samp",[\pos,2.8,\dur,td*0.01*0.8]);
                       td.wait})
                }).reset.play(t)
)

r.stop; t.clear; n.free; b.free;

// Patterns irregolari
(
n = [20,300,154,75,987];
t = TempoClock(1000);
r = Routine.new({
                 n.do({arg item;
                       Synth("samp",[\pos,2.8,\dur,item*0.01*0.8]);
                       item.wait})
                }).reset.play(t)
)

SC Pattern

Oppure con i Patterns:

// Sequenze finite
(
b = 92;
t = TempoClock(b/60);
n = 10;
p = Pbind(\instrument, \sperc,
          \freq, 1234,
          \amp,  0.6,
          \delta, Pwhite(0.1,1,n)
          ).play(t)
)

// Sequenze infinite
(
t = TempoClock(1000);
n = inf;
p = Pbind(\instrument, \samp,
          \pos,  Pwhite(0.0,3.0,n),
          \dur,  0.2,
          \delta, Pwhite(25,1000,n)
).play(t)
)

p.stop;t.clear

Max

Scarica patch di Max

Sequenze deterministiche

In conclusione di questo paragrafo osserviamo come attraverso le tecniche di sequencing già illustrate in precedenza possiamo realizzare qualsiasi tipo di pulsazione specificando in modo deterministico i valori di durate e tempi delta in liste o array:

SC Routine

Possiamo ad esempio specificare solo la durata ed utilizzare valori negativi per le pause:

(
var reg,metMensReg,metMensIrreg,noMens,irreg,seq,id;

    reg          = [0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5];
    metMensReg   = [0.25,0.25,0.25,0.25,1,0.333,0.333,0.333,0.5,-0.5,0.25,0.25,0.25,0.25,1,0.333,0.333,0.333,1];
    metMensIrreg = [-0.5,0.5,0.2,-0.2,0.2,0.2,-0.2,0.2,-0.75,0.25,1,-0.5,0.5,0.2,0.2,0.2,0.2,0.2,0.2,0.75,0.25,1]; 
    noMens       = [0.25,0.5,1,-0.125,0.333,-0.333,0.2,0.5,1,-0.2,0.2,0.333,-0.333,0.2,0.5];
    irreg        = [0.234,-0.526,0.123,-0.7,1.54,0.12,-0.34,0.456,-0.234,0.526,0.123,0.7,-1.54,0.12,-0.34,0.456];

    seq          = [reg, metMensReg, metMensIrreg, noMens, irreg].choose; // scelta random tra sequenze determinate
//                    0       1             2         3       4

    seq.postln;

b = 100;
t = TempoClock(b/60);
r = Routine.new({
                 seq.do({arg item;
                         if(item > 0, {Synth("samp",[\pos,item,\dur,item]); "nota".postln});
                            item.abs.wait})
                         }).reset.play(t);
)

r.stop; t.clear; b.free;

Oppure utilizzare Array sincronizzati per tutti i parametri desiderati:

(
var dt,dur,pos;

    dt  = [0.25,0.25,0.25,0.25,1.0,0.333,0.333,0.333,0.5,0.5,0.25,0.25,0.25,0.25,1,  0.333,0.333,0.333,1];
    dur = [1,   0.75,0.5, 0.25,0.5,1,    0.5,  1,    1,  0.5,1,   1,   1,   1,   1,  0.25, 0.5,  1,    1]; // % dt
    pos = [0.25,0.5, 1,   2.15,1.3,0.45, 0.57, 0.5,  1.5,0.2,0.2, 2.34,1.33,0.2, 0.5,0.23, 1.56, 2.34, 1];

b = 52;
t = TempoClock(b/60);
r = Routine.new({
                 dt.do({arg item,id;
                        var tabs = item * dur[id] * (60/b);    // durata assoluta da inviare

                        Synth("samp",[\pos,pos[id],\dur,tabs]);
                        item.wait})
                        }).reset.play(t);
)

r.stop; t.clear; b.free;

SC Pattern

Array e List possono essere letti anche dai Pattern ma prima di introdurre nel dettaglio gli oggetti che compiono questa operazione dobbiamo focalizzare due concetti:

Pattern vs Stream

Per meglio comprendere cominciamo da un esempio astratto: generare una sequenza numerica da 0 a infinito con incremento di 1 a ogni passo (valutiazione). Programmiamolo sia con una Routine che con un Pattern:

(
r = Routine({
             var i = 0;
             {i.yield;       // stop e stampa
              i = i+1}.loop; // contatore
             });
)

r.next;                      // valuta un passo alla volta

p = Pseries(0,1,inf);
p.next;                      // riporta 'a Pseries', non i valori

Le Routine come risulta evidente dall’esempio precedente sono strutture di controllo che possono essere fermate per poi riprendere dal punto in cui sono state interrotte (a ogni passo il valore della variabile i viene tenuto in memoria e aggiornato). Questo è un esempio di uno Stream che può essere definito come la rappresentazione di una sequenza di valori incrementali ottenuti dall’invocazione del metodo .next e che può ripartire da capo invocando il metodo .reset. I Patterns invece sono come il progetto di una casa, le piantine e gli spezzati, non la casa stessa, che sarà creata solo nel momento in cui gli operai la costruiranno passo dopo passo seguendo i progetti. Non hanno il concetto di stato corrente. Possiamo però trasformare i Patterns in Streams invocando su di loro il metodo .asStream:

(
r = Routine({
             var i = 0;
             {i.yield;
              i = i + 1}.loop;
              })
)

r.nextN(10); // esegue 10 volte '.next'

p = Pseries(0,1,inf).asStream;
p.nextN(10); // esegue 10 volte '.next'

Se come in questo caso c’è un loop infinito all’interno di una Routine possiamo utilizzare il metodo .nextN() per ottenere un Array con i valori generati dal numero di valutazioni specificate come argomento. In pratica, i Patterns definiscono le azioni, gli Streams le eseguono un po come la differenza tra una partitura e la sua esecuzione, una Classe e un’istanza da essa derivata. Questa separazione di compiti ci permette ad esempio di generare Streams diversi dallo stesso Pattern:

p = Pseries(0,1,inf);
u = p.asStream;
u.next;
f = p.asStream;
[u.next, f.next];

Eventi e prototipi

Abbiamo visto in precedenza come Pbind() possa essere considerato un modo per inviare dinamicamente valori a nomi (backslashkey). Inoltre abbiamo appurato che ogni singolo evento temporale (azione) può essere definito da un insieme di questi. Se per esempio consideriamo l’azione di eseguire una singola nota musicale possiamo specificare numerosi parametri tra i quali lo strumento che la deve eseguire, l’altezza, l’intensità, la durata, etc. In questo caso, l’insieme di coppie \nome, valore è un oggetto chiamato Event. Un Event è dunque un insieme di comandi che specificano un azione da compiere. Se trasformiamo un Pbind() in uno Stream possiamo richiamare un Event per volta invocando il metodo .next(Event.new). In questo caso il metodo .new non può essere sottinteso. Possiamo anche utilizzare l’abbreviazione sintattica .next(()):

(
p = Pbind(\freq, Pwhite(60,90,inf).midicps,
          \amp,  Pwhite(60,90,inf),
          ).asStream;
)

p.next(Event.new);
p.next(());       // abbreviazione sintattica

Osserviamo come usando l’abbreviazione sintattica l’ordine sia invertito.

Se sostituiamo Pwhite() con Pseq([]) e leggiamo gli eventi nella Post window, dovrebbe risultare estremamente chiaro cosa fa Pbind():

(
p = Pbind(\freq, Pseq(#[60,61,62,63].midicps,1),
          \amp,  Pseq(#[64,74,84,94]/127,1),
          ).asStream;
)

p.next(());

L’oggetto Pseq([]) definisce un Pattern che creerà uno Stream che itererà un Array. Ha due argomenti:

(
p = Pbind(\freq, Pseq(#[60,61,62,63].midicps,inf),
          \amp,  Pseq(#[64,74,84,94]/127,inf),
          ).asStream;
)

p.next(());

Fino a questo punto abbiamo solo generato data trasformando Pbind() in Stream e richiamando un Event alla volta. Sappiamo però che così come per le Routine anche su Pbind() possiamo invocare il metodo .play:

Pbind().play;

Eseguendo il codice precedente, oltre che udire dei suoni leggeremo nella Post window la scritta an EventStreamPlayer. Questo perchè ogni volta che invochiamo il metodo .play su un Pattern un oggetto così chiamato viene generato e legge gli Event uno dopo l’altro, separati da un tempo delta (\delta) espresso in beats e non in secondi. Nell’esempio precedente però nessun parametro è specificato da coppie \key, value e, anche se sappiamo già che in questi casi Pbind() adotta i valori di default, non conosciamo quali. Possiamo ottenere questa informazione nella Post window valutando la riga seguente che è la rappresentazione di un singolo Event:

('freq': 440, 'amp': 0.1).play;

L’insieme delle azioni riportate è definito un event prototype. Questi per essere tale deve includere una funzione (’msgFunc’: a Function) che viene eseguita quando invochiamo il metodo .play. Gli altri parametri di default sono specificati da un evento di default che è stato da altri già programmato. Contiene le istruzioni per suonare un Synth, un insieme di coppie \key, value predefinite che ne definiscono i parametri di default e le più comuni conversioni e manipolazioni di questi.

Event.default;

Riassumendo

Un esempio di loop infinito:

(
p = Pbind(\instrument, \sperc,
          \freq, Pseq(#[60,61,62,63].midicps, inf),
          \amp,  Pseq(#[34,54,84,104]/127,    inf),
          ).play;
)

p.stop

Se invece vogliamo denerare una sequenza finita ma all’interno dello stesso Pbind() gli Array inclusi in due o più Pseq([]) sono di lunghezza (.size) differente oppure se il numero di ripetizioni non è lo stesso, Pbind() interrompe la sequenza esauriti gli eventi di quello con minor lunghezza:

(
Pbind(\instrument, \sperc,
      \freq, Pseq(#[60,61,62,63,64,65,66].midicps, 1),
      \amp,  Pseq(#[34,54,84,104]/127,             1),
      ).play;
)

(
Pbind(\instrument, \sperc,
      \freq, Pseq(#[60,61,62,63].midicps, 2), // 2 ripetizioni
      \amp,  Pseq(#[34,54,84,104]/127,    1), // 1 ripetizione
      ).play;
)

Se però specifichiamo inf nella sequenza con lunghezza minore, questa sarà letta ciclicamente da sinistra a destra fino al’esaurirsi degli eventi della sequenza con lunghezza maggiore:

(
Pbind(\instrument, \sperc,
      \freq, Pseq(#[60,61,62,63,64,65,66,67].midicps, 1),
      \amp,  Pseq(#[34,54,84,104]/127,              inf),
      ).play;
)

Per quanto riguarda pause e durate possiamo specificare le prime nell'Array delle frequenze con la keyword \rest mentre le seconde con una Pseq dedicata assegnata all'etichetta di default \dur. Se ci sono pause dovremo specificare la loro durata esattamente come per le note:

(
Pbind(\instrument, \sperc,
      \freq, Pseq(#[60,61,\rest,62,63,64,\rest,65,66,67].midicps, 1),
      \amp,  Pseq(#[34,54,84,104]/127, inf),
      \dur,  Pseq(#[0.2,0.5,1,0.55,0.74,1], inf)
      ).play;
)

Max

Scarica patch di Max

Laboratorio 5

Realizzare in SuperCollider il seguente esercizio compositivo:

  1. Scaricare l'audio file bach.wav.

  2. Caricarlo in un Buffer.

  3. Stabilire sulla carta almeno otto frammenti del file (onsets d'inizio e durata di ogni singolo frammento) le cui durate possono essere comprese tra 0.02 secondi e 2 secondi ed essere approssimate a tempi mensurali o meno, a seconda delle scelte che compiremo al punto 5.

  4. Realizzare con Routine o Pattern un loop finito o infinito iterando tra loro i frammenti in modo deterministico (ragguppando in diversi Arrays onsets, durate e ampiezze di ognuno) che dovranno essere suonati da un Synth derivato dalla SynthDef \samp. Tenere presente che i frammenti possono anche essere separati da pause.

  5. L'organizzazione temporale dei frammenti nel loop dovrà corrispondere ad una sola tipologia di pulsazioni scelta tra quelle illustrate in precedenza.

  6. La sequenza potrà essere sia monofonica che polifonica (un Synth per ogni voce) ma nel secondo caso tutte le voci dovranno svolgersi nella stessa tipologia di pulsazione generando in questo modo diverse textures ritmiche.