Overview
Here are some notes on MIDI input and output.
Content
MIDI Input
To receive MIDI messages from any MIDI device you need to connect these.
MIDIIn.connectAll; // connect all avaiable MIDI devices to SC
MIDIIn.disconnectAll; // disconnect all connected MIDI devicesAfter this you can trace any incoming MIDI message.
MIDIFunc.trace(true); // trace all incoming MIDI messages
MIDIFunc.trace(false);There are two Objects for receiving MIDI data, MIDIFunc and MIDIdef. MIDIFunc does need to be saved in a variable, while MIDIdef stores itself at a key within a global dictionary, which is more convenient.
// two objects for receiving MIDI data
MIDIFunc() // needs to be saved into a variable, superclass to MIDIdef
MIDIdef() // stores itself at a key within a global dictionaryBoth objects have different methods for receiving different kind of MIDI data like note-on, note-off or cc messages.
(
MIDIdef.noteOn(\on, {
"note-on".postln;
});
)
(
MIDIdef.cc(\on, {
"cc-message".postln;
});
)But you need to extract the needed MIDI data from the incoming messages. The MIDI data is coming in a specific order.
// you need to extract the needed MIDI data
(
MIDIdef.noteOn(\on, {
// MIDIdef.noteOn gets these values in this order
arg vel, num, chan, src; // vel, num and chan are the expected values, src is a unique ID for the MIDI device
[vel, num, chan, src].postln;
});
)Acting on MIDI Note messages
Every function inside a MIDIdef can be executed, e.g. triggering a pattern.
// create a pattern and play that from the keyboard
(
a = Pbind(
\instrument, \quick,
\degree, Pwhite(0, 10, 5),
\amp, Pwhite(0.05, 0.2),
\dur, 0.1
);
)
// test
a.play;
// trigger pattern from pad or keyboard
(
MIDIdef.noteOn(\quneo, {
arg vel, note;
a.play});
)Handling note on/off messages for sustained envelopes
You can assign a Synth Voice to an Slot in an Array, since a array can hold any object you wish, it can also hold a Synth. Per default you set the gate value of you SynthDef to 1. When you receive a MIDI note on message, you create a instance of a Synth and assign it to the array, at the slot corresponding to the MIDI Note value. When you receive a Note Off message, you check the note value and set the gate of this Array slot to 0.
// control the \default synth with MIDI.noteOn messages
(
MIDIdef.noteOn(\on, {
arg vel, num;
Synth(\default, [freq: num.midicps, amp: vel/127 * 0.3]);
});
) // this causes to let the Synths live forever, you need to introduce a noteOff mechanism
// instead save every Synth instance in an array and operate on this
(
var noteArray = Array.newClear(128); // array has one slot per possible MIDI note
MIDIdef.noteOn(\noteOn, {
arg vel, num;
noteArray[num] = Synth(\default, [\freq, num.midicps, \amp, vel.linlin(0, 127, 0, 1)]);
});
MIDIdef.noteOff(\noteOff, {
arg vel, num;
noteArray[num].set(\gate, 0);
});
)Acting on MIDI CC
Check your incomin CC messages.
// check on your incoming cc messages
(
MIDIdef.cc(\cc, {
arg val, num;
[val, num].postln;
})
)By appending a array with values, MIDIdef.cc is filtering out every cc message, that is not from one of these.
// by appending an array with values, MIDIdef.cc is filtering out every cc message, that is not from one of these controller numbers
(
MIDIdef.cc(\cc, {
arg val, num;
[val, num].postln;
}, [1, 7]) // only receives messages from controller number 1 and 7
)
(// only receives messages from controller number 1 and write it to a global var
MIDIdef.cc(\cc, {
arg val, num;
~cc1 = val;
~cc1.postln;
}, [1])
)By saving the CC values to a array which holds control busses, you can access these values somewhere else in your program.
// write the MIDI data to a control bus
~faderfox = {Bus.control(s, 1)} ! 16;
(
MIDIdef.cc(\faderfox, {
arg val, num;
var ccNums = (0..15);
switch(num)
{ccNums[0]} {~faderfox[0].set(val/127)}
{ccNums[1]} {~faderfox[1].set(val/127)}
{ccNums[2]} {~faderfox[2].set(val/127)}
{ccNums[3]} {~faderfox[3].set(val/127)}
{ccNums[4]} {~faderfox[4].set(val/127)}
{ccNums[5]} {~faderfox[5].set(val/127)}
{ccNums[6]} {~faderfox[6].set(val/127)}
{ccNums[7]} {~faderfox[7].set(val/127)}
{ccNums[8]} {~faderfox[8].set(val/127)}
{ccNums[9]} {~faderfox[9].set(val/127)}
{ccNums[10]} {~faderfox[10].set(val/127)}
{ccNums[11]} {~faderfox[11].set(val/127)}
{ccNums[12]} {~faderfox[12].set(val/127)}
{ccNums[13]} {~faderfox[13].set(val/127)}
{ccNums[14]} {~faderfox[14].set(val/127)}
{ccNums[15]} {~faderfox[15].set(val/127)}
}, (0..15)).permanent_(true) // make it exist permant, so that CMD + . does not kill the MIDIdef
)You can also get the current value from this control bus array now via .getSynchronous and inset this to patterns.
// you can also get the current value from this control bus array now via
~faderfox[0].getSynchronous;
// and control patterns with it
(
Pbindef(\p,
\degree, Pwhite(-5, Pfunc({ ~faderfox[1].getSynchronous.linlin(0, 1, 4, 16) }), inf).round,
\dur, Pfunc({ ~faderfox[0].getSynchronous.linexp(0, 1, 0.01, 1.0) }),
\legato, Pwhite(1.0, 1.5, inf),
\db, Pwhite(-32, -20, inf),
).play;
)