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).
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:
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
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.
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.
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