Forgot a help fix: Drag a dock's title bar, not divider, to reposition
[supercollider.git] / HelpSource / Classes / MIDIIn.schelp
blob67e3205f54efa7a57cbe274dd7fad5fedbaf55f4
1 class:: MIDIIn
2 summary:: receive MIDI messages
3 related:: Classes/MIDIOut, Classes/MIDIFunc, Classes/MIDIdef, Guides/MIDI, Guides/UsingMIDI
4 categories:: External Control>MIDI
6 description::
7 This document explains technical details of the MIDI hardware interface class, MIDIIn.
9 MIDIIn is a simple and direct interface. When MIDI events come into Super Collider, MIDIIn evaluates simple handler functions.
11 note::
12 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.
15 Certain MIDI messages are supported only through MIDIIn. These are: sysex, sysrt, smpte.
17 See the link::Guides/UsingMIDI:: helpfile for practical considerations and techniques for using MIDI in SC.
19 subsection::The MIDIIn class
21 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:
23         noteOff, noteOn, polytouch, control, program, touch, bend, sysex, sysrt, smpte
25 The first argument these functions receive is an unique identifier that specifies the source of the data.
28 ClassMethods::
30 private::prDispatchEvent, connectByUID, disconnectByUID
32 method::findPort
33 searches for a connected link::Classes/MIDIEndPoint:: by name.
34 code::
35 //list connected ins:
36 MIDIClient.init;
37 MIDIClient.sources;
40 method::addFuncTo
41 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.
43 argument::what
44 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).
46 argument::func
47 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.
49 method::removeFuncFrom
50 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.
52 argument::what
53 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).
55 argument::func
56 The link::Classes/Function:: or similar object to be removed.
58 method::replaceFuncTo
59 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.
61 argument::what
62 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).
64 argument::func
65 The link::Classes/Function:: or similar object to be replaced.
67 argument::newFunc
68 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.
70 subsection:: Getter/Setters for Specific Message Types
72 The methods below allow you to register a function to respond to a particular message type.
74 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.::
76 method::noteOn
77 argument::value
78 a link::Classes/Function:: evaluated whenever a MIDI noteOn message is received. It is passed the following arguments:
79 definitionList::
80 ## uid || unique identifier of the MIDI port
81 ## MIDIchannel || ranges from 0 to 15
82 ## keyNumber || 0 - 127
83 ## velocity || 0 - 127
86 method::noteOff
87 argument::value
88 a link::Classes/Function:: evaluated whenever a MIDI noteOff message is received. It is passed the following arguments:
89 definitionList::
90 ## uid || unique identifier of the MIDI port
91 ## MIDIchannel || ranges from 0 to 15
92 ## keyNumber || 0 - 127
93 ## velocity || 0 - 127 (typically 64 unless noteOff velocity is supported)
96 method::polytouch
97 argument::value
98 a link::Classes/Function:: evaluated whenever a MIDI polytouch message is received. It is passed the following arguments:
99 definitionList::
100 ## uid || unique identifier of the MIDI port
101 ## MIDIchannel || ranges from 0 to 15
102 ## keyNumber || 0 - 127
103 ## pressure || 0 - 127
106 method::control
107 argument::value
108 a link::Classes/Function:: evaluated whenever a MIDI control change message (CC) is received. It is passed the following arguments:
109 definitionList::
110 ## uid || unique identifier of the MIDI port
111 ## MIDIchannel || ranges from 0 to 15
112 ## controllerNumber || 0 - 127
113 ## value || 0 - 127
116 method::program
117 argument::value
118 a link::Classes/Function:: evaluated whenever a MIDI program change message is received. It is passed the following arguments:
119 definitionList::
120 ## uid || unique identifier of the MIDI port
121 ## MIDIchannel || ranges from 0 to 15
122 ## programNumber || 0 - 127
125 method::touch
126 argument::value
127 a link::Classes/Function:: evaluated whenever a MIDI after-touch message is received. It is passed the following arguments:
128 definitionList::
129 ## uid || unique identifier of the MIDI port
130 ## MIDIchannel || ranges from 0 to 15
131 ## pressure || 0 - 127
134 method::bend
135 argument::value
136 a link::Classes/Function:: evaluated whenever a MIDI pitch wheel change message is received. It is passed the following arguments:
137 definitionList::
138 ## uid || unique identifier of the MIDI port
139 ## MIDIchannel || ranges from 0 to 15
140 ## bend || 0 - 16383 (14bits, the midpoint is 8192)
143 method::sysex
144 note::
145 The current implementation assembles a complete system exclusive packet before evaluating the function.
147 argument::value
148 a link::Classes/Function:: evaluated whenever a MIDI System Exclusive message is received. It is passed the following arguments:
149 definitionList::
150 ## uid || unique identifier of the MIDI port
151 ## data || an link::Classes/Int8Array:: (includes f0 and f7). See manufacturer references for details.
154 method::sysrt
155 table::
156 ## strong::index:: || strong::data:: || strong::message::
157 ## 2 || 14bits || song pointer
158 ## 3 || 7bits || song select
159 ## 8 || || midiclock
160 ## 10 || || start
161 ## 11 || || continue
162 ## 12 || || stop
164 argument::value
165 a link::Classes/Function:: evaluated whenever a MIDI System Real-Time message is received. It is passed the following arguments:
166 definitionList::
167 ## uid || unique identifier of the MIDI port
168 ## index || ranges from 0 to 15
169 ## data ||
172 method::smpte
173 Over MIDI, SMPTE is transmitted at 1/4 frame intervals four times faster than the frame rate.
174 table::
175 ## strong::index:: || strong::data::
176 ## 0 || frames low nibble
177 ## 1 || frames hi nibble
178 ## 2 || seconds low nibble
179 ## 3 || seconds hi nibble
180 ## 4 || minutes low nibble
181 ## 5 || minutes hi nibble
182 ## 6 || hours low nibble
183 ## 7 || hours hi emphasis::bit:: OR'ed with frameRate
184 list::
185 ## 0 -> 24 fps
186 ## 2 -> 25 fps
187 ## 4 -> 30 fps drop frame
188 ## 6 -> 30 fps
191 Nibbles are sent in ascending order.
192 argument::value
193 a link::Classes/Function:: evaluated whenever a MIDI smpte message is received. It is passed the following arguments:
194 definitionList::
195 ## uid || unique identifier of the MIDI port
196 ## index || ranges from 0 to 7
197 ## data || 0 - 15 (4bits)
200 Examples::
202 subsection::Quick start for 1 port
203 code::
205 MIDIIn.connect; // init for one port midi interface
206 // register functions:
207 ~noteOff = { arg src, chan, num, vel;   [chan,num,vel / 127].postln; };
208 ~noteOn = { arg src, chan, num, vel;    [chan,num,vel / 127].postln; };
209 ~polytouch = { arg src, chan, num, vel; [chan,num,vel / 127].postln; };
210 ~control = { arg src, chan, num, val;   [chan,num,val].postln; };
211 ~program = { arg src, chan, prog;               [chan,prog].postln; };
212 ~touch = { arg src, chan, pressure;     [chan,pressure].postln; };
213 ~bend = { arg src, chan, bend;          [chan,bend - 8192].postln; };
214 ~sysex = { arg src, sysex;              sysex.postln; };
215 ~sysrt = { arg src, chan, val;          [chan,val].postln; };
216 ~smpte = { arg src, chan, val;          [chan,val].postln; };
217 MIDIIn.addFuncTo(\noteOn, ~noteOn);
218 MIDIIn.addFuncTo(\noteOff, ~noteOff);
219 MIDIIn.addFuncTo(\polytouch, ~polytouch);
220 MIDIIn.addFuncTo(\control, ~control);
221 MIDIIn.addFuncTo(\program, ~program);
222 MIDIIn.addFuncTo(\touch, ~touch);
223 MIDIIn.addFuncTo(\bend, ~bend);
224 MIDIIn.addFuncTo(\sysex, ~sysex);
225 MIDIIn.addFuncTo(\sysrt, ~sysrt);
226 MIDIIn.addFuncTo(\smpte, ~smpte);
229 //cleanup
231 MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
232 MIDIIn.removeFuncFrom(\noteOff, ~noteOff);
233 MIDIIn.removeFuncFrom(\polytouch, ~polytouch);
234 MIDIIn.removeFuncFrom(\control, ~control);
235 MIDIIn.removeFuncFrom(\program, ~program);
236 MIDIIn.removeFuncFrom(\touch, ~touch);
237 MIDIIn.removeFuncFrom(\bend, ~bend);
238 MIDIIn.removeFuncFrom(\sysex, ~sysex);
239 MIDIIn.removeFuncFrom(\sysrt, ~sysrt);
240 MIDIIn.removeFuncFrom(\smpte, ~smpte);
244 subsection::Quick start for 2 or more ports
245 code::
247         var inPorts = 2;
248         var outPorts = 2;
249         MIDIClient.init(inPorts,outPorts);      // explicitly intialize the client
250         inPorts.do({ arg i;
251                 MIDIIn.connect(i, MIDIClient.sources.at(i));
252         });
256 subsection::example with sound
257 code::
258 MIDIIn.connect;
259 s = Server.local;
260 s.boot;
263 SynthDef("sik-goo", { arg freq=440,formfreq=100,gate=0.0,bwfreq=800;
264         var x;
265         x = Formant.ar(
266                 SinOsc.kr(0.02, 0, 10, freq),
267                 formfreq,
268                 bwfreq
269         );
270         x = EnvGen.kr(Env.adsr, gate,Latch.kr(gate,gate)) * x;
271         Out.ar(0, x);
272 }).add;
275 x = Synth("sik-goo");
277 //set the action:
279 ~noteOn = {arg src, chan, num, vel;
280         x.set(\freq, num.midicps / 4.0);
281         x.set(\gate, vel / 200 );
282         x.set(\formfreq, vel / 127 * 1000);
284 MIDIIn.addFuncTo(\noteOn, ~noteOn);
286 ~noteOff = { arg src,chan,num,vel;
287         x.set(\gate, 0.0);
289 MIDIIn.addFuncTo(\noteOff, ~noteOff);
291 ~bend = { arg src,chan,val;
292         //(val * 0.048828125).postln;
293         x.set(\bwfreq, val * 0.048828125 );
295 MIDIIn.addFuncTo(\bend, ~bend);
298 //cleanup
299 MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
300 MIDIIn.removeFuncFrom(\noteOff, ~noteOff);
301 MIDIIn.removeFuncFrom(\bend, ~bend);
304 subsection::writing to the bus rather than directly to the synth
305 code::
306 //i used this and got acceptable latency for triggering synths live.
307 //The latency might actually be less than sc2, but i haven't used it enough
308 //to tell for sure yet.
309 //Powerbook G4, 512mb ram.
310 //- matrix6k@somahq.com
312 s = Server.local;
313 s.boot;
316 SynthDef("moto-rev", { arg ffreq=100;
317         var x;
318         x = RLPF.ar(LFPulse.ar(SinOsc.kr(0.2, 0, 10, 21), [0,0.1], 0.1),
319                 ffreq, 0.1)
320                 .clip2(0.4);
321         Out.ar(0, x);
322 }).add;
325 b = Bus.control(s);
327 x = Synth("moto-rev");
329 // map the synth's first input (ffreq) to read
330 // from the bus' output index
331 x.map(0, b);
334 MIDIIn.connect;
335 //set the action:
337 ~noteOn = {arg src, chan, num, vel;
338         b.value = num.midicps.postln;
340 MIDIIn.addFuncTo(\noteOn, ~noteOn);
342 ~control = {arg src, chan, num, val;
343         [chan,num,val].postln;
345 MIDIIn.addFuncTo(\control, ~control);
347 ~bend = {arg src, chan, val;
348         val.postln;
350 MIDIIn.addFuncTo(\bend, ~bend);
353 // cleanup
354 x.free;
355 b.free;
356 MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
357 MIDIIn.removeFuncFrom(\control, ~control);
358 MIDIIn.removeFuncFrom(\bend, ~bend);
361 subsection::Keyboard Split for two voices
362 code::
363 //pbend to cutoff, mod to rez, 7 to amp
364 //- matrix6k@somahq.com
366 s.boot;
368 SynthDef("funk",{ arg freq = 700, amp = 0.2, gate = 1, cutoff = 20000, rez = 1, lfospeed=0;
369         var e,x,env,range,filterfreq;
370         e = Env.new([0, 0.1, 0.1, 0], [0, 0.1, 0.1], 'linear', 2);
371         env=Env.adsr(0.3,1,1,1);
372         range = cutoff -1;
373         filterfreq = SinOsc.kr(lfospeed,0, range, cutoff).abs;
374         x = RLPF.ar(Mix.ar([
375                         Mix.arFill(2, {Saw.ar(freq *2 + 0.2.rand2, amp)}),
376                         Mix.arFill(2, {Saw.ar(freq *4+ 0.2.rand2, amp)})
377                 ]),
378                 EnvGen.kr(env,gate)*filterfreq,
379                 rez);
380         Out.ar([0,1],x * EnvGen.kr(e, gate, doneAction: 2))
381 }).add;
383 SynthDef("strings",{ arg freq = 700, amp = 0.2, gate = 1;
384         var x,enve;
385         enve = Env.new([0, 0.1, 0.1, 0], [2, 0.1, 1], 'linear', 2);
386         x = RLPF.ar(Mix.ar([
387                         Mix.arFill(2, {Saw.ar(freq +2.rand2,0.6)}),
388                         Mix.arFill(2, {Saw.ar(freq *0.5 + 2.rand2,0.6)})
389                 ]),
390                 6000,1);
391         Out.ar([0,1],x * EnvGen.kr(enve, gate, doneAction: 2))
392 }).add;
396 var keys, cutspec, cutbus, rezspec, rezbus, lfospec, lfobus;
397 keys = Array.newClear(128);
399 MIDIClient.init;
400 MIDIIn.connect(0, MIDIClient.sources.at(0));
402 g = Group.new;
404 cutspec = ControlSpec(100,10000,\linear,0.001);
405 cutbus = Bus.new(\control,1,1,s);
406 cutbus.value = 10000;
408 rezspec = ControlSpec(1,0,\linear,0.001);
409 rezbus = Bus.new(\control,2,1,s);
410 rezbus.value = 1.0;
412 lfospec = ControlSpec(0,50,\linear,0.001);
413 lfobus = Bus.new(\control,3,1,s);
415 ~control = {arg src, chan, num, val;
416         if(num == 1,{
417                 rezbus.value = rezspec.map(val/127.0);
418         });
419         if(num == 7,{
420                 lfobus.value = lfospec.map(val/127.0).postln;
421         });
423 MIDIIn.addFuncTo(\control, ~control);
425 ~bend = {arg src, chan, val;
426         cutbus.value = cutspec.map(val/16383.0);
428 MIDIIn.addFuncTo(\bend, ~bend);
430 ~noteOn = {arg src, chan, num, vel;
431         var node;
432         if(num < 60, {
433                 node = Synth.tail(g, "funk", [\freq, num.midicps, \amp, vel/255]);
434                 node.map("cutoff",1,"rez",2,"lfospeed",3);
435 //              node = Synth.basicNew("funk",s);
436 //              s.sendBundle(nil,
437 //                      node.addToTailMsg(g,[\freq, num.midicps, \amp, vel/255]),
438 //                      node.mapMsg("cutoff",1,"rez",2,"lfospeed",3)
439 //              );
440                 keys.put(num, node)
441         },{
442                 node = Synth.tail(g, "strings", [\freq, num.midicps, \amp, vel/255]);
443                 keys.put(num, node)
444         });
446 MIDIIn.addFuncTo(\noteOn, ~noteOn);
448 ~noteOff = {arg src, chan, num, vel;
449         var node;
450         node = keys.at(num);
451         if (node.notNil, {
452                 keys.put(num, nil);
453                 s.sendMsg("/n_set", node.nodeID, "gate", 0);
454                 // or node.release
455                 // then free it ... or get the NodeWatcher to do it
456         });
458 MIDIIn.addFuncTo(\noteOff, ~noteOff);
461 //cleanup
462 MIDIIn.removeFuncFrom(\noteOn, ~noteOn);
463 MIDIIn.removeFuncFrom(\control, ~control);
464 MIDIIn.removeFuncFrom(\bend, ~bend);