Inviluppi dinamici

Prima di addentrarci nella descrizione delle diverse possibilità che abbiamo a disposizione per modificare dinamicamente i parametri degli inviluppi (così come abbiamo fatto per l'ampiezza) poniamo l'attenzione su tre argomenti che ci torneranno utili nello sviluppo dei temi successivi:

  1. Possiamo utilizzare un'abbreviazione sintattica che unisce idealmente EnvGen con Env trasformando una BPF in segnale:

    s.boot;
    s.scope(1);
    s.meter(1,1);
    
    (
    SynthDef(\envi,
                  {arg freq=890, t_gate=0;       
                   var sig,env;
                       sig = SinOsc.ar(freq);
                       env = Env.new([0,1,0.3,0],[0.01,0.2,0.3],\cub).kr(doneAction:0, gate:t_gate);
                   Out.ar(0,sig*env)
                   }
              ).add;
    
    {a = Synth(\envi)}.defer(0.1);  
    )
    
    a.set(\t_gate,1);
    

    Personalmente trovo questa abbreviazione sintattica più chiara del codice scritto per esteso e la adotteremo negli esempi seguenti.

  2. Nei codici di esempio precedenti e in quelli seguenti abbiamo utilizzato quasi sempre una scelta randomica dei valori (non deterministica). Questa scelta è stata fatta esclusivamente per scopi esemplificativi, tutto quanto esposto vale anche per l'utilizzo di tecniche deterministiche.

  3. Abbiamo visto come ci siano tre diversi modi di specificare i valori temporali di un inviluppo:

    • un'Array di tempi delta [1,1,1] come nel caso di Env.new, Env.perc, Env.adsr, etc.
    • un'Array di onsets [0,1,2] come nel caso di Env.pair e Env.xyc
    • la Durata totale come nel caso di Env.triangle e Env.sine

    Tra questi il modo più vicino al pensiero musicale tradizionale è quest'ultimo ovvero specificare la durata totale di un suono:

    (
    SynthDef(\envi,
                  {arg dur=0.1,t_gate=0;
                   var sig,amp;
                       sig  = SinOsc.ar(1200);
                       amp  = Env.triangle(dur).kr(0,t_gate);
                    Out.ar(0,sig*amp)
                    }
              ).add;
    
    {a = Synth(\envi)}.defer(0.1);  
    )
    
    a.set(\dur,rrand(0.01,1),\t_gate,1); 
    

    ma, siccome non tutti i metodi dedicati agli inviluppi accettano questo parametro, ricordiamo alcune utili conversioni di valori temporali tra le diverse modalità appena esposte:

    [1,2,3,4].sum             // Tempi delta --> durata totale
    [1,2,3,4].normalizeSum    // Riscala tutti i valori tra 0 e 1
    
    [1,2,3,4].integrate       // Tempi delta --> onsets
    [0]++[1,2,3,4].integrate; // aggiunge il tempo 0 (inizio
    
    [0,1,3,6,10].drop(1);     // rimuove il primo item (tempo 0)
    [1,3,6,10].differentiate  // Onsets --> tempi delta
    

Esattamente come abbiamo fatto per le ampiezze possiamo modificare dinamicamente nel tempo tutti i parametri di un invilupo utilizzando le stesse tecniche che già conosciamo.

Client side

La prima consiste nell'inviare dall'Interprete al Server i diversi parametri con il messaggio .set(). Ognuno dei tre parametri principali degli inviluppi ha delle specificità. Vediamole nel dettaglio:

Livelli

Se vogliamo controllare dinamicamente i livelli degli inviluppi inviando i valori dall'Interprete al Synth (Server) abbiamo a disposizione due tecniche (o modi di pensare) differenti, ognuna è propria di una specifica esigenza musicale:

  1. modificare dinamicamente tutti i livelli specificandoli in valori assoluti (così come abbiamo fatto finora):

    s.scope(1)
    s.meter(1,1);
    
    (
    s.waitForBoot{
    
    // ------------------------------ SynthDef e Synth
    
    SynthDef(\envi,
                  {arg t_gate=0,liv=#[0,0.65,0.43,0];      // literal Array...
                   var sig,env;
                       sig = SinOsc.ar(986);
                       env = Env.new(liv,[0.02,0.3,0.2],\cub).kr(0,t_gate);
                    Out.ar(0,sig*env)
                    }
              ).add;
    
    {a = Synth(\envi)}.defer(0.1);
    
    // ------------------------------ GUI
    
    w = Window.new("EnvelopeView", 500@400);
    w.alwaysOnTop;
    w.onClose_({e.free;w.free;r.stop;});
    w.front;
    e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
    
    // ------------------------------ Sequencing
    
    r = Routine.new({
                     inf.do({var liv;
                                 liv = [0]++({rand(1.0)}!2)++[0]; // livelli random ad eccezione
                                                                  // del primo e dell'ultimo
                             a.set(\liv,liv,\t_gate,1);           // invia al Synth
    
                             {e.value_([                          // invia all'interfaccia
                                       [0]++[0.02,0.3,0.2].normalizeSum.integrate, // onsets tra 0 e 1                              
                                       liv                                         // livelli
                                       ])
                             }.defer(0);                                  
    		
                             0.6.wait // Deve essere >= della durata dell'inviluppo
                             })
                     }).reset.play;
    }
    )
    
    r.stop;
    

    Due cose da notare:

    • Nella SynthDef l'Array specificato come argomento di default è un Literal Array.

    • Se un trigger arriva prima che l'inviluppo sia finito questo riparte dal valore in cui si trova.

  2. modificare dinamicamente solo l'ampiezza di picco lasciando invariata la forma dell'inviluppo. In questo caso dobbiamo specificare tutti i livelli in valori relativi (proporzioni) con 0 come minimo e 1 come massimo per poi moltiplicarli per un fattore di ampiezza così come abbiamo fatto per gli inviluppi con forma d'onda fissa (triangolare, sinusoidale, trapezoidale, etc).

    (
    s.waitForBoot{
    
    // ------------------------------ SynthDef e Synth
    
    SynthDef(\envi,
                  {arg t_gate=0,amp=0;
                   var sig,env;
                       sig = SinOsc.ar(1986);
                       env = Env.new([0,1,0.35,0],[0.02,0.02,0.2],\cub).kr(0,t_gate)*amp; // min=0 max=1 * ampiezza                 
                    Out.ar(0,sig*env)
                    }
              ).add;
    
    {a = Synth(\envi)}.defer(0.1);
    
    // ------------------------------ GUI
    
    w = Window.new("EnvelopeView", 500@400);
    w.alwaysOnTop;
    w.onClose_({e.free;w.free;r.stop;});
    w.front;
    e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
    
    // ------------------------------ Sequencing
    
    r = Routine.new({
                     inf.do({var amp=rand(1.0);
                             a.set(\amp,amp,\t_gate,1);
    
                            {e.value_([                                   
                                       [0]++[0.02,0.02,0.2].normalizeSum.integrate,                               
                                       [0,1,0.35,0]*amp                                
                                       ])
                             }.defer(0);                                  
    
                             0.6.wait
                             })
                     }).reset.play;
    }
    )
    
    r.stop;
    

Tempi

Anche per quello che riguarda i cambiamenti dinamici del tempo o dei tempi di un'inviluppo abbiamo a disposizione le stesse due modalità che abbiamo appena visto per i livelli:

  1. modificare dinamicamente tutti i tempi dei singoli nodi specificandoli in valori assoluti (così come abbiamo fatto finora): In questo caso dobbiamo però distinguere tra i diversi tipi di inviluppo:

    • Inviluppi con i tempi specificati in tempi delta come Env.new() e Env.circle():

      (
      s.waitForBoot{
      
      // ------------------------------ SynthDef e Synth
      
      SynthDef(\envi,
                    {arg t_gate = 0,
                         liv    = #[0,0.65,0.34,0.78,0],
                         delta  = #[0.1,0.3,0.4,0.5]; 
                     var sig,env;
                         sig = SinOsc.ar(986);
                         env = Env.new(liv,delta,\cub).kr(0,t_gate);
                         sig = SinOsc.ar(986);
                     Out.ar(0,sig*env)
                     }
                ).add;
      
      {a = Synth(\envi)}.defer(0.1);
      
      // ------------------------------ GUI
      
      w = Window.new("EnvelopeView", 500@400);
      w.alwaysOnTop;
      w.onClose_({e.free;w.free;r.stop;});
      w.front;
      e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
      
      // ------------------------------ Sequencing
      
      r = Routine.new({
                       inf.do({var amp,delta,dur;
                                   amp   = [0.8,0.3,0.5];      //{rand(1.0)}!3; 
                                   delta = {rrand(0.01,1)}!4;
                                   dur   = delta.sum;         // Durata totale
      
                               a.set(\liv,[0]++amp++[0],      // invia i valori al Synth
                                     \delta, delta,
                                     \t_gate, 1
                                     );
      
                              {e.value_([                    // invia i valori all'interfaccia
                                         [0]++delta.normalizeSum.integrate, // onsets tra 0 e 1
                                         [0]++amp++[0]
                                         ])
                               }.defer(0);                    // AppClock
      
                              dur.wait
      	                    })
                       }).reset.play;
      }
      )
      
      r.stop
      
    • Inviluppi con i tempi specificati in onsets come Env.pairs() e Env.xyc():

      (
      s.waitForBoot{
      
      // ------------------------------ SynthDef e Synth
      
      SynthDef(\envi,
                    {arg t_gate = 0,
                         n1=#[0,0.001], // SynthDef non accetta Array 2D...
                         n2=#[0.2,1],
                         n3=#[0.5,0.6],
                         n4=#[2,0.001]; 
                     var sig,bpf,env;
                         sig = SinOsc.ar(986);
                         env = Env.pairs([n1,n2,n3,n4],\cub).kr(0,t_gate);
                     Out.ar(0,sig*env)
                      }
                ).add;
      
      {a = Synth(\envi)}.defer(0.1);
      
      // ------------------------------ GUI
      
      w = Window.new("EnvelopeView", 500@400);
      w.alwaysOnTop;
      w.onClose_({e.free;w.free;r.stop;});
      w.front;
      e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
      
      // ------------------------------ Sequencing
      
      r = Routine.new({
                       inf.do({var amp,tempi,dur,xy,tempi_e;
                                   amp   = [0,0.7,0.3,0];
                                   tempi = {1.0.rand}!4;       // Array di tempi delta
                                   tempi = tempi.putFirst(0);  // primo = 0
                                   tempi_e = tempi.normalizeSum.integrate; // per visualizzazione
                                   tempi = tempi.integrate;    // Tempi delta -->  onsets
                                   dur   = tempi.last;         // Durata globale
                                   xy   = [tempi,amp].flop;    // Crea Array 2D dei nodi
                                   xy.postln;                  // Stampa
      
                               a.set(                          // invia i valori al Synth
                                     \n1, xy[0],
                                     \n2, xy[1],
                                     \n3, xy[2],
                                     \n4, xy[3],
                                     \t_gate, 1
                                     );
      
                              {e.value_([tempi_e, amp])}.defer(0); // invia i valori all'interfaccia
      
                              dur.wait
                              })
                       }).reset.play;
      }
      )
      
      r.stop
      
    • Inviluppi con i tempi specificati in durata totale come Env.triangle(), Env.sine() e Env.perc():

      (
      s.waitForBoot{
      
      // ------------------------------ SynthDef e Synth
      
      SynthDef(\envi,
                    {arg t_gate=0,
                         dur=1;
                     var sig,env;
                         sig = SinOsc.ar(986);
                         env = Env.triangle(dur).kr(0,t_gate);
                     Out.ar(0,sig*env)
                     }
                ).add;
      
      {a = Synth(\envi)}.defer(0.1);
      
      // ------------------------------ GUI
      
      w = Window.new("EnvelopeView", 500@400);
      w.alwaysOnTop;
      w.onClose_({e.free;w.free;r.stop;});
      w.front;
      e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
      
      // ------------------------------ Sequencing
      
      r = Routine.new({
                       inf.do({var dur;
                               dur=rrand(0.1,1);
                               a.set(\dur, dur,\t_gate, 1);
       
                              {e.setEnv(Env.triangle(dur))}.defer(0); // .setEnv() al posto di .value_()
      
                              dur.wait
                              })
                       }).reset.play;
      }
      )
      
      r.stop
      
    • Inviluppi con i tempi specificati in valori legati alle loro caratteristiche come Env.linen(), Env.asr() e Env.adsr():

      (
      s.waitForBoot{
      
      // ------------------------------ SynthDef e Synth
      
      SynthDef(\envi,
                    {arg t_gate = 0,
                         atk=0.02,
                         sust=0.5,
                         rel=0.02,
                         amp=0;
                     var sig,env;
                         sig = SinOsc.ar(986);
                         env = Env.linen(atk,sust,rel,amp,\cub).kr(0,t_gate);
                     Out.ar(0,sig*env)
                     }
                ).add;
      
      {a = Synth(\envi)}.defer(0.1);
      
      // ------------------------------ GUI
      
      w = Window.new("EnvelopeView", 500@400);
      w.alwaysOnTop;
      w.onClose_({e.free;w.free;r.stop;});
      w.front;
      e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
      
      // ------------------------------ Sequencing
      
      r = Routine.new({
                       inf.do({var atk,sust,rel,dur,amp;
                                   atk = rrand(0.01,0.2);
                                   sust= rrand(0.1,0.7);
                                   rel = rrand(0.01,0.7);
                                   dur = atk+sust+rel;
                                   amp = 0.7;
                               a.set(
                                     \atk, atk,
                                     \sust, sust,
                                     \rel, rel,
                                     \amp, amp,
                                     \t_gate, 1
                                     );
       
                              {e.value_([
                                         [0]++[atk,sust,rel].normalizeSum.integrate,
                                         [0,amp,amp,0]])
                               }.defer(0);
      
                              dur.wait
                              })
                       }).reset.play;
      }
      )
      
      r.stop
      
  2. modificare dinamicamente solo la durata totale lasciando invariata la forma dell'inviluppo. In questo caso dobbiamo specificare tutti i tempi in valori relativi (proporzionali) compresi tra 0 e 1 per poi moltiplicarli per un fattore di durata così come abbiamo fatto per le ampiezze. In alternativa se i valori specificati per qualche motivo eccedono il range possiamo invocare su un'istanza di Env il metodo .duration_(n) che riscala dinamicamente le porporzioni temporali nella durata desiderata. Nel caso si debbano specificare i tempi delta la loro somma deve essere 1 e il metodo .normalizeSum può risultare estremamente utile in quanto riscala i valori di un Array facendo si che la somma sia 1.

    [23,34,65,87].normalizeSum; // Riscalati con somma = 1
    

    Un altra possibile strategia consiste nello specificare i segmenti temporali in valori percentuali (tra 0 e 100):

    [5,45,50].normalizeSum;
    [23,45,50].normalizeSum; // anche se la somma supera 100 la converzione riporta entro 1
    

    Il codice seguente riporta due esempi esemplificativi validi per tutti i tipi di inviluppo, nel primo i valori sono tempi delta e vengono moltiplicati per la durata, il secondo sono espressi in onsets e utilizza il metodo .duration_(dur)

    (
    s.waitForBoot{
    
    var liv,delta,dur;  
    
    // ------------------------------ SynthDef e Synth
    
    SynthDef(\envi,
                  {arg t_gate = 0,
                       liv    = #[0,0.65,0.34,0.78,0],
                       delta  = #[0.1,0.3,0.4,0.5],  // Valori proporzionali
                       dur    = 1,                   // Durata totale
                       amp    = 0;
                   var sig,env;
                       sig = SinOsc.ar(986);
                       env = Env.new(liv,delta*dur,\cub).kr(0,t_gate); // Durate dinamiche
                   Out.ar(0,sig*env)
                   }
              ).add;
    
    {a = Synth(\envi)}.defer(0.1);
    
    // ------------------------------ GUI
    
    w = Window.new("EnvelopeView", 500@400);
    w.alwaysOnTop;
    w.onClose_({e.free;w.free;r.stop;});
    w.front;
    e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
    
    liv   = [0,1,0.3,0.6,0];
    delta = {rrand(0.01,1)}!4;
    delta = delta.normalizeSum;            // tempi delta porporzionali
    
    a.set(\liv,liv,\delta, delta,\amp,1);  // setta i livelli, i tempi delta e l'ampiezza solo una volta
    
    // ------------------------------ Sequencing
      
    r = Routine.new({
                     inf.do({var dur;
                                 dur = rrand(0.1,1);
                                 dur.postln;
    
                             a.set(\dur,dur,\t_gate, 1); // durata totale
    
                            {e.value_([ [0]++delta.integrate, liv ])}.defer(0);                    
    
                            dur.wait
    	                    })
                     }).reset.play;
    }
    )
    
    r.stop
    
    // ------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------
    
    (
    s.waitForBoot{
    
    // ------------------------------ SynthDef e Synth
    
    SynthDef(\envi,
                  {arg t_gate = 0,dur=1,amp=0;
                   var sig,env;
                       sig = SinOsc.ar(986);
                       env = Env.pairs([[0,0],[0.01,0.51],[0.03,0.23],[0.12,0.51],
                                        [0.33,0.76],[0.59,0.47],[0.84,0.84],[1,0]],
                                        \cub
                                       ).duration_(dur).kr(0,t_gate)*amp; // metodo .duration_()
                   Out.ar(0,sig*env)
                    }
              ).add;
    
    {a = Synth(\envi)}.defer(0.1);
    
    // ------------------------------ GUI
    
    w = Window.new("EnvelopeView", 500@400);
    w.alwaysOnTop;
    w.onClose_({e.free;w.free;r.stop;});
    w.front;
    e = EnvelopeView.new(w, Rect(10,10,480,380)).resize_(5);
    e.setEnv(Env.pairs([[0,0],[0.01,0.51],[0.03,0.23],[0.12,0.51],
                        [0.33,0.76],[0.59,0.47],[0.84,0.84],[1,0]],
                        \cub));
    
    // ------------------------------ Sequencing
    
    r = Routine.new({
                     inf.do({var dur;
                                 dur = rrand(1,5);
                                 dur.postln;
    
                             a.set(\dur,dur,\amp,0.7,\t_gate, 1);
    
                            dur.wait
    	                    })
                     }).reset.play;
    }
    )
    
    r.stop
    

Curve

Per quanto riguarda le curve possiamo modificare dinamicamente:

Env.newClear()

Un'altra tecnica che possiamo utilizzare per modificare dinamicamente nel tempo un inviluppo consiste nel crearne uno vuoto con un numero predefinito di nodi e passargli successivamente tutti i valori in modo dinamico modificandone la forma. L'unico limite è dato dal fatto che il numero di nodi non è modificabile dinamicamente.

Nel caso precedente la durata globale dell'inviluppo è data dalla somma dei valori dei tre segmenti e va da un minimo di 0.03 a un massimo di 1.5. Se invece volessimo specificare una durata globale e scegliere i valori dei singoli segmenti all'interno di questa, possiamo utilizzare il metodo .duration_() già illustrato in precedenza.

(
d = 0.02;                   // Definiamo una durata
y = {rand(0.5)}!3;          // Creiamo un Array di 3 valori random
x = Env.new([0.001,rand(1.0),rand(1.0),0.001],y,\cub).duration_(d); // riscaliamo
a.set(\env,x, \t_trig,1);   // Inviamolo al Synth
e.setEnv(x)                 // Visualizzazione dul GUI
)					

Infine vediamo come sia possibile creare un database di inviluppi e richiamarli dinamicamente uno alla volta.

(
var env;
    env = [Env.perc(0.1,1,0.3),
           Env.perc(1,0.1,0.7),
           Env.perc(0.3,0.5,0.4),
           Env.perc(0.9,0.01,0.7),
           Env.perc(0.01,0.2,0.9),
          ].choose;
a.set(\env,env,\t_trig,1);
e.setEnv(env)
)

Interazione e GUI

MIDI

Se vogliamo modificare dinamicamente i diversi parametri degli inviuppi interagendo con devices esterni o GUI dobbiamo:

  1. stabilire quali sono i parametri sonori o musicali (argomenti della SynthDef) che vogliamo poter controllare dinamicamente interagendo con l'interfaccia e che nel caso degli inviluppi potrebbero essere:

    • Trigger (gate:0 e gate:1)
    • Ampiezza di picco
    • Durata
    • Curva
    • I valori xy dei singoli nodi
    • ...
  2. mettere in relazione (mappare) i dati provenienti da un singolo slider (o bottone o knob o altro cc) con uno dei parametri scelti nel punto precedente, separandolo dai dati provenienti dagli altri. Per farlo dobbiamo innanzitutto monitorare su quali canali MIDI o control number (CC) arrivano i valori dei diversi fader (o knob o bottoni) nel modo che già conosciamo:

    MIDIIn.connectAll;     // 1 Connettere tutti i devices MIDI
    MIDIFunc.trace(true);  // 2 Stampare tutti i messaggi midi in entrata per verificare il numero
                           // del controller (num:) di due o più slider
    MIDIFunc.trace(false); // 3 spegnere il monitoraggio
    

    Dopodichè invece che ricevere i valori provenienti da tutti i canali e da tutti i controller come abbiamo fatto in precedenza:

    MIDIdef.cc(\mioK1, {arg ...args; args.postln})

    Possiamo separarli e filtrarli specificando due ulteriori argomenti di MIDIdef.cc():

    MIDIdef.cc(\test1, {arg ...args; args.postln}, 1);    // riceve solo i valori dal cc 1
    MIDIdef.cc(\test2, {arg ...args; args.postln}, 1, 1); // riceve solo i valori dal cc 1 e dal canale 1	
    

    A questo punto possiamo creare una MIDIdef.cc() per ogni slider (knob, etc.) che vogliamo utilizzare, specificando questi argomenti con i valori monitorati in precedenza.

I codici seguenti illustrano tre esempi significativi di questa modalità di controllo dei parametri degli inviluppi. I primi due riguardano i due diversi tipi di inviluppo (con e senza sfase di sostegno) mentre il terzo la modularità di questo processo e rappresenta un semplice esempio di live sequencing.

Inviluppi senza fase di sostegno

image not found

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

(
s.waitForBoot{

// ------------------------------ SynthDef e Synth

SynthDef(\no_sust,
              {arg t_gate=0,amp=0,dur=1,curva=1;     
               var sig,env;
                   sig = SinOsc.ar(1970);
                   env = Env.new([0,1,0.3,0],[0.01,0.2,1],          // tempi, livelli
                                  K2A.ar(curva).lag(0.2)            // curva
                                 ).duration_(K2A.ar(dur).lag(0.02)) // durata globale
                                  .kr(0,t_gate)*                    // trigger
                                  K2A.ar(amp).lag(0.02);            // ampiezza           
               Out.ar(0,sig*env)
               }
          ).add;

{a = Synth(\no_sust)}.defer(0.1);

// ------------------------------ GUI

w = Window.new("Controlli", 200@230);
w.onClose_({a.free;e.free}).front.alwaysOnTop;

MIDIIn.connectAll;

c = CheckBox.new(w,Rect(77, 10)).action_({arg i; a.set(\t_gate,i.value.asInt)});
d = StaticText.new(w,Rect(95,10, 50, 15)).string = "Gate";
MIDIdef.cc(\mioK1, {arg val; {c.valueAction_(val/127)}.defer(0)},41);    // cc number 41 (Play)

e = Slider.new(w,Rect(10, 50, 50, 170)).action_({arg i; a.set(\amp, i.value)});
f = StaticText.new(w,Rect(22, 30, 50, 15)).string = "Amp";
MIDIdef.cc(\mioK2, {arg val; {e.valueAction_(val/127)}.defer(0)},0); 

g = Slider.new(w,Rect(75, 50, 50, 170)).action_({arg i;a.set(\dur, i.value*0.9+0.1)});
h = StaticText.new(w,Rect(77, 30, 50, 15)).string = "Tempo";
MIDIdef.cc(\mioK3, {arg val; {g.valueAction_(val/127)}.defer(0)},1); 

i = Slider.new(w,Rect(135, 50, 50, 170)).action_({arg i;a.set(\curva, i.value*10+10)}); // da -5 a +5
l = StaticText.new(w,Rect(140, 30, 50, 15)).string = "Curva";
MIDIdef.cc(\mioK4, {arg val; {i.valueAction_(val/127)}.defer(0)},2);
}
)

Inviluppi con fase di sostegno

image not found

// N.B. le durate non possono essere riscalate
(
s.waitForBoot{

// ------------------------------ SynthDef e Synth

SynthDef(\sust,
              {arg gate=0,amp=0,dur=1,curva=1;     
               var sig,env;
                   sig = SinOsc.ar(1970);
                   env = Env.new([0,1,0.3,0],[0.01,0.2,1],
                                 K2A.ar(curva).lag(0.2),2
                                 ).kr(0,gate)*
                                 K2A.ar(amp).lag(0.02);
               Out.ar(0,sig*env)
               }
          ).add;

{a = Synth(\sust)}.defer(0.1);

// ------------------------------ GUI

w = Window.new("Controlli", 135@230);
w.onClose_({a.free;e.free}).front.alwaysOnTop;

MIDIIn.connectAll;

c = CheckBox.new(w,Rect(50, 10)).action_({arg i; a.set(\gate,i.value.asInt)});
d = StaticText.new(w,Rect(70,10, 50, 15)).string = "Note";
MIDIdef.cc(\mioK1, {arg val; {c.valueAction_(val/127)}.defer(0)},41);    // cc number 41 (Play)

e = Slider.new(w,Rect(10, 50, 50, 170)).action_({arg i; a.set(\amp, i.value)});
f = StaticText.new(w,Rect(22, 30, 50, 15)).string = "Amp";
MIDIdef.cc(\mioK2, {arg val; {e.valueAction_(val/127)}.defer(0)},0); 

g = Slider.new(w,Rect(75, 50, 50, 170)).action_({arg i;a.set(\curva, i.value*10+10)});
h = StaticText.new(w,Rect(77, 30, 50, 15)).string = "Curva";
MIDIdef.cc(\mioK3, {arg val; {g.valueAction_(val/127)}.defer(0)},1);
}
)

Live sequencing

image not found

(
s.waitForBoot{

// ------------------------------ SynthDef e Synth

SynthDef(\no_sust,
              {arg t_gate=0,amp=0,dur=1,curva=1;     
               var sig,env;
                   sig = SinOsc.ar(1970);
                   env = Env.new([0,1,0.3,0],[0.01,0.2,1],          // tempi, livelli
                                  K2A.ar(curva).lag(0.2)            // curva
                                 ).duration_(K2A.ar(dur).lag(0.02)) // durata globale
                                  .kr(0,t_gate)*                    // trigger
                                  K2A.ar(amp).lag(0.02);            // ampiezza           
               Out.ar(0,sig*env)
               }
          ).add;

{a = Synth(\no_sust)}.defer(0.1);

// ------------------------------ GUI

w = Window.new("Controlli", 200@230);
w.onClose_({a.free;e.free;r.free}).front.alwaysOnTop;

MIDIIn.connectAll;

c = CheckBox.new(w,Rect(77, 10)).action_({arg i;
	                                      if(i.value == true, {r.reset.play},{r.stop})
                                          });

d = StaticText.new(w,Rect(95,10, 100, 15)).string = "Play Seq";
MIDIdef.cc(\mioK1, {arg val; {c.valueAction_(val/127)}.defer(0)},41);    // cc number 41 (Play)

e = Slider.new(w,Rect(10, 50, 50, 170)).action_({arg i; a.set(\amp, i.value)});
f = StaticText.new(w,Rect(22, 30, 50, 15)).string = "Amp";
MIDIdef.cc(\mioK2, {arg val; {e.valueAction_(val/127)}.defer(0)},0); 

g = Slider.new(w,Rect(75, 50, 50, 170)).action_({arg i;a.set(\dur, i.value*0.9+0.1)});
h = StaticText.new(w,Rect(77, 30, 50, 15)).string = "Tempo";
MIDIdef.cc(\mioK3, {arg val; {g.valueAction_(val/127)}.defer(0)},1); 

i = Slider.new(w,Rect(135, 50, 50, 170)).action_({arg i;a.set(\curva, i.value*10+10)}); // da -5 a +5
l = StaticText.new(w,Rect(140, 30, 50, 15)).string = "Curva";
MIDIdef.cc(\mioK4, {arg val; {i.valueAction_(val/127)}.defer(0)},2);

// ------------------------------ Sequencing

r = Routine.new({
                 inf.do({
                         a.set(\t_gate,rand(2));
                         0.1.wait;
                         })
                });
}
)