Devices

Se vogliamo invece controllare i parametri di un Synth interagendo fisicamente con devices esterni come prima cosa dobbiamo cercare nelle specifiche tecniche dello strumento o del controller se ha o meno bisogno di essere connesso al computer con un cavo o se utilizza una qualche connessione Wi-fi.

In secondo luogo dobbiamo verificare quale protocollo di comunicazione utilizza per parlare con il mondo esterno ovvero che "lingua" utilizza.

Uno dei due principali protocolli utilizzati dai costruttori è il MIDI (Musical Instrument Digital Interface) e usualmente gli strumenti sono collegati tra loro con un cavo MIDI e un'interfaccia (oggigiorno anche con un connettore USB e un'interfaccia virtuale).

image not found

In SuperCollider i valori ricevuti in ingresso provenienti dai devices esterni possono essere letti (ed eventualmente inviati ad altri software o devices) nell'Interprete per poi essere eventualmente inviati al Server.

Dopo aver connesso fisicamente i devices al computer dobbiamo inizializzare il MIDI in SuperCollider:

MIDIClient.init;		

Eseguendo il codice precedente comparirà una lista contenente tutti i devices dai quali possiamo ricevere dati (MIDI Sources) e una di quelli ai quali possiamo inviare dati (MIDI Destinations):

MIDI Sources:
	MIDIEndPoint("Driver IAC", "Bus 1")
	MIDIEndPoint("nanoKONTROL2", "SLIDER/KNOB")
	MIDIEndPoint("ARIUS", "ARIUS")
MIDI Destinations:
	MIDIEndPoint("Driver IAC", "Bus 1")
	MIDIEndPoint("nanoKONTROL2", "CTRL")
	MIDIEndPoint("ARIUS", "ARIUS")
-> MIDIIn

Possiamo anche ottenere nuovamente le due liste eseguendo questo codice:

MIDIClient.sources;      // riporta un Array con i devices da cui ricevere
MIDIClient.destinations; // riporta un Array con i devices a cui inviare

Comandi

Ricevere messaggi MIDI

Per ricevere messaggi MIDI da altri devices dobbiamo compiere le seguenti operazioni:

  1. connettere virtualmente a SuperCollider tutti i devices connessi fisicamente al computer:

    MIDIIn.connectAll; // connette tutti i devices MIDI
    

    Siccome il comando precedente può essere eseguito una sola volta possiamo "eliminare" gli effetti di eventuali connessioni pregresse valutando prima il codice seguente:

    MIDIIn.disconnectAll; // disconnette tutti i devices MIDI eventualmente connessi
    
  2. recuperare alcune informazioni utili riguardanti i valori in ingresso monitorando tutti i dati che passano in tutte le porte midi eseguendo la seguente sintassi:

    MIDIFunc.trace(true);  // legge tutti i messaggi MIDI in ingresso e riporta i dati nella Post window
    MIDIFunc.trace(false); // Termina il monitoraggio 
    

    In questo modo possiamo monitorare ad esempio la corrispondenza tra controller fisico e ccn piuttosto che la porta MIDI sulla quale trasmette.

  3. scegliere il tipo di data MIDI che vogliamo recuperare filtrando tutti gli altri utilizzando la Classe MIDIdef:

    (
    MIDIdef.noteOn( \noteOn, {arg ...args; args.postln}); // velocity, note,      canale, uid
    MIDIdef.noteOff(\noteOff,{arg ...args; args.postln}); // velocity, note,      canale, uid
    MIDIdef.cc(     \control,{arg ...args; args.postln}); // valore,   numero cc, canale, uid
    )
    

    Nel codice precedente abbiamo recuperato i parametri MIDI più comuni come .noteOn(), noteOff() e .cc() ma attraverso la Classe MIDIdef possiamo eventualmente recuperare anche i valori di: .polytouch, .touch, .bend, .program, .sysex, .smpte, .sysrt.

    Per i dettagli è meglio consultare l'help file. Gli argomenti principali di tutti questi metodi sono:

    • un nome sotto forma di simbolo.
    • una funzione che viene valutata ogni volta che arriva un dato MIDI del tipo specifico.

    Ad esempio se eseguiamo il codice precedente e schiacciamo un tasto di una tastiera musicale connessa o muoviamo uno slider di un controller connesso vedremo comparire un Array di valori nella Post window. Per ogni metodo questi rappresentano un parametro differente anche se i principali sono mantenuti sempre nelle stesse posizioni dell'Array come commentato direttamente nel codice precedente.

    Possiamo ora isolare i singoli parametri dell'Array nel modo usuale ([].at(id)) e assegnarli ad una variabile per utilizzarli nel codice.

  4. Se volgiamo distruggere la singola MIDIDef o tutte:

    MIDIdef(\noteOn).free; // Cancella una specifica MIDIdef
    MIDIdef.freeAll;       // Cancella tutte le MIDIdef		
    

Synth monofonico

Il codice successivo sintetizza i diversi controlli di un Synth monfonico:

(
s.reboot;
MIDIIn.disconnectAll;  // disconnette tutti i devices MIDI
MIDIIn.connectAll;     // connette tutti i devices MIDI
)
	
(
// ------------------------------ SynthDef e Synth (monofonico)

SynthDef(\midi,
              {arg freq=440, amp=0, smooth=0.02, gain=0;
               var sig;
                   sig = SinOsc.ar(freq)*amp.lag(smooth);    
               Out.ar(0,sig*gain,lag(0.2))
               }
          ).add;

{~synth = Synth(\midi)}.defer(0.1);

// ------------------------------ Ascolta i devices esterni

MIDIdef.cc(\masterOut, {arg val;                  // solo primo argomento (valore del cc)
                        ~synth.set(\gain,val/127) // master out tra 0 e 1
           });
MIDIdef.noteOn(\noteOn,{arg vel,note;
                        ~synth.set(\freq,note.midicps, // frequenze in Hz
                                   \amp,vel/127);      // ampiezza tra 0 e 1
           });
MIDIdef.noteOff(\noteOff,{~synth.set(\amp,0)});        // note off
)

~synth.set(\smooth,5); // cambia il tempo di fade in e fade out

Synth polifonico

Il codice successivo sintetizza i diversi controlli di un Synth polifonico:

(
s.reboot;
MIDIIn.disconnectAll;  // disconnette tutti i devices MIDI
MIDIIn.connectAll;     // connette tutti i devices MIDI
)

(
SynthDef(\midi_poli,
                 {arg freq=440, amp=0, smooth=0.02,trig=0;
		  var sig,env;
		  sig = SinOsc.ar(freq)*amp.lag(smooth);
		  env = EnvGen.kr(Env.perc,trig,doneAction:2);
                  Out.ar(0,sig*env)
                  }
          ).add;

MIDIdef.noteOn(\noteOn1,{arg vel, note;
	                 Synth(\midi_poli,[\freq,note.midicps,\amp,vel/127,\trig,1])
               })
)					

Notiamo come nel primo codice l'inviluppo di ampiezza è generato direttamente da uno smoothing dalla velocity Midi riscalata mentre nel secondo i messaggi di note on e note off gestiscono il trigger di un inviluppo (gate:0 e gate:1).

Infine le diverse tecniche di gestione della polifonia (voice allocation) anche attraverso devices Midi sono descritte in un Paragrafo dedicato

Canali e ccn

Nei codici precedenti le MIDIdef "ascoltavano" tutti i messaggi in entrata provenienti da tutti i devices collegati al computer.

Se vogliamo discriminare i canali e per quanto riguarda i messaggi di controllo i control numbers dobbiamo specificare ulteriori argomenti:

MIDIdef.cc(\control_0,{arg val; val.postln});    // da tutti i controller
MIDIdef.cc(\control_1,{arg val; val.postln},1);  // solo dal cc 1 chan 0
MIDIdef.cc(\control_2,{arg val; val.postln},2);  // solo dal cc 2 chan 0
MIDIdef.cc(\control_3,{arg val; val.postln},3);  // solo dal cc 3 chan 0

MIDIdef.cc(\control_1,{arg val; val.postln},1,0);  // solo dal cc 1 chan 0
MIDIdef.cc(\control_2,{arg val; val.postln},1,1);  // solo dal cc 1 chan 1
MIDIdef.cc(\control_3,{arg val; val.postln},1,2);  // solo dal cc 1 chan 2

Inviare messaggi MIDI

Se invece vogliamo inviare messaggi MIDI ad altri devices dobbiamo specificare la destination alla quale inviare i messaggi sotto forma di indice e per facilitarci le cose possiamo recuperare l'Array di tutti i devices disponibili:

MIDIClient.destinations; // riporta un Array con i devices a cui inviare

// risultato:
-> [ MIDIEndPoint("Driver IAC", "Bus 1"),           // ID 0
     MIDIEndPoint("iPhone di Andrea", "Bluetooth"), // ID 1
     MIDIEndPoint("to Max 1", "to Max 1"),          // ID 2
     MIDIEndPoint("to Max 2", "to Max 2")           // ID 3
   ]

Ora se vogliamo inviare i messaggi ad uno di essi basterà generare un'istanza di MIDIOut.new(ID); e specificare come argomento l'indice dell'Array corrispondente al device al quale vogliamo inviare i messaggi MIDI.

c = MIDIOut.new(0);

Per poi inviare i singoli messaggi con i diversi metodi le varie tipologie di messaggio:

//       canale, pitch,     velocity
c.noteOn(0,      rand(127), rand(127));
c.noteOff(0,     rand(127), rand(127));
c.allNotesOff(0);


//       canale, ccn,  val
c.control(0,     1,   rand(127));					

Scarichiamo e lanciamo il patch di Max