class:: MIDIIn
summary:: receive MIDI messages
related:: Classes/MIDIClient, Classes/MIDIOut, Classes/MIDIFunc, Classes/MIDIdef, Guides/MIDI, Guides/UsingMIDI
categories:: External Control>MIDI

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, strong::users should not use the MIDIIn class directly::. See the link::Classes/MIDIFunc:: and link::Classes/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 link::Guides/UsingMIDI:: helpfile for practical considerations and techniques for using MIDI in SC.

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


ClassMethods::

private::prDispatchEvent, connectByUID, disconnectByUID

method::findPort
searches for a connected link::Classes/MIDIEndPoint:: by name.
code::
//list connected ins:
MIDIClient.init;
MIDIClient.sources;
::

method::addFuncTo
Add a link::Classes/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.

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

argument::func
A link::Classes/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.

method::removeFuncFrom
Remove a link::Classes/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.

argument::what
A link::Classes/Symbol:: indicating the message type, one of code::'noteOn':: code::'noteOff'::, code::'polytouch'::, code::'control'::, code::'program'::, code::'touch'::, code::'bend'::, code::'sysex'::, code::'sysrt'::, code::'smpte'::, or code::'invalid':: (for invalid sysex messages).

argument::func
The link::Classes/Function:: or similar object to be removed.

method::replaceFuncTo
Replace a link::Classes/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.

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

argument::func
The link::Classes/Function:: or similar object to be replaced.

argument::newFunc
A link::Classes/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.


method::noteOnZeroAsNoteOff

By default this flag is code::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 code::false::


method::connectAll

Connect to all possible MIDI inputs.

argument::verbose

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

method::disconnectAll

Disconnect from all MIDI inputs.


subsection:: 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 link::#*addFuncTo::, link::#*removeFuncFrom:: and link::#*replaceFuncTo:: methods above instead of these methods, as they will not overwrite any functions added by system objects.::

method::noteOn
argument::value
a link::Classes/Function:: evaluated whenever a MIDI noteOn message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## MIDIchannel || ranges from 0 to 15
## keyNumber || 0 - 127
## velocity || 0 - 127
::

method::noteOff
argument::value
a link::Classes/Function:: evaluated whenever a MIDI noteOff message is received. It is passed the following arguments:
definitionList::
## 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)
::

method::polytouch
argument::value
a link::Classes/Function:: evaluated whenever a MIDI polytouch message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## MIDIchannel || ranges from 0 to 15
## keyNumber || 0 - 127
## pressure || 0 - 127
::

method::control
argument::value
a link::Classes/Function:: evaluated whenever a MIDI control change message (CC) is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## MIDIchannel || ranges from 0 to 15
## controllerNumber || 0 - 127
## value || 0 - 127
::

method::program
argument::value
a link::Classes/Function:: evaluated whenever a MIDI program change message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## MIDIchannel || ranges from 0 to 15
## programNumber || 0 - 127
::

method::touch
argument::value
a link::Classes/Function:: evaluated whenever a MIDI after-touch message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## MIDIchannel || ranges from 0 to 15
## pressure || 0 - 127
::

method::bend
argument::value
a link::Classes/Function:: evaluated whenever a MIDI pitch wheel change message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## MIDIchannel || ranges from 0 to 15
## bend || 0 - 16383 (14bits, the midpoint is 8192)
::

method::sysex
note::
The current implementation assembles a complete system exclusive packet before evaluating the function.
::
argument::value
a link::Classes/Function:: evaluated whenever a MIDI System Exclusive message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## data || an link::Classes/Int8Array:: (includes f0 and f7). See manufacturer references for details.
::

method::sysrt
table::
## strong::index:: || strong::data:: || strong::message::
## 2 || 14bits || song pointer
## 3 || 7bits || song select
## 8 || || midiclock
## 10 || || start
## 11 || || continue
## 12 || || stop
::
argument::value
a link::Classes/Function:: evaluated whenever a MIDI System Real-Time message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## index || ranges from 0 to 15
## data ||
::

method::smpte
Over MIDI, SMPTE is transmitted at 1/4 frame intervals four times faster than the frame rate.
table::
## strong::index:: || strong::data::
## 0 || frames low nibble
## 1 || frames hi nibble
## 2 || seconds low nibble
## 3 || seconds hi nibble
## 4 || minutes low nibble
## 5 || minutes hi nibble
## 6 || hours low nibble
## 7 || hours hi emphasis::bit:: OR'ed with frameRate
list::
## 0 -> 24 fps
## 2 -> 25 fps
## 4 -> 30 fps drop frame
## 6 -> 30 fps
::
::
Nibbles are sent in ascending order.
argument::value
a link::Classes/Function:: evaluated whenever a MIDI smpte message is received. It is passed the following arguments:
definitionList::
## uid || unique identifier of the MIDI port
## index || ranges from 0 to 7
## data || 0 - 15 (4bits)
::

Examples::

subsection::Quick start for 1 port
code::
(
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);
)
::

subsection::Quick start for 2 or more ports
code::
(
	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));
	});
)
::

subsection::example with sound
code::
MIDIIn.connect;
s.boot;

(
SynthDef("sik-goo", { |out, 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(out, 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);
::

subsection::writing to the bus rather than directly to the synth
code::
//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", { |out, 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(out, 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);
::

subsection::Keyboard Split for two voices
code::
//pbend to cutoff, mod to rez, 7 to amp
//- matrix6k@somahq.com

s.boot;
(
SynthDef("funk",{ | out, 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(out, x * EnvGen.kr(e, gate, doneAction: Done.freeSelf))
}).add;

SynthDef("strings", { |out, 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(out, x * EnvGen.kr(enve, gate, doneAction: Done.freeSelf))
}).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);

::
