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 devices

After 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 dictionary

Both 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;
)