MIDI e OSC¶
Intro ¶
Protocolli di comunicazione fra devices e software diversi.
Sintassi simili.
MIDI $\rightarrow$ connessioni fisiche $\rightarrow$ solo int tra 0 e 127 $\rightarrow$ messaggi prestabiliti.
OSC $\rightarrow$ connessioni wireless $\rightarrow$ int, float, stringhe $\rightarrow$ qualsiasi messaggio.
MIDI ¶
Strumenti connessi tra loro mediante cavi, interfacce o porte USB.

Inizializzaimo la comunicazione midi in SuperCollider.
MIDIClient.init;
Recuperiamo i dispositivi midi (sia fisici che virtuali) connessi al computer.
MIDIClient.sources; // riporta un Array con i devices da cui ricevere
MIDIClient.destinations; // riporta un Array con i devices a cui inviare
- Porte $\rightarrow$ connettori fisici (In, Out, Thru).
- Canali $\rightarrow$ 16 per ogni porta $\rightarrow$ righi vuoti di una partitura $\rightarrow$ ognuno può essere monofonico o polifonico (il numero di voci dipende dall'expander).
- Voci $\rightarrow$ lo strumento che assegnamo a un canale.
- Messaggi $\rightarrow$ di canale o di sistema.
Per approfondire $\rightarrow$ scarica.
Ricevere ¶
Disconnettiamo eventuali dispositivi non desiserati rimasti in memoria.
Connettiamo i dispositivi.
(
MIDIIn.disconnectAll; // disconnette tutti i devices MIDI eventualmente connessi
MIDIIn.connectAll; // connette tutti i devices MIDI
)
Verifichiamo la comunicazione e il formato dei messsaggi in ingresso.
MIDIFunc.trace(true); // legge tutti i messaggi MIDI in ingresso e riporta i dati nella Post window
MIDIFunc.trace(false); // Termina il monitoraggio
Scegliamo il tipo di messaggio midi vogliamo recuperare.
Invochiamo il metodo corrispondente sulla 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
)
La funzione viene valutata ogni volta che riceve un messaggio midi del tipo corrispondente.
Restituisce un Array con info specifiche sul messaggio ricevuto.
Principali tipi di messaggi .noteOn, noteOff, .cc, .polytouch, .touch, .bend, .program, .sysex, .smpte, .sysrt $\rightarrow$ consulta l'Help file.
Eliminare una MIDIdef o tutte.
MIDIdef(\noteOn).free; // Cancella una specifica MIDIdef
MIDIdef.freeAll; // Cancella tutte le MIDIdef
Se non specifichiamo nulla riceviamo messaggi da tutti i canali ed eventualemnte da tutti i ccn.
Per ricevere da un CCN o da un canale specifico.
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
Synth monofonico ¶
- Creiamo l'istanza di synth al di fuori delle MIDIdef.
- Inviamo i parametri dalle MIDIdef.
(
s.boot;
MIDIIn.disconnectAll; // disconnette tutti i devices MIDI
MIDIIn.connectAll; // connette tutti i devices MIDI
)
(
s.plotTree;
s.meter;
// ------------------------------ 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;
// ------------------------------ 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 = Synth(\midi);
~synth.set(\smooth,5); // cambia il tempo di fade in e fade out
Synth polifonico senza sostegno ¶
Allocazione dinamica delle voci.
Creiamo un'istanza di Synth ogni volta che una MIDIdef riceve un messaggio di noteOn.
L'istanza si autodistrugge dopo un tempo stabilito.
Non sono necessari i messaggi di noteOff.
(
SynthDef(\midi_poli1, {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_poli1,[\freq,note.midicps,\amp,vel/127,\trig,1])
})
)
Synth polifonico con sostegno ¶
Allocazione statica delle voci.
Creare un Array vuoto con 128 items.
Midi note $\rightarrow$ indici dell'array.
Note On $\rightarrow$ sostituisce nil con un Synth all'indice del pitch.
Note Off $\rightarrow$ invia gate:0 al Synth che corrisponde all'indice del pitch e lo sostituisce con 'nil'.
(
SynthDef(\midi_poli2, {arg freq=789, amp=0, pan=0, gate=0;
var sig, env;
sig = SinOsc.ar(freq);
env = Env.adsr(0.01,0.3,0.5,1);
env = EnvGen.kr(env, gate,doneAction:2);
sig = sig * env * amp;
sig = Pan2.ar(sig, pan);
Out.ar(0, sig)
}).add
)
(
~note = Array.newClear(128); // Array vuoto di 128 elementi (tutte le note midi)
// Tutti gli elementi sono 'nil'
MIDIdef.noteOn(\noteOn1,{arg vel, pitch;
// sostituisce nil con il synth alla posizione corrispondente
~note.put(pitch,
Synth(\midi_poli2,[\freq,pitch.midicps,
\amp,vel/127,
\gate,1]))
});
MIDIdef.noteOff(\noteOff1,{arg vel, pitch;
// triggera il note off alla posizione corrispondente
~note.at(pitch).set(\gate,0);
~note.put(pitch, nil)
});
)
Inviare ¶
Restituisce un Array con tutti i devices disponibili alla ricezione di messaggi MIDI.
MIDIClient.destinations;
Specifichiamo quello a cui inviare i messaggi attraverso il suo indice nell'Array.
Inviamo i singoli messaggi.
c = MIDIOut.new(0);
// 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));
OSC ¶
Strumenti o software connessi tra loro in modalità wirless attraverso una connessione LAN oppure internet.

E' il protocollo impiegato per far comunicare Interprete e Server di SuperCollider.
Ci sono diverse app per smartphone e tablet che supportano questo protocollo rendendo di fatto questi devices degli strumenti musicali.
TouchOsc è una di quelle più utilizzate.
FaceOsc invece può essere utile per testare le comunicazioni tra software nello stesso computer.
Per inviare messaggi dobbiamo creare almeno un canale di comunicazione tra dispositivi e/o software:
- indirizzo IP come "127.0.0.1" che identifica in modo univoco un device (quello indicato sta per 'locale').
- porta $\rightarrow$ un numero intero a piacere che definisce un canale di comunicazione monodirezionale.
Ogni indirizzo IP (device) può avere più porte, tipicamente una per ricevere e una per inviare messaggi.
Ricevere ¶
Verificare la connessione tra i devices.
Monitorare il tipo di messaggi in ingresso.
Il Server deve essere spento altrimenti saranno riportati anche i messaggi tra Server e Interprete.
OSCFunc.trace(true); // legge tutti i messaggi OSC in ingresso e riporta i dati nella Post window
OSCFunc.trace(false); // Termina il monitoraggio
I messaggi sono identificati attraverso un etichetta caratterizzata dal fatto che inizia con uno slash.
Per il routing dei messaggi.
// nome, funzione, indirizzo porta
OSCdef.new(\criccio, {arg msg; msg.postln}, '/x', recvPort:8001)
OSCdef.new(\ciaccio, {arg msg; msg.postln}, '/y', recvPort:8001)
OSCdef.new(\ciuccio, {arg msg; msg.postln}, '/z', recvPort:8001)
OSCdef.new(\cieccio, {arg msg; msg.postln}, '/1/fader1',recvPort:8001)
La intassi è pressochè identica a MIDIdef.
Elminare una o tutte le OSCdef.
OSCdef(\criccio).free; // Cancella una specifica OSCdef
OSCdef.freeAll; // Cancella tutte le OSCdef
Un semplice esempio sonoro.
s.boot;
(
SynthDef(\osc,
{arg gain=0;
var sig;
sig = SinOsc.ar;
Out.ar(0,sig*gain.lag)
}).add;
)
(
a = Synth(\osc);
OSCdef.new(\master, {arg msg;
msg.postln;
a.set(\gain,msg[1]) }, // master out tra 0 e 1
'/fade1', recvPort:8000);
)
Inviare ¶
Specifichiamo il device a cui inviare i messaggi con il suo indirizzo IP.
Inviamo i singoli messaggi.
c = NetAddr("192.168.100.2", 9000);
c.sendMsg("/fade1", rand(1.0));
c.sendMsg("/fade1", rand(1.0), rand(1.0), rand(1.0)); // più valori sono separati da una virgola
Server $\rightarrow$ Interprete ¶
Sottocampionare segnali nel Server per inviarli all'interprete come float.
s.boot;
// -------------------------------> Un solo segnale
(
SynthDef(\inviaM, {var sig;
sig = LFNoise0.kr(3); // Segnale da campionare
SendReply.kr(Impulse.kr(2), // Rata di sottocampionamento
'/posa', // Etichetta
sig); // Segnale da inviare
}).add;
OSCdef.freeAll;
OSCdef.new(\ricevi, {arg val; val.postln},'/posa');
// val = [Etichetta, Nodo, -1, valore]
)
a = Synth(\inviaM);
a.free;
// -------------------------------> Più segnali dallo stesso Synth
(
SynthDef(\inviaP, {var sig1,sig2;
sig1 = LFNoise0.kr(3); // Primo segnale
sig2 = SinOsc.kr(5); // Secondo segnale
SendReply.kr(Impulse.kr(2),
'/posa',
[sig1,sig2]); // Array di segnali da inviare
}).add;
OSCdef.freeAll;
OSCdef.new(\ricevi, {arg val; val.postln},'/posa');
)
a = Synth(\inviaP);
a.free;
Visualizzazione (GUI) ¶
Sia MIDI che OSC.
Due strategie di programmazione.
- in cascata flusso di dati unico.
(
s.boot;
s.meter;
)
(
s.plotTree;
MIDIClient.init;
MIDIIn.disconnectAll;
MIDIIn.connectAll;
SynthDef(\sig, {arg amp=0,freq=400;
var sig,filt;
sig = WhiteNoise.ar;
filt = BPF.ar(sig, freq.lag(0.2), 0.001);
Out.ar(0,filt*amp.lag(0.2))
}).add;
)
a = Synth(\sig);
(
w = Window.new("Multi", Rect.new(0,0,60,220));
h = Slider.new(w, Rect(5,60,50,150));
i = Knob.new(w, Rect(5,5,50,50));
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;h.free;i.free;a.free});
// ------------------------------ GUI --> Interprete --> Synth
h.action_({a.set(\amp, h.value)});
i.action_({a.set(\freq,i.value.linlin(0,1,300,3000))}); // riscala i valori
// ------------------------------ MIDI --> Interprete --> GUI
MIDIdef.cc(\amp,{arg val; {h.valueAction_(val/127)}.defer // alla GUI (riscalato)
}, 0); // riceve dal cc 0
MIDIdef.cc(\freq,{arg val;{i.valueAction_(val/127)}.defer // alla GUI (riscalato)
},16); // riceve dal cc 16
// ------------------------------ OSC --> Interprete --> GUI
OSCdef.new(\amp, {arg msg; {h.valueAction_(msg[1])}.defer}, '/fade1', recvPort:8000);
OSCdef.new(\freq, {arg msg; {i.valueAction_(msg[1])}.defer}, '/rotary1',recvPort:8000)
)
- in parallelo $\rightarrow$ poter eliminare la GUI senza interrompere il collegamento con i devices.
(
s.boot;
s.meter;
)
(
s.plotTree;
MIDIClient.init;
MIDIIn.disconnectAll;
MIDIIn.connectAll;
SynthDef(\sig, {arg amp=0,freq=400;
var sig,filt;
sig = WhiteNoise.ar;
filt = BPF.ar(sig, freq.lag(0.2), 0.001);
Out.ar(0,filt*amp.lag(0.2))
}).add;
)
a = Synth(\sig);
(
w = Window.new("Multi", Rect.new(0,0,60,220));
h = Slider.new(w, Rect(5,60,50,150));
i = Knob.new(w, Rect(5,5,50,50));
w.front;
w.alwaysOnTop_(true);
w.onClose_({w.free;h.free;i.free;a.free});
// ------------------------------ GUI --> Interprete --> Synth
h.action_({a.set(\amp, h.value)});
i.action_({a.set(\freq,i.value.linlin(0,1,300,3000))});
// ------------------------------ MIDI --> Interprete --> Synth
// MIDI --> Interprete --> GUI
MIDIdef.cc(\amp,{arg val;
a.set(\amp,val/127); // al Synth (riscalato)
{h.value_(val/127)}.defer // alla GUI (riscalato)
}, 0); // riceve dal cc 0
MIDIdef.cc(\freq,{arg val;
a.set(\freq,val.linexp(0,127,300,3000)); // al Synth (riscalato)
{i.value_(val/127)}.defer // alla GUI (riscalato)
}, 16); // riceve dal cc 16
// ------------------------------ OSC --> Interprete --> Synth
// OSC --> Interprete --> GUI
OSCdef.new(\amp, {arg msg; {h.value_(msg[1])}.defer; a.set(\amp,msg[1])}, '/fade1',recvPort:8000);
OSCdef.new(\freq, {arg msg; {i.value_(msg[1])}.defer; a.set(\freq,msg[1].linexp(0,1,300,3000))}, '/rotary1',recvPort:8000)
)