SuperCollider CLASSES

MIDIIn

receive MIDI messages
Inherits from: Object

Description

This document explains technical details of the MIDI hardware interface class, MIDIIn.

MIDIIn is a simple and direct interface. When MIDI events come into SuperCollider, MIDIIn evaluates simple handler functions.

NOTE: For general programming, users should not use the MIDIIn class directly. See the MIDIFunc and MIDIdef classes for higher level event matching and more flexible handling of event handlers.

Certain MIDI messages are supported only through MIDIIn. These are: sysex, sysrt, smpte.

See the Using MIDI helpfile for practical considerations and techniques for using MIDI in SC.

The MIDIIn class

MIDIIn links MIDI input received from the operating system to a set of user defined functions. Only one set of MIDI input handling functions can be active at a time, they are stored in the following class variables:

noteOff, noteOn, polytouch, control, program, touch, bend, sysex, sysrt, smpte

The first argument these functions receive is an unique identifier that specifies the source of the data.

Class Methods

*findPort (deviceName, portName)

searches for a connected MIDIEndPoint by name.

//list connected ins:
MIDIClient.init;
MIDIClient.sources;

*addFuncTo (what, func)

Add a Function or similar object to be evaluated whenever a particular MIDI message is received. This method is preferable to the setters below, since it will not overwrite any existing functions.

Arguments:

what

A Symbol indicating the message type to wait for, one of 'noteOn' 'noteOff', 'polytouch', 'control', 'program', 'touch', 'bend', 'sysex', 'sysrt', 'smpte', or 'invalid' (for invalid sysex messages).

func

A Function or similar object to be evaluated when a message of the specified type is received. See the setters below for the arguments which will be passed at evaluation time.

*removeFuncFrom (what, func)

Remove a Function or similar object from the list of those to be evaluated whenever a particular MIDI message is received. This method is preferable to the setters below, since it will leave any existing functions in place.

Arguments:

what

A Symbol indicating the message type, one of 'noteOn' 'noteOff', 'polytouch', 'control', 'program', 'touch', 'bend', 'sysex', 'sysrt', 'smpte', or 'invalid' (for invalid sysex messages).

func

The Function or similar object to be removed.

*replaceFuncTo (what, func, newFunc)

Replace a Function or similar object in the list to be evaluated whenever a particular MIDI message is received with another one. This method is preferable to the setters below, since it will not overwrite any existing functions.

Arguments:

what

A Symbol indicating the message type to wait for, one of 'noteOn' 'noteOff', 'polytouch', 'control', 'program', 'touch', 'bend', 'sysex', 'sysrt', 'smpte', or 'invalid' (for invalid sysex messages).

func

The Function or similar object to be replaced.

newFunc

A Function or similar object to be evaluated when a message of the specified type is received. See the setters below for the arguments which will be passed at evaluation time.

*noteOnZeroAsNoteOff

*noteOnZeroAsNoteOff = value

By default this flag is true and SuperCollider interprets incoming MIDI noteOn message with velocity 0 as noteOff messages. In case you do not want this automatic translation, you can set this flag to false

*connectAll (verbose: true)

From extension in /usr/local/share/SuperCollider/SCClassLibrary/Platform/linux/extMIDIOut.sc

Connect to all possible MIDI inputs.

Arguments:

verbose

If set to true (default) it will print out the ports found in MIDIClient.init.

*disconnectAll

Disconnect from all MIDI inputs.

Getter/Setters for Specific Message Types

The methods below allow you to register a function to respond to a particular message type.

NOTE: It is preferable to use the *addFuncTo, *removeFuncFrom and *replaceFuncTo methods above instead of these methods, as they will not overwrite any functions added by system objects.

*noteOn

*noteOn = value

Arguments:

(value)

a Function evaluated whenever a MIDI noteOn message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
MIDIchannel
ranges from 0 to 15
keyNumber
0 - 127
velocity
0 - 127

*noteOff

*noteOff = value

Arguments:

(value)

a Function evaluated whenever a MIDI noteOff message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
MIDIchannel
ranges from 0 to 15
keyNumber
0 - 127
velocity
0 - 127 (typically 64 unless noteOff velocity is supported)

*polytouch

*polytouch = value

Arguments:

(value)

a Function evaluated whenever a MIDI polytouch message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
MIDIchannel
ranges from 0 to 15
keyNumber
0 - 127
pressure
0 - 127

*control

*control = value

Arguments:

(value)

a Function evaluated whenever a MIDI control change message (CC) is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
MIDIchannel
ranges from 0 to 15
controllerNumber
0 - 127
value
0 - 127

*program

*program = value

Arguments:

(value)

a Function evaluated whenever a MIDI program change message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
MIDIchannel
ranges from 0 to 15
programNumber
0 - 127

*touch

*touch = value

Arguments:

(value)

a Function evaluated whenever a MIDI after-touch message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
MIDIchannel
ranges from 0 to 15
pressure
0 - 127

*bend

*bend = value

Arguments:

(value)

a Function evaluated whenever a MIDI pitch wheel change message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
MIDIchannel
ranges from 0 to 15
bend
0 - 16383 (14bits, the midpoint is 8192)

*sysex

*sysex = value

NOTE: The current implementation assembles a complete system exclusive packet before evaluating the function.

Arguments:

(value)

a Function evaluated whenever a MIDI System Exclusive message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
data
an Int8Array (includes f0 and f7). See manufacturer references for details.

*sysrt

*sysrt = value

indexdatamessage
214bitssong pointer
37bitssong select
8midiclock
10start
11continue
12stop

Arguments:

(value)

a Function evaluated whenever a MIDI System Real-Time message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
index
ranges from 0 to 15
data

*smpte

*smpte = value

Over MIDI, SMPTE is transmitted at 1/4 frame intervals four times faster than the frame rate.
indexdata
0frames low nibble
1frames hi nibble
2seconds low nibble
3seconds hi nibble
4minutes low nibble
5minutes hi nibble
6hours low nibble
7hours hi bit OR'ed with frameRate
  • 0 -> 24 fps
  • 2 -> 25 fps
  • 4 -> 30 fps drop frame
  • 6 -> 30 fps

Nibbles are sent in ascending order.

Arguments:

(value)

a Function evaluated whenever a MIDI smpte message is received. It is passed the following arguments:

uid
unique identifier of the MIDI port
index
ranges from 0 to 7
data
0 - 15 (4bits)

Inherited class methods

Undocumented class methods

*action

*action = value

*bendList

*bendList = value

*connect (inport: 0, device: 0)

*controlList

*controlList = value

*disconnect (inport: 0, device: 0)

*doAction (src, status, a, b, c)

*doBendAction (src, chan, val)

*doControlAction (src, chan, num, val)

*doInvalidSysexAction (src, packet)

*doNoteOffAction (src, chan, num, veloc)

*doNoteOnAction (src, chan, num, veloc)

*doPolyTouchAction (src, chan, num, val)

*doProgramAction (src, chan, val)

*doSMPTEaction (src, frameRate, timecode)

*doSysexAction (src, packet)

*doSysrtAction (src, index, val)

*doTouchAction (src, chan, val)

*invalid

*invalid = value

*noteOffList

*noteOffList = value

*noteOnList

*noteOnList = value

*polyList

*polyList = value

*programList

*programList = value

*touchList

*touchList = value

*waitBend (port, chan, val)

*waitControl (port, chan, num, val)

*waitNoteOff (port, chan, note, veloc)

*waitNoteOn (port, chan, note, veloc)

*waitPoly (port, chan, note, veloc)

*waitProgram (port, chan, num)

*waitTouch (port, chan, val)

Instance Methods

Inherited instance methods

Examples

Quick start for 1 port

(
MIDIIn.connect;    // init for one port midi interface
// register functions:
~noteOff = { arg src, chan, num, vel;    [chan,num,vel / 127].postln; };
~noteOn = { arg src, chan, num, vel;    [chan,num,vel / 127].postln; };
~polytouch = { arg src, chan, num, vel;    [chan,num,vel / 127].postln; };
~control = { arg src, chan, num, val;    [chan,num,val].postln; };
~program = { arg src, chan, prog;        [chan,prog].postln; };
~touch = { arg src, chan, pressure;    [chan,pressure].postln; };
~bend = { arg src, chan, bend;        [chan,bend - 8192].postln; };
~sysex = { arg src, sysex;        sysex.postln; };
~sysrt = { arg src, chan, val;        [chan,val].postln; };
~smpte = { arg src, chan, val;        [chan,val].postln; };
MIDIIn.addFuncTo(\noteOn, ~noteOn);
MIDIIn.addFuncTo(\noteOff, ~noteOff);
MIDIIn.addFuncTo(\polytouch, ~polytouch);
MIDIIn.addFuncTo(\control, ~control);
MIDIIn.addFuncTo(\program, ~program);
MIDIIn.addFuncTo(\touch, ~touch);
MIDIIn.addFuncTo(\bend, ~bend);
MIDIIn.addFuncTo(\sysex, ~sysex);
MIDIIn.addFuncTo(\sysrt, ~sysrt);
MIDIIn.addFuncTo(\smpte, ~smpte);
)

//cleanup
(
MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
MIDIIn.removeFuncFrom(\noteOff, ~noteOff);
MIDIIn.removeFuncFrom(\polytouch, ~polytouch);
MIDIIn.removeFuncFrom(\control, ~control);
MIDIIn.removeFuncFrom(\program, ~program);
MIDIIn.removeFuncFrom(\touch, ~touch);
MIDIIn.removeFuncFrom(\bend, ~bend);
MIDIIn.removeFuncFrom(\sysex, ~sysex);
MIDIIn.removeFuncFrom(\sysrt, ~sysrt);
MIDIIn.removeFuncFrom(\smpte, ~smpte);
)

Quick start for 2 or more ports

(
    var inPorts = 2;
    var outPorts = 2;
    MIDIClient.init(inPorts,outPorts);    // explicitly intialize the client
    inPorts.do({ arg i;
        MIDIIn.connect(i, MIDIClient.sources.at(i));
    });
)

example with sound

MIDIIn.connect;
s.boot;

(
SynthDef("sik-goo", { arg freq=440,formfreq=100,gate=0.0,bwfreq=800;
    var x;
    x = Formant.ar(
        SinOsc.kr(0.02, 0, 10, freq),
        formfreq,
        bwfreq
    );
    x = EnvGen.kr(Env.adsr, gate,Latch.kr(gate,gate)) * x;
    Out.ar(0, x);
}).add;
)

x = Synth("sik-goo");

//set the action:
(
~noteOn = {arg src, chan, num, vel;
    x.set(\freq, num.midicps / 4.0);
    x.set(\gate, vel / 200 );
    x.set(\formfreq, vel / 127 * 1000);
};
MIDIIn.addFuncTo(\noteOn, ~noteOn);

~noteOff = { arg src,chan,num,vel;
    x.set(\gate, 0.0);
};
MIDIIn.addFuncTo(\noteOff, ~noteOff);

~bend = { arg src,chan,val;
    //(val * 0.048828125).postln;
    x.set(\bwfreq, val * 0.048828125 );
};
MIDIIn.addFuncTo(\bend, ~bend);
)

//cleanup
MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
MIDIIn.removeFuncFrom(\noteOff, ~noteOff);
MIDIIn.removeFuncFrom(\bend, ~bend);

writing to the bus rather than directly to the synth

//i used this and got acceptable latency for triggering synths live.
//The latency might actually be less than sc2, but i haven't used it enough
//to tell for sure yet.
//Powerbook G4, 512mb ram.
//- matrix6k@somahq.com

s.boot;

(
SynthDef("moto-rev", { arg ffreq=100;
    var x;
    x = RLPF.ar(LFPulse.ar(SinOsc.kr(0.2, 0, 10, 21), [0,0.1], 0.1),
        ffreq, 0.1)
        .clip2(0.4);
    Out.ar(0, x);
}).add;
)

b = Bus.control(s);

x = Synth("moto-rev");

// map the synth's first input (ffreq) to read
// from the bus' output index
x.map(0, b);


MIDIIn.connect;
//set the action:
(
~noteOn = {arg src, chan, num, vel;
    b.value = num.midicps.postln;
};
MIDIIn.addFuncTo(\noteOn, ~noteOn);

~control = {arg src, chan, num, val;
    [chan,num,val].postln;
};
MIDIIn.addFuncTo(\control, ~control);

~bend = {arg src, chan, val;
    val.postln;
};
MIDIIn.addFuncTo(\bend, ~bend);
)

// cleanup
x.free;
b.free;
MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
MIDIIn.removeFuncFrom(\control, ~control);
MIDIIn.removeFuncFrom(\bend, ~bend);

Keyboard Split for two voices

//pbend to cutoff, mod to rez, 7 to amp
//- matrix6k@somahq.com

s.boot;
(
SynthDef("funk",{ arg freq = 700, amp = 0.2, gate = 1, cutoff = 20000, rez = 1, lfospeed=0;
    var e,x,env,range,filterfreq;
    e = Env.new([0, 0.1, 0.1, 0], [0, 0.1, 0.1], 'linear', 2);
    env=Env.adsr(0.3,1,1,1);
    range = cutoff -1;
    filterfreq = SinOsc.kr(lfospeed,0, range, cutoff).abs;
    x = RLPF.ar(Mix.ar([
            Mix.arFill(2, {Saw.ar(freq *2 + 0.2.rand2, amp)}),
            Mix.arFill(2, {Saw.ar(freq *4+ 0.2.rand2, amp)})
        ]),
        EnvGen.kr(env,gate)*filterfreq,
        rez);
    Out.ar([0,1],x * EnvGen.kr(e, gate, doneAction: 2))
}).add;

SynthDef("strings",{ arg freq = 700, amp = 0.2, gate = 1;
    var x,enve;
    enve = Env.new([0, 0.1, 0.1, 0], [2, 0.1, 1], 'linear', 2);
    x = RLPF.ar(Mix.ar([
            Mix.arFill(2, {Saw.ar(freq +2.rand2,0.6)}),
            Mix.arFill(2, {Saw.ar(freq *0.5 + 2.rand2,0.6)})
        ]),
        6000,1);
    Out.ar([0,1],x * EnvGen.kr(enve, gate, doneAction: 2))
}).add;
)

(
var keys, cutspec, cutbus, rezspec, rezbus, lfospec, lfobus;
keys = Array.newClear(128);

MIDIClient.init;
MIDIIn.connect(0, MIDIClient.sources.at(0));

g = Group.new;

cutspec = ControlSpec(100,10000,\linear,0.001);
cutbus = Bus.new(\control,1,1,s);
cutbus.value = 10000;

rezspec = ControlSpec(1,0,\linear,0.001);
rezbus = Bus.new(\control,2,1,s);
rezbus.value = 1.0;

lfospec = ControlSpec(0,50,\linear,0.001);
lfobus = Bus.new(\control,3,1,s);

~control = {arg src, chan, num, val;
    if(num == 1,{
        rezbus.value = rezspec.map(val/127.0);
    });
    if(num == 7,{
        lfobus.value = lfospec.map(val/127.0).postln;
    });
};
MIDIIn.addFuncTo(\control, ~control);

~bend = {arg src, chan, val;
    cutbus.value = cutspec.map(val/16383.0);
};
MIDIIn.addFuncTo(\bend, ~bend);

~noteOn = {arg src, chan, num, vel;
    var node;
    if(num < 60, {
        node = Synth.tail(g, "funk", [\freq, num.midicps, \amp, vel/255]);
        node.map("cutoff",1,"rez",2,"lfospeed",3);
//        node = Synth.basicNew("funk",s);
//        s.sendBundle(nil,
//            node.addToTailMsg(g,[\freq, num.midicps, \amp, vel/255]),
//            node.mapMsg("cutoff",1,"rez",2,"lfospeed",3)
//        );
        keys.put(num, node)
    },{
        node = Synth.tail(g, "strings", [\freq, num.midicps, \amp, vel/255]);
        keys.put(num, node)
    });
};
MIDIIn.addFuncTo(\noteOn, ~noteOn);

~noteOff = {arg src, chan, num, vel;
    var node;
    node = keys.at(num);
    if (node.notNil, {
        keys.put(num, nil);
        s.sendMsg("/n_set", node.nodeID, "gate", 0);
        // or node.release
        // then free it ... or get the NodeWatcher to do it
    });
};
MIDIIn.addFuncTo(\noteOff, ~noteOff);
)

//cleanup
MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
MIDIIn.removeFuncFrom(\control, ~control);
MIDIIn.removeFuncFrom(\bend, ~bend);