scide: LookupDialog - redo lookup on classes after partial lookup
[supercollider.git] / SCClassLibrary / Common / Control / MIDIOut.sc
blob98ff7d75c38d645335162fccdf6344b0705e5592
1 MIDIEndPoint {
2         var <>device, <>name, <>uid;
3         *new{ arg device, name, uid;
4                 ^super.newCopyArgs(device, name, uid)
5         }
6         printOn { arg stream;
7                 stream << this.class.name << "(" <<<*
8                         [device, name]  <<")"
9         }
12 MIDIClient {
13         classvar <sources, <destinations;
14         classvar <initialized=false;
15         *init { arg inports, outports; // by default initialize all available ports
16                                                                 // you still must connect to them using MIDIIn.connect
18                 this.prInitClient;
19                 this.list;
20                 if(inports.isNil,{inports = sources.size});
21                 if(outports.isNil,{outports = destinations.size});
22 //                      this.disposeClient;
24                 this.prInit(inports,outports);
25                 initialized = true;
26                 // might ask for 1 and get 2 if your device has it
27                 // or you might ask for a 1 and get 0 of nothing is plugged in
28                 // so we warn you
29                 if(sources.size < inports or: {destinations.size < outports},{
30                         "WARNING:".postln;
31                         ("MIDIClient-init requested " ++ inports ++ " inport(s) and " ++ outports
32                                 ++ " outport(s),").postln;
33                         ("but found only " ++ sources.size ++ " inport(s) and " ++ destinations.size
34                                 ++ " outport(s).").postln;
35                         "Some expected MIDI devices may not be available.".postln;
36                 });
38                 this.list;
40                 ShutDown.add { this.disposeClient };
42                 Post << "MIDI Sources:" << Char.nl;
43                 sources.do({ |x| Post << Char.tab << x << Char.nl });
44                 Post << "MIDI Destinations:" << Char.nl;
45                 destinations.do({ |x| Post << Char.tab << x << Char.nl });
46         }
47         *list {
48                 var list;
49                 list = this.prList;
50                 if(list.notNil, {
51                         sources = list.at(0).collect({ arg id,i;
52                                 MIDIEndPoint(list.at(1).at(i), list.at(2).at(i), id)
53                         });
54                         destinations = list.at(3).collect({arg id, i;
55                                 MIDIEndPoint(list.at(5).at(i), list.at(4).at(i), id)
56                         });
57                 });
58         }
59         *prInit { arg inports, outports;
60                 _InitMIDI
61                 ^this.primitiveFailed
62         }
63         *prInitClient {
64                 _InitMIDIClient
65                 ^this.primitiveFailed
66         }
67         *prList {
68                 _ListMIDIEndpoints
69                 ^this.primitiveFailed
70         }
71         *disposeClient {
72                 _DisposeMIDIClient
73                 ^this.primitiveFailed
74         }
75         *restart {
76                 _RestartMIDI
77                 ^this.primitiveFailed
78         }
82 MIDIEvent {
83         var <>status, <>port, <>chan, <>b, <>c, <>thread;
85         *new { arg status, port, chan, b, c, thread;
86                 ^super.newCopyArgs(status, port, chan, b, c, thread)
87         }
88         set { arg inStatus, inPort, inChan, inB, inC, inThread;
89                 status = inStatus;
90                 port = inPort;
91                 chan = inChan;
92                 b = inB;
93                 c = inC;
94                 inThread !? { thread = inThread };
95         }
96         match { arg inPort, inChan, inB, inC;
97                 ^port.matchItem(inPort) and: {
98                         chan.matchItem(inChan) and: {
99                         b.matchItem(inB) and: {
100                         c.matchItem(inC)
101                 }}}
102         }
103         // convenience accessors
104         note { ^b }
105         veloc { ^c }
106         ctlnum { ^b }
107         ctlval { ^c }
110 MIDIIn {
111         var port;
112         classvar <>action,
113         <> noteOn, <> noteOff, <> polytouch,
114         <> control, <> program,
115         <> touch, <> bend,
116         <> sysex, sysexPacket, <> sysrt, <> smpte, <> invalid;
118         classvar
119         <> noteOnList, <> noteOffList, <> polyList,
120         <> controlList, <> programList,
121         <> touchList, <> bendList;
123         // safer than global setters
124         *addFuncTo { |what, func|
125                 this.perform(what.asSetter, this.perform(what).addFunc(func))
126         }
128         *removeFuncFrom { |what, func|
129                 this.perform(what.asSetter, this.perform(what).removeFunc(func))
130         }
132         *replaceFuncTo { |what, func, newFunc|
133                 this.perform(what.asSetter, this.perform(what).replaceFunc(func, newFunc))
134         }
136         *waitNoteOn { arg port, chan, note, veloc;
137                 var event;
138                 event = MIDIEvent(\noteOn, port, chan, note, veloc, thisThread);
139                 noteOnList = noteOnList.add(event); // add to waiting list
140                 nil.yield; // pause the thread.
141                 ^event
142         }
143         *waitNoteOff { arg port, chan, note, veloc;
144                 var event;
145                 event = MIDIEvent(\noteOff, port, chan, note, veloc, thisThread);
146                 noteOffList = noteOffList.add(event); // add to waiting list
147                 nil.yield; // pause the thread.
148                 ^event
149         }
150         *waitPoly { arg port, chan, note, veloc;
151                 var event;
152                 event = MIDIEvent(\poly, port, chan, note, veloc, thisThread);
153                 polyList = polyList.add(event); // add to waiting list
154                 nil.yield; // pause the thread.
155                 ^event
156         }
157         *waitTouch { arg port, chan, val;
158                 var event;
159                 event = MIDIEvent(\touch, port, chan, val, nil, thisThread);
160                 touchList = touchList.add(event); // add to waiting list
161                 nil.yield; // pause the thread.
162                 ^event
163         }
164         *waitControl { arg port, chan, num, val;
165                 var event;
166                 event = MIDIEvent(\control, port, chan, num, val, thisThread);
167                 controlList = controlList.add(event); // add to waiting list
168                 nil.yield; // pause the thread.
169                 ^event
170         }
171         *waitBend { arg port, chan, val;
172                 var event;
173                 event = MIDIEvent(\bend, port, chan, val, nil, thisThread);
174                 bendList = bendList.add(event); // add to waiting list
175                 nil.yield; // pause the thread.
176                 ^event
177         }
178         *waitProgram { arg port, chan, num;
179                 var event;
180                 event = MIDIEvent(\program, port, chan, num, nil, thisThread);
181                 programList = programList.add(event); // add to waiting list
182                 nil.yield; // pause the thread.
183                 ^event
184         }
186         *doAction { arg src, status, a, b, c;
187                 action.value(src, status, a, b, c);
188         }
189         *doNoteOnAction { arg src, chan, num, veloc;
190                 noteOn.value(src, chan, num, veloc);
191                 this.prDispatchEvent(noteOnList, \noteOn, src, chan, num, veloc);
192         }
193         *doNoteOffAction { arg src, chan, num, veloc;
194                 noteOff.value(src, chan, num, veloc);
195                 this.prDispatchEvent(noteOffList, \noteOff, src, chan, num, veloc);
196         }
197         *doPolyTouchAction { arg src, chan, num, val;
198                 polytouch.value(src, chan, num, val);
199                 this.prDispatchEvent(polyList, \poly, src, chan, num, val);
200         }
201         *doControlAction { arg src, chan, num, val;
202                 control.value(src, chan, num, val);
203                 this.prDispatchEvent(controlList, \control, src, chan, num, val);
204         }
205         *doProgramAction { arg src, chan, val;
206                 program.value(src, chan, val);
207                 this.prDispatchEvent(programList, \program, src, chan, val);
208         }
209         *doTouchAction { arg src, chan, val;
210                 touch.value(src, chan, val);
211                 this.prDispatchEvent(touchList, \touch, src, chan, val);
212         }
213         *doBendAction { arg src, chan, val;
214                 bend.value(src, chan, val);
215                 this.prDispatchEvent(bendList, \bend, src, chan, val);
216         }
218         *doSysexAction { arg src,  packet;
219                 sysexPacket = sysexPacket ++ packet;
220                 if (packet.last == -9, {
221                         sysex.value(src, sysexPacket);
222                         sysexPacket = nil
223                 });
224         }
225         *doInvalidSysexAction { arg src, packet;
226                 invalid.value(src, packet);
227         }
229         *doSysrtAction { arg src, index, val;
230                 sysrt.value(src, index, val);
231         }
233         *doSMPTEaction { arg src, frameRate, timecode;
234                 smpte.value(src, frameRate, timecode);
235         }
237         *findPort { arg deviceName,portName;
238                 ^MIDIClient.sources.detect({ |endPoint| endPoint.device == deviceName and: {endPoint.name == portName}});
239         }
240         *connectAll {
241                 if(MIDIClient.initialized.not,{ MIDIClient.init });
242                 MIDIClient.sources.do({ |src,i|
243                         MIDIIn.connect(i,src);
244                 });
245         }
246         *connect { arg inport=0, device=0;
247                 var uid,source;
248                 if(MIDIClient.initialized.not,{ MIDIClient.init });
249                 if(device.isNumber, {
250                         if(device >= 0, {
251                                 if ( device > MIDIClient.sources.size,{ // on linux the uid's are very large numbers
252                                         source = MIDIClient.sources.detect{ |it| it.uid == device };
253                                         if(source.isNil,{
254                                                 ("MIDI device with uid"+device+ "not found").warn;
255                                         },{
256                                                 uid = source.uid;
257                                         })
258                                 },{
259                                         source = MIDIClient.sources.at(device);
260                                         if(source.isNil,{
261                                                 "MIDIClient failed to init".warn;
262                                         },{
263                                                 uid = MIDIClient.sources.at(device).uid;
264                                         });
265                                 });
266                         },{ // elsewhere they tend to be negative
267                                 uid = device;
268                         });
269                 },{
270                         if(device.isKindOf(MIDIEndPoint), {uid = device.uid}); // else error
271                 });
272                 this.connectByUID(inport,uid);
273         }
274         *disconnect { arg inport=0, device=0;
275                 var uid, source;
276                 if(device.isKindOf(MIDIEndPoint), {uid = device.uid});
277                 if(device.isNumber, {
278                         if(device.isPositive, {
279                                 if ( device > MIDIClient.sources.size,
280                                         {
281                                                 source = MIDIClient.sources.select{ |it| it.uid == device }.first;
282                                                 if(source.isNil,{
283                                                         ("MIDI device with uid"+device+ "not found").warn;
284                                                 },{
285                                                         uid = source.uid;
286                                                 })
287                                         },
288                                         {
289                                                 source = MIDIClient.sources.at(device);
290                                                 if(source.isNil,{
291                                                         "MIDIClient failed to init".warn;
292                                                 },{
293                                                         uid = MIDIClient.sources.at(device).uid;
294                                                 });
295                                         });
296                         },{
297                                 uid = device;
298                         });
299                 });
300                 this.disconnectByUID(inport,uid);
301         }
302         *connectByUID {arg inport, uid;
303                 _ConnectMIDIIn
304         }
305         *disconnectByUID {arg inport, uid;
306                 _DisconnectMIDIIn
307         }
309         *prDispatchEvent { arg eventList, status, port, chan, b, c;
310                 var selectedEvents;
311                 eventList ?? {^this};
312                 eventList.takeThese {| event |
313                         if (event.match(port, chan, b, c))
314                         {
315                                 selectedEvents = selectedEvents.add(event);
316                                 true
317                         }
318                         { false };
319                 };
320                 selectedEvents.do{ |event|
321                                 event.set(status, port, chan, b, c);
322                                 event.thread.next;
323                 }
324         }
327 MIDIOut {
328         var <>port, <>uid, <>latency=0.2;
330         *new { arg port, uid;
331                 if(thisProcess.platform.name != \linux) {
332                         ^super.newCopyArgs(port, uid ?? { MIDIClient.destinations[port].uid });
333                 } {
334                         ^super.newCopyArgs(port, uid ?? 0 );
335                 }
336         }
337         *newByName { arg deviceName,portName,dieIfNotFound=true;
338                 var endPoint,index;
339                 endPoint = MIDIClient.destinations.detect({ |ep,epi|
340                         index = epi;
341                         ep.device == deviceName and: {ep.name == portName}
342                 });
343                 if(endPoint.isNil,{
344                         if(dieIfNotFound,{
345                                 Error("Failed to find MIDIOut port " + deviceName + portName).throw;
346                         },{
347                                 ("Failed to find MIDIOut port " + deviceName + portName).warn;
348                         });
349                 });
350                 ^this.new(index,endPoint.uid)
351         }
352         *findPort { arg deviceName,portName;
353                 ^MIDIClient.destinations.detect({ |endPoint| endPoint.device == deviceName and: {endPoint.name == portName}});
354         }
356         write { arg len, hiStatus, loStatus, a=0, b=0;
357                 this.send(port, uid, len, hiStatus, loStatus, a, b, latency);
358         }
360         noteOn { arg chan, note=60, veloc=64;
361                 this.write(3, 16r90, chan.asInteger, note.asInteger, veloc.asInteger);
362         }
363         noteOff { arg chan, note=60, veloc=64;
364                 this.write(3, 16r80, chan.asInteger, note.asInteger, veloc.asInteger);
365         }
366         polyTouch { arg chan, note=60, val=64;
367                 this.write(3, 16rA0, chan.asInteger, note.asInteger, val.asInteger);
368         }
369         control { arg chan, ctlNum=7, val=64;
370                 this.write(3, 16rB0, chan.asInteger, ctlNum.asInteger, val.asInteger);
371         }
372         program { arg chan, num=1;
373                 this.write(2, 16rC0, chan.asInteger, num.asInteger);
374         }
375         touch { arg chan, val=64;
376                 this.write(2, 16rD0, chan.asInteger, val.asInteger);
377         }
378         bend { arg chan, val=8192;
379                 val = val.asInteger;
380                 this.write(3, 16rE0, chan, val bitAnd: 127, val >> 7);
381         }
382         allNotesOff { arg chan;
383                 this.control(chan, 123, 0);
384         }
385         smpte   { arg frames=0, seconds=0, minutes=0, hours=0, frameRate = 3;
386                 var packet;
387                 packet = [frames, seconds, minutes, hours]
388                         .asInteger
389                         .collect({ arg v, i; [(i * 2 << 4) | (v & 16rF), (i * 2 + 1 << 4) | (v >> 4) ] });
390                 packet = packet.flat;
391                 packet.put(7, packet.at(7) | ( frameRate << 1 ) );
392                 packet.do({ arg v; this.write(2, 16rF0, 16r01, v); });
393         }
394         songPtr { arg songPtr;
395                 songPtr = songPtr.asInteger;
396                 this.write(4, 16rF0, 16r02, songPtr & 16r7f, songPtr >> 7 & 16r7f);
397         }
398         songSelect { arg song;
399                 this.write(3, 16rF0, 16r03, song.asInteger);
400         }
401         midiClock {
402                 this.write(1, 16rF0, 16r08);
403         }
404         start {
405                 this.write(1, 16rF0, 16r0A);
406         }
407         continue {
408                 this.write(1, 16rF0, 16r0B);
409         }
410         stop {
411                 this.write(1, 16rF0, 16r0C);
412         }
413         reset {
414                 this.write(1, 16rF0, 16r0F);
415         }
417         sysex { arg packet;
418                 ^this.prSysex( uid, packet );
419         }
421         send { arg outport, uid, len, hiStatus, loStatus, a=0, b=0, late;
422                 _SendMIDIOut
423         }
425         prSysex { arg uid, packet;
426                 _SendSysex
427                 ^this.primitiveFailed;
428         }