1 /*
2 Alsa MIDI/Sequencer support.
3 Copyright (c) 2004 stefan kersten.
4
5 ====================================================================
6
7 SuperCollider real time audio synthesis system
8 Copyright (c) 2002 James McCartney. All rights reserved.
9 http://www.audiosynth.com
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26 #include "SCBase.h"
27 #include "VMGlobals.h"
28 #include "PyrSymbolTable.h"
29 #include "PyrInterpreter.h"
30 #include "PyrKernel.h"
31
32 #include "PyrPrimitive.h"
33 #include "PyrObjectProto.h"
34 #include "PyrPrimitiveProto.h"
35 #include "PyrKernelProto.h"
36 #include "SC_InlineUnaryOp.h"
37 #include "SC_InlineBinaryOp.h"
38 #include "PyrSched.h"
39 #include "GC.h"
40 #include "SC_LanguageClient.h"
41
42 #include <atomic>
43 #include <thread>
44
45 PyrSymbol* s_midiin;
46 PyrSymbol* s_domidiaction;
47 PyrSymbol* s_midiNoteOnAction;
48 PyrSymbol* s_midiNoteOffAction;
49 PyrSymbol* s_midiTouchAction;
50 PyrSymbol* s_midiControlAction;
51 PyrSymbol* s_midiPolyTouchAction;
52 PyrSymbol* s_midiProgramAction;
53 PyrSymbol* s_midiBendAction;
54 PyrSymbol* s_midiSysexAction;
55 PyrSymbol* s_midiSysrtAction;
56 PyrSymbol* s_midiSMPTEAction;
57
58 const int kMaxMidiPorts = 128;
59 bool gMIDIInitialized = false;
60
61 extern bool compiledOK;
62
63 // =====================================================================
64 // Platform declarations (interface routines)
65 // =====================================================================
66
67 static int initClient();
68 static int initMIDI(int numIn, int numOut);
69 static int disposeMIDI();
70 static int restartMIDI();
71 static void cleanUpMIDI();
72
73 static int listMIDIEndpoints(struct VMGlobals* g, PyrSlot* a);
74 static int connectMIDIIn(int inputIndex, int uid);
75 static int disconnectMIDIIn(int inputIndex, int uid);
76 static int connectMIDIOut(int outputIndex, int uid);
77 static int disconnectMIDIOut(int outputIndex, int uid);
78
79 static int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late);
80 static int sendMIDISysex(int port, int destId, int length, uint8* data);
81
82 // =====================================================================
83 // Platform declarations (ALSA)
84 // =====================================================================
85
86 #include <alsa/asoundlib.h>
87 #include <vector>
88 #include <string.h>
89
90 static const size_t kAlsaMaxPacketSize = 3;
91 static const size_t kAlsaMaxPortNameLen = 256;
92
93 // MIDI packet
94 struct SC_AlsaMidiPacket {
95 uint8 data[kAlsaMaxPacketSize];
96 };
97
98 // MIDI client state
99 struct SC_AlsaMidiClient {
100 snd_seq_t* mHandle;
101 int mQueue;
102 int mNumInPorts;
103 int mInPorts[kMaxMidiPorts];
104 int mNumOutPorts;
105 int mOutPorts[kMaxMidiPorts];
106 snd_midi_event_t* mEventToMidi;
107 snd_midi_event_t* mMidiToEvent;
108 std::thread mInputThread;
109 std::atomic_bool mShouldBeRunning { false };
110
startInputThreadSC_AlsaMidiClient111 int startInputThread() {
112 mShouldBeRunning = true;
113 try {
114 std::thread inputThread([this] { inputThreadFunc(); });
115 mInputThread = std::move(inputThread);
116 return errNone;
117 } catch (...) {
118 post("MIDI (ALSA): could not start input thread\n");
119 return errFailed;
120 }
121 }
122
joinInputThreadSC_AlsaMidiClient123 void joinInputThread() {
124 mShouldBeRunning = false;
125 if (mInputThread.joinable())
126 mInputThread.join();
127 }
128
129 void inputThreadFunc();
130 void processEvent(snd_seq_event_t* evt);
131
132 int connectInput(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*),
133 const char* actionName);
134 int connectOutput(int outputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*),
135 const char* actionName);
136 int sendEvent(int outputIndex, int uid, snd_seq_event_t* evt, float late = 0.f);
137
138 int mClientID;
139 };
140
141 static SC_AlsaMidiClient gMIDIClient;
142
143 // Port description
144 struct SC_AlsaMidiPort {
SC_AlsaMidiPortSC_AlsaMidiPort145 SC_AlsaMidiPort(): uid(0) {
146 *name = 0;
147 *device = 0;
148 }
149
150 char name[kAlsaMaxPortNameLen];
151 char device[kAlsaMaxPortNameLen];
152 int32 uid;
153 };
154
155 // =====================================================================
156 // Platform implementation (ALSA)
157 // =====================================================================
158
SC_AlsaMakeUID(int clientID,int portID)159 static inline int SC_AlsaMakeUID(int clientID, int portID) { return (clientID << 16) | (portID & 0xFFFF); }
160
SC_AlsaParseUID(int uid,int & clientID,int & portID)161 static inline void SC_AlsaParseUID(int uid, int& clientID, int& portID) {
162 clientID = uid >> 16;
163 portID = uid & 0xFFFF;
164 }
165
processEvent(snd_seq_event_t * evt)166 void SC_AlsaMidiClient::processEvent(snd_seq_event_t* evt) {
167 int status = lockLanguageOrQuit(mShouldBeRunning);
168 if (status == EINTR)
169 return;
170 if (status) {
171 postfl("error when locking language (%d)\n", status);
172 return;
173 }
174
175 if (compiledOK) {
176 VMGlobals* g = gMainVMGlobals;
177 PyrInt8Array* sysexArray;
178
179 SC_AlsaMidiPacket pkt;
180
181 g->canCallOS = false; // cannot call the OS
182
183 // class MIDIIn
184 ++g->sp;
185 SetObject(g->sp, s_midiin->u.classobj);
186 // source
187 ++g->sp;
188 SetInt(g->sp, SC_AlsaMakeUID(evt->source.client, evt->source.port));
189
190 switch (evt->type) {
191 // midi events
192 case SND_SEQ_EVENT_NOTEOFF: // noteOff
193 ++g->sp;
194 SetInt(g->sp, evt->data.note.channel);
195 ++g->sp;
196 SetInt(g->sp, evt->data.note.note);
197 ++g->sp;
198 SetInt(g->sp, evt->data.note.velocity);
199 runInterpreter(g, s_midiNoteOffAction, 5);
200 break;
201 case SND_SEQ_EVENT_NOTEON: // noteOn
202 ++g->sp;
203 SetInt(g->sp, evt->data.note.channel);
204 ++g->sp;
205 SetInt(g->sp, evt->data.note.note);
206 ++g->sp;
207 SetInt(g->sp, evt->data.note.velocity);
208 // runInterpreter(g, evt->data.note.velocity ? s_midiNoteOnAction : s_midiNoteOffAction, 5);
209 runInterpreter(g, s_midiNoteOnAction, 5);
210 break;
211 case SND_SEQ_EVENT_KEYPRESS: // polytouch
212 ++g->sp;
213 SetInt(g->sp, evt->data.note.channel);
214 ++g->sp;
215 SetInt(g->sp, evt->data.note.note);
216 ++g->sp;
217 SetInt(g->sp, evt->data.note.velocity);
218 runInterpreter(g, s_midiPolyTouchAction, 5);
219 break;
220 case SND_SEQ_EVENT_CONTROLLER: // control
221 ++g->sp;
222 SetInt(g->sp, evt->data.control.channel);
223 ++g->sp;
224 SetInt(g->sp, evt->data.control.param);
225 ++g->sp;
226 SetInt(g->sp, evt->data.control.value);
227 runInterpreter(g, s_midiControlAction, 5);
228 break;
229 case SND_SEQ_EVENT_PGMCHANGE: // program
230 ++g->sp;
231 SetInt(g->sp, evt->data.control.channel);
232 ++g->sp;
233 SetInt(g->sp, evt->data.control.value);
234 runInterpreter(g, s_midiProgramAction, 4);
235 break;
236 case SND_SEQ_EVENT_CHANPRESS: // touch
237 ++g->sp;
238 SetInt(g->sp, evt->data.control.channel);
239 ++g->sp;
240 SetInt(g->sp, evt->data.control.value);
241 runInterpreter(g, s_midiTouchAction, 4);
242 break;
243 case SND_SEQ_EVENT_PITCHBEND: // bend
244 ++g->sp;
245 SetInt(g->sp, evt->data.control.channel);
246 ++g->sp;
247 SetInt(g->sp, evt->data.control.value + 8192);
248 runInterpreter(g, s_midiBendAction, 4);
249 break;
250 // system common events
251 case SND_SEQ_EVENT_QFRAME: // mtc quarter frame
252 {
253 int index = evt->data.control.value >> 4;
254 int data = evt->data.control.value & 0xf;
255
256 #if 0
257 post(
258 "mtc qframe: byte 0x%x index 0x%x data 0x%x\n",
259 evt->data.control.value,
260 index, data
261 );
262 #endif
263
264 switch (index) {
265 case 1:
266 case 3:
267 case 5:
268 case 7:
269 data = data << 4;
270 }
271
272 ++g->sp;
273 SetInt(g->sp, index);
274 ++g->sp;
275 SetInt(g->sp, data);
276 }
277 runInterpreter(g, s_midiSMPTEAction, 4);
278 break;
279 case SND_SEQ_EVENT_SONGPOS: // song ptr
280 ++g->sp;
281 SetInt(g->sp, evt->data.control.channel);
282 ++g->sp;
283 SetInt(g->sp, (evt->data.control.value << 7) | evt->data.control.param);
284 runInterpreter(g, s_midiSysrtAction, 4);
285 break;
286 case SND_SEQ_EVENT_SONGSEL: // song sel
287 ++g->sp;
288 SetInt(g->sp, evt->data.control.channel);
289 ++g->sp;
290 SetInt(g->sp, evt->data.control.param);
291 runInterpreter(g, s_midiSysrtAction, 4);
292 break;
293 // system realtime events
294 case SND_SEQ_EVENT_TUNE_REQUEST: // tuning request
295 ++g->sp;
296 SetInt(g->sp, 0x6);
297 ++g->sp;
298 SetInt(g->sp, 0);
299 runInterpreter(g, s_midiSysrtAction, 4);
300 break;
301 case SND_SEQ_EVENT_CLOCK: // clock
302 ++g->sp;
303 SetInt(g->sp, 0x8);
304 ++g->sp;
305 SetInt(g->sp, 0);
306 runInterpreter(g, s_midiSysrtAction, 4);
307 break;
308 case SND_SEQ_EVENT_TICK: // tick
309 ++g->sp;
310 SetInt(g->sp, 0x9);
311 ++g->sp;
312 SetInt(g->sp, 0);
313 runInterpreter(g, s_midiSysrtAction, 4);
314 break;
315 case SND_SEQ_EVENT_START: // start
316 ++g->sp;
317 SetInt(g->sp, 0xA);
318 ++g->sp;
319 SetInt(g->sp, 0);
320 runInterpreter(g, s_midiSysrtAction, 4);
321 break;
322 case SND_SEQ_EVENT_CONTINUE: // continue
323 ++g->sp;
324 SetInt(g->sp, 0xB);
325 ++g->sp;
326 SetInt(g->sp, 0);
327 runInterpreter(g, s_midiSysrtAction, 4);
328 break;
329 case SND_SEQ_EVENT_STOP: // stop
330 ++g->sp;
331 SetInt(g->sp, 0xC);
332 ++g->sp;
333 SetInt(g->sp, 0);
334 runInterpreter(g, s_midiSysrtAction, 4);
335 break;
336 case SND_SEQ_EVENT_SENSING: // active sensing
337 ++g->sp;
338 SetInt(g->sp, 0xE);
339 ++g->sp;
340 SetInt(g->sp, 0);
341 runInterpreter(g, s_midiSysrtAction, 4);
342 break;
343 case SND_SEQ_EVENT_RESET: // system reset
344 ++g->sp;
345 SetInt(g->sp, 0xF);
346 ++g->sp;
347 SetInt(g->sp, 0);
348 runInterpreter(g, s_midiSysrtAction, 4);
349 break;
350 // sysex events
351 case SND_SEQ_EVENT_SYSEX: // sysex
352 sysexArray = newPyrInt8Array(g->gc, evt->data.ext.len, 0, true);
353 memcpy(sysexArray->b, evt->data.ext.ptr, evt->data.ext.len);
354 sysexArray->size = evt->data.ext.len;
355 ++g->sp;
356 SetObject(g->sp, (PyrObject*)sysexArray);
357 runInterpreter(g, s_midiSysexAction, 3);
358 break;
359 default:
360 // unknown: convert to midi packet
361 snd_midi_event_reset_decode(mEventToMidi);
362 memset(pkt.data, 0, kAlsaMaxPacketSize);
363 if (snd_midi_event_decode(mEventToMidi, pkt.data, kAlsaMaxPacketSize, evt) > 0) {
364 for (size_t i = 0; i < kAlsaMaxPacketSize; i++) {
365 ++g->sp;
366 SetInt(g->sp, pkt.data[i]);
367 }
368 runInterpreter(g, s_domidiaction, 2 + kAlsaMaxPacketSize);
369 } else {
370 g->sp -= 2;
371 }
372 }
373 g->canCallOS = false;
374 }
375 gLangMutex.unlock();
376 }
377
inputThreadFunc()378 void SC_AlsaMidiClient::inputThreadFunc() {
379 int npfd = snd_seq_poll_descriptors_count(mHandle, POLLIN);
380 struct pollfd pfd[npfd];
381
382 snd_seq_poll_descriptors(mHandle, pfd, npfd, POLLIN);
383
384 while (mShouldBeRunning.load(std::memory_order_relaxed)) {
385 if (poll(pfd, npfd, 2000) > 0) { // 2s timeout
386 for (int i = 0; i < npfd; i++) {
387 if (pfd[i].revents > 0) {
388 do {
389 snd_seq_event_t* evt = nullptr;
390 int status = snd_seq_event_input(mHandle, &evt);
391 if (status > 0) {
392 assert(evt);
393 processEvent(evt);
394 snd_seq_free_event(evt);
395 }
396 } while (snd_seq_event_input_pending(mHandle, 0) > 0);
397 }
398 }
399 }
400 }
401 }
402
connectInput(int inputIndex,int uid,int (* action)(snd_seq_t *,snd_seq_port_subscribe_t *),const char * actionName)403 int SC_AlsaMidiClient::connectInput(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*),
404 const char* actionName) {
405 snd_seq_t* seq = mHandle;
406 snd_seq_client_info_t* cinfo;
407 snd_seq_port_subscribe_t* subs;
408 snd_seq_addr_t src, dst;
409 int cid, pid;
410
411 if ((inputIndex < 0) || (inputIndex >= mNumInPorts))
412 return errIndexOutOfRange;
413
414 snd_seq_client_info_alloca(&cinfo);
415 if (snd_seq_get_client_info(seq, cinfo) < 0) {
416 post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
417 return errFailed;
418 }
419
420 dst.client = snd_seq_client_info_get_client(cinfo);
421 dst.port = mInPorts[inputIndex];
422 SC_AlsaParseUID(uid, cid, pid);
423 src.client = cid;
424 src.port = pid;
425
426 // post("MIDI (ALSA): connect ndx %d uid %u dst %d:%d src %d:%d\n", inputIndex, uid, dst.client, dst.port,
427 // src.client, src.port);
428
429 snd_seq_port_subscribe_alloca(&subs);
430 snd_seq_port_subscribe_set_sender(subs, &src);
431 snd_seq_port_subscribe_set_dest(subs, &dst);
432
433 if ((*action)(seq, subs) < 0) {
434 post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
435 return errFailed;
436 }
437
438 return errNone;
439 }
440
connectOutput(int outputIndex,int uid,int (* action)(snd_seq_t *,snd_seq_port_subscribe_t *),const char * actionName)441 int SC_AlsaMidiClient::connectOutput(int outputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*),
442 const char* actionName) {
443 snd_seq_t* seq = mHandle;
444 snd_seq_client_info_t* cinfo;
445 snd_seq_port_subscribe_t* subs;
446 snd_seq_addr_t src, dst;
447 int cid, pid;
448
449 if ((outputIndex < 0) || (outputIndex >= mNumOutPorts))
450 return errIndexOutOfRange;
451
452 snd_seq_client_info_alloca(&cinfo);
453 if (snd_seq_get_client_info(seq, cinfo) < 0) {
454 post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
455 return errFailed;
456 }
457
458 src.client = snd_seq_client_info_get_client(cinfo);
459 src.port = mOutPorts[outputIndex];
460 SC_AlsaParseUID(uid, cid, pid);
461 dst.client = cid;
462 dst.port = pid;
463
464 // post("MIDI (ALSA): connect ndx %d uid %u dst %d:%d src %d:%d\n", outputIndex, uid, dst.client, dst.port,
465 // src.client, src.port);
466
467 snd_seq_port_subscribe_alloca(&subs);
468 snd_seq_port_subscribe_set_sender(subs, &src);
469 snd_seq_port_subscribe_set_dest(subs, &dst);
470
471 if ((*action)(seq, subs) < 0) {
472 post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
473 return errFailed;
474 }
475
476 return errNone;
477 }
478
sendEvent(int outputIndex,int uid,snd_seq_event_t * evt,float late)479 int SC_AlsaMidiClient::sendEvent(int outputIndex, int uid, snd_seq_event_t* evt, float late) {
480 snd_seq_real_time time;
481
482 if ((outputIndex < 0) || (outputIndex >= mNumOutPorts))
483 return errIndexOutOfRange;
484
485 snd_seq_ev_set_source(evt, mOutPorts[outputIndex]);
486 if (uid == 0) {
487 // send to all subscribed ports
488 snd_seq_ev_set_subs(evt);
489 } else {
490 // send to specific port
491 int cid, pid;
492 SC_AlsaParseUID(uid, cid, pid);
493 snd_seq_ev_set_dest(evt, cid, pid);
494 }
495
496 // long latelong;
497 if (late > 0.f) {
498 // latelong = (long) (late * 1000000000);
499 // new time calculation. The old one was not correct
500 // time.tv_sec = (long)(latelong / 1000000000); // seconds
501 // time.tv_nsec = (long)(latelong % 1000000000); // nanoseconds
502 time.tv_sec = (long)(floorf(late));
503 time.tv_nsec = (long)((late - time.tv_sec) * 1e9f);
504 } else {
505 time.tv_sec = time.tv_nsec = 0;
506 }
507
508 // evt->flags = evt->flags | SND_SEQ_TIME_STAMP_REAL;
509
510 // post("MIDI (ALSA): sending event, time %i, %i, late %f, latelong %i\n", time.tv_sec, time.tv_nsec, late,
511 // latelong);
512
513 snd_seq_ev_schedule_real(evt, mQueue, 1, &time);
514 snd_seq_event_output_direct(mHandle, evt);
515 // snd_seq_event_output(mHandle, evt);
516
517 // snd_seq_continue_queue(mHandle, mQueue, 0);
518 // snd_seq_drain_output(mHandle);
519
520 return errNone;
521 }
522
initMIDI(int numIn,int numOut)523 int initMIDI(int numIn, int numOut) {
524 SC_AlsaMidiClient* client = &gMIDIClient;
525 int i;
526
527 if (client->mHandle)
528 cleanUpMIDI();
529
530 if (numIn > kMaxMidiPorts) {
531 printf("MIDI: note that maximum midi in ports is limited to %d.\n", kMaxMidiPorts);
532 }
533 if (numOut > kMaxMidiPorts) {
534 printf("MIDI: note that maximum midi out ports is limited to %d.\n", kMaxMidiPorts);
535 }
536 numIn = sc_clip(numIn, 1, kMaxMidiPorts);
537 numOut = sc_clip(numOut, 1, kMaxMidiPorts);
538
539 // initialize client handle
540 if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
541 client->mHandle = nullptr;
542 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
543 return errFailed;
544 }
545
546 snd_seq_set_client_name(client->mHandle, "SuperCollider");
547
548 // allocate i/o ports
549 for (i = 0; i < numIn; i++) {
550 char str[32];
551 int port;
552
553 sprintf(str, "in%d", i);
554
555 port = snd_seq_create_simple_port(client->mHandle, str, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
556 SND_SEQ_PORT_TYPE_APPLICATION);
557
558 if (port < 0) {
559 post("MIDI (ALSA): could not create MIDI in port %d: %s\n", i, snd_strerror(errno));
560 break;
561 }
562
563 client->mInPorts[i] = port;
564 }
565
566 client->mNumInPorts = i;
567
568 for (i = 0; i < numOut; i++) {
569 char str[32];
570 int port;
571
572 sprintf(str, "out%d", i);
573
574 port = snd_seq_create_simple_port(client->mHandle, str, SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
575 SND_SEQ_PORT_TYPE_APPLICATION);
576
577 if (port < 0) {
578 post("MIDI (ALSA): could not create MIDI out port %d: %s\n", i, snd_strerror(errno));
579 break;
580 }
581
582 client->mOutPorts[i] = port;
583 }
584
585 client->mNumOutPorts = i;
586
587 // initialize queue
588 client->mQueue = snd_seq_alloc_queue(client->mHandle);
589 snd_seq_start_queue(client->mHandle, client->mQueue, nullptr);
590 snd_seq_drain_output(client->mHandle);
591 // snd_seq_set_client_pool_output(seqHandle, ??);
592
593 // initialize event en-/decoders
594 if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
595 client->mEventToMidi = nullptr;
596 post("MIDI (ALSA): could not create MIDI decoder\n");
597 return errFailed;
598 }
599
600 if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
601 client->mMidiToEvent = nullptr;
602 post("MIDI (ALSA): could not create MIDI encoder\n");
603 return errFailed;
604 }
605
606 snd_midi_event_no_status(client->mEventToMidi, 1);
607 snd_midi_event_no_status(client->mMidiToEvent, 1);
608
609 // start input thread
610 return client->startInputThread();
611 }
612
initMIDIClient()613 int initMIDIClient() {
614 SC_AlsaMidiClient* client = &gMIDIClient;
615
616 if (client->mHandle)
617 return errNone;
618
619 // initialize client handle
620 if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
621 client->mHandle = nullptr;
622 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
623 return errFailed;
624 }
625
626 snd_seq_set_client_name(client->mHandle, "SuperCollider");
627 client->mClientID = snd_seq_client_id(client->mHandle);
628
629
630 // initialize queue
631 client->mQueue = snd_seq_alloc_queue(client->mHandle);
632 snd_seq_start_queue(client->mHandle, client->mQueue, nullptr);
633 snd_seq_drain_output(client->mHandle);
634 // snd_seq_set_client_pool_output(seqHandle, ??);
635
636 // initialize event en-/decoders
637 if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
638 client->mEventToMidi = nullptr;
639 post("MIDI (ALSA): could not create MIDI decoder\n");
640 return errFailed;
641 }
642
643 if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
644 client->mMidiToEvent = nullptr;
645 post("MIDI (ALSA): could not create MIDI encoder\n");
646 return errFailed;
647 }
648
649 snd_midi_event_no_status(client->mEventToMidi, 1);
650 snd_midi_event_no_status(client->mMidiToEvent, 1);
651
652 return client->startInputThread();
653 }
654
disposeMIDI()655 int disposeMIDI() {
656 cleanUpMIDI();
657 return errNone;
658 }
659
restartMIDI()660 int restartMIDI() { return errNone; }
661
cleanUpMIDI()662 void cleanUpMIDI() {
663 SC_AlsaMidiClient* client = &gMIDIClient;
664
665 if (client->mHandle) {
666 client->joinInputThread();
667
668 snd_seq_remove_events_t* revt;
669 snd_seq_remove_events_malloc(&revt);
670 snd_seq_remove_events_set_queue(revt, client->mQueue);
671 snd_seq_remove_events_set_condition(revt, SND_SEQ_REMOVE_OUTPUT | SND_SEQ_REMOVE_IGNORE_OFF);
672 snd_seq_remove_events(client->mHandle, revt);
673 snd_seq_remove_events_free(revt);
674
675 snd_seq_stop_queue(client->mHandle, client->mQueue, nullptr);
676 snd_seq_free_queue(client->mHandle, client->mQueue);
677
678 if (client->mEventToMidi) {
679 snd_midi_event_free(client->mEventToMidi);
680 }
681
682 if (client->mMidiToEvent) {
683 snd_midi_event_free(client->mMidiToEvent);
684 }
685
686 snd_seq_close(client->mHandle);
687 client->mHandle = nullptr;
688 }
689 }
690
SC_AlsaCheckPerm(snd_seq_port_info_t * pinfo,int bits)691 inline static bool SC_AlsaCheckPerm(snd_seq_port_info_t* pinfo, int bits) {
692 int cap = snd_seq_port_info_get_capability(pinfo);
693 return ((cap & bits) == bits) && !(cap & SND_SEQ_PORT_CAP_NO_EXPORT);
694 }
695
listMIDIEndpoints(struct VMGlobals * g,PyrSlot * a)696 int listMIDIEndpoints(struct VMGlobals* g, PyrSlot* a) {
697 snd_seq_t* seq;
698 snd_seq_client_info_t* cinfo;
699 snd_seq_port_info_t* pinfo;
700
701 if (!gMIDIClient.mHandle)
702 return errFailed;
703
704 seq = gMIDIClient.mHandle;
705
706 snd_seq_client_info_alloca(&cinfo);
707 snd_seq_port_info_alloca(&pinfo);
708 snd_seq_client_info_set_client(cinfo, -1);
709
710 std::vector<SC_AlsaMidiPort> srcPorts;
711 std::vector<SC_AlsaMidiPort> dstPorts;
712
713 while (snd_seq_query_next_client(seq, cinfo) >= 0) {
714 int cid = snd_seq_client_info_get_client(cinfo);
715 const char* cname = snd_seq_client_info_get_name(cinfo);
716
717 if ((cid < 0) || (cid > 0xffff)) {
718 post("MIDI (ALSA): client ID out of range.\n");
719 return errFailed;
720 }
721
722 snd_seq_port_info_set_client(pinfo, cid);
723 snd_seq_port_info_set_port(pinfo, -1);
724
725 while (snd_seq_query_next_port(seq, pinfo) >= 0) {
726 int pid = snd_seq_port_info_get_port(pinfo);
727 const char* pname = snd_seq_port_info_get_name(pinfo);
728
729 if ((pid < 0) || (pid > 0xffff)) {
730 post("MIDI (ALSA): port ID out of range.\n");
731 return errFailed;
732 }
733
734 if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ)) {
735 // src port
736 srcPorts.push_back(SC_AlsaMidiPort());
737 snprintf(srcPorts.back().name, kAlsaMaxPortNameLen, "%s", pname);
738 snprintf(srcPorts.back().device, kAlsaMaxPortNameLen, "%s", cname);
739 srcPorts.back().uid = SC_AlsaMakeUID(cid, pid);
740 // post("MIDI (ALSA): src %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
741 }
742
743 if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) {
744 // dst port
745 dstPorts.push_back(SC_AlsaMidiPort());
746 snprintf(dstPorts.back().name, kAlsaMaxPortNameLen, "%s", pname);
747 snprintf(dstPorts.back().device, kAlsaMaxPortNameLen, "%s", cname);
748 dstPorts.back().uid = SC_AlsaMakeUID(cid, pid);
749 // post("MIDI (ALSA): dst %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
750 }
751 }
752 }
753
754 int numSrc = srcPorts.size();
755 int numDst = dstPorts.size();
756
757 PyrObject* idarray = newPyrArray(g->gc, 6 * sizeof(PyrObject), 0, true);
758 SetObject(a, idarray);
759
760 PyrObject* idarraySo = newPyrArray(g->gc, numSrc * sizeof(int32), 0, true);
761 SetObject(idarray->slots + idarray->size++, idarraySo);
762 g->gc->GCWrite(idarray, idarraySo);
763
764 PyrObject* devarraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0, true);
765 SetObject(idarray->slots + idarray->size++, devarraySo);
766 g->gc->GCWrite(idarray, devarraySo);
767
768 PyrObject* namearraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0, true);
769 SetObject(idarray->slots + idarray->size++, namearraySo);
770 g->gc->GCWrite(idarray, namearraySo);
771
772 PyrObject* idarrayDe = newPyrArray(g->gc, numDst * sizeof(int32), 0, true);
773 SetObject(idarray->slots + idarray->size++, idarrayDe);
774 g->gc->GCWrite(idarray, idarrayDe);
775
776 PyrObject* namearrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0, true);
777 SetObject(idarray->slots + idarray->size++, namearrayDe);
778 g->gc->GCWrite(idarray, namearrayDe);
779
780 PyrObject* devarrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0, true);
781 SetObject(idarray->slots + idarray->size++, devarrayDe);
782 g->gc->GCWrite(idarray, devarrayDe);
783
784
785 for (int i = 0; i < numSrc; ++i) {
786 char* name = srcPorts[i].name;
787 char* devicename = srcPorts[i].device;
788
789 PyrString* string = newPyrString(g->gc, name, 0, true);
790 SetObject(namearraySo->slots + i, string);
791 namearraySo->size++;
792 g->gc->GCWrite(namearraySo, (PyrObject*)string);
793
794 PyrString* devstring = newPyrString(g->gc, devicename, 0, true);
795 SetObject(devarraySo->slots + i, devstring);
796 devarraySo->size++;
797 g->gc->GCWrite(devarraySo, (PyrObject*)devstring);
798
799 SetInt(idarraySo->slots + i, srcPorts[i].uid);
800 idarraySo->size++;
801 }
802
803 for (int i = 0; i < numDst; ++i) {
804 char* name = dstPorts[i].name;
805 char* devicename = dstPorts[i].device;
806
807 PyrString* string = newPyrString(g->gc, name, 0, true);
808 SetObject(namearrayDe->slots + namearrayDe->size++, string);
809 g->gc->GCWrite(namearrayDe, (PyrObject*)string);
810
811 PyrString* devstring = newPyrString(g->gc, devicename, 0, true);
812 SetObject(devarrayDe->slots + i, devstring);
813 devarrayDe->size++;
814 g->gc->GCWrite(devarrayDe, (PyrObject*)devstring);
815
816 SetInt(idarrayDe->slots + i, dstPorts[i].uid);
817 idarrayDe->size++;
818 }
819
820 return errNone;
821 }
822
connectMIDIIn(int inputIndex,int uid)823 int connectMIDIIn(int inputIndex, int uid) {
824 if (!gMIDIClient.mHandle)
825 return errFailed;
826 return gMIDIClient.connectInput(inputIndex, uid, &snd_seq_subscribe_port, "connect");
827 }
828
disconnectMIDIIn(int inputIndex,int uid)829 int disconnectMIDIIn(int inputIndex, int uid) {
830 if (!gMIDIClient.mHandle)
831 return errFailed;
832 return gMIDIClient.connectInput(inputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
833 }
834
connectMIDIOut(int outputIndex,int uid)835 int connectMIDIOut(int outputIndex, int uid) {
836 if (!gMIDIClient.mHandle)
837 return errFailed;
838 return gMIDIClient.connectOutput(outputIndex, uid, &snd_seq_subscribe_port, "connect");
839 }
840
disconnectMIDIOut(int outputIndex,int uid)841 int disconnectMIDIOut(int outputIndex, int uid) {
842 if (!gMIDIClient.mHandle)
843 return errFailed;
844 return gMIDIClient.connectOutput(outputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
845 }
846
sendMIDI(int port,int uid,int length,int hiStatus,int loStatus,int aval,int bval,float late)847 int sendMIDI(int port, int uid, int length, int hiStatus, int loStatus, int aval, int bval, float late) {
848 if (!gMIDIClient.mHandle)
849 return errFailed;
850
851 // post("MIDI (ALSA): send %x %x %d %d %i\n", hiStatus>>4, loStatus, aval, bval, gMIDIClient.mMidiToEvent);
852
853 snd_seq_event_t evt;
854 SC_AlsaMidiPacket pkt;
855
856 snd_seq_ev_clear(&evt);
857 pkt.data[0] = (hiStatus & 0xF0) | (loStatus & 0x0F);
858 pkt.data[1] = (uint8)aval;
859 pkt.data[2] = (uint8)bval;
860
861 snd_midi_event_reset_encode(gMIDIClient.mMidiToEvent);
862
863 if (snd_midi_event_encode(gMIDIClient.mMidiToEvent, pkt.data, length, &evt) < 0) {
864 post("MIDI (ALSA): could not encode midi data: %s\n", snd_strerror(errno));
865 return errFailed;
866 }
867
868 return gMIDIClient.sendEvent(port, uid, &evt, late);
869 }
870
sendMIDISysex(int port,int uid,int length,uint8 * data)871 int sendMIDISysex(int port, int uid, int length, uint8* data) {
872 if (!gMIDIClient.mHandle)
873 return errFailed;
874 snd_seq_event_t evt;
875 evt.type = SND_SEQ_EVENT_SYSEX; // MIDIOut.sysex patch 2007-01-16
876 snd_seq_ev_set_variable(&evt, length, data);
877 return gMIDIClient.sendEvent(port, uid, &evt, 0.f);
878 }
879
880 // =====================================================================
881 // Primitives
882 // =====================================================================
883
884 int prInitMIDI(struct VMGlobals* g, int numArgsPushed);
prInitMIDI(struct VMGlobals * g,int numArgsPushed)885 int prInitMIDI(struct VMGlobals* g, int numArgsPushed) {
886 // PyrSlot *a = g->sp - 2;
887 PyrSlot* b = g->sp - 1;
888 PyrSlot* c = g->sp;
889
890 int err, numIn, numOut;
891 err = slotIntVal(b, &numIn);
892 if (err)
893 return errWrongType;
894
895 err = slotIntVal(c, &numOut);
896 if (err)
897 return errWrongType;
898
899 return initMIDI(numIn, numOut);
900 }
901
902 int prInitMIDIClient(struct VMGlobals* g, int numArgsPushed);
prInitMIDIClient(struct VMGlobals * g,int numArgsPushed)903 int prInitMIDIClient(struct VMGlobals* g, int numArgsPushed) { return initMIDIClient(); }
904
905 int prDisposeMIDIClient(VMGlobals* g, int numArgsPushed);
prDisposeMIDIClient(VMGlobals * g,int numArgsPushed)906 int prDisposeMIDIClient(VMGlobals* g, int numArgsPushed) { return disposeMIDI(); }
907
908 int prRestartMIDI(VMGlobals* g, int numArgsPushed);
prRestartMIDI(VMGlobals * g,int numArgsPushed)909 int prRestartMIDI(VMGlobals* g, int numArgsPushed) { return restartMIDI(); }
910
911 int prListMIDIEndpoints(struct VMGlobals* g, int numArgsPushed);
prListMIDIEndpoints(struct VMGlobals * g,int numArgsPushed)912 int prListMIDIEndpoints(struct VMGlobals* g, int numArgsPushed) { return listMIDIEndpoints(g, g->sp); }
913
914 int prConnectMIDIIn(struct VMGlobals* g, int numArgsPushed);
prConnectMIDIIn(struct VMGlobals * g,int numArgsPushed)915 int prConnectMIDIIn(struct VMGlobals* g, int numArgsPushed) {
916 // PyrSlot *a = g->sp - 2;
917 PyrSlot* b = g->sp - 1;
918 PyrSlot* c = g->sp;
919
920 int err, inputIndex, uid;
921 err = slotIntVal(b, &inputIndex);
922 if (err)
923 return err;
924
925 err = slotIntVal(c, &uid);
926 if (err)
927 return err;
928
929 return connectMIDIIn(inputIndex, uid);
930 }
931
932 int prDisconnectMIDIIn(struct VMGlobals* g, int numArgsPushed);
prDisconnectMIDIIn(struct VMGlobals * g,int numArgsPushed)933 int prDisconnectMIDIIn(struct VMGlobals* g, int numArgsPushed) {
934 PyrSlot* b = g->sp - 1;
935 PyrSlot* c = g->sp;
936
937 int err, inputIndex, uid;
938 err = slotIntVal(b, &inputIndex);
939 if (err)
940 return err;
941
942 err = slotIntVal(c, &uid);
943 if (err)
944 return err;
945
946 return disconnectMIDIIn(inputIndex, uid);
947 }
948
949 int prConnectMIDIOut(struct VMGlobals* g, int numArgsPushed);
prConnectMIDIOut(struct VMGlobals * g,int numArgsPushed)950 int prConnectMIDIOut(struct VMGlobals* g, int numArgsPushed) {
951 // PyrSlot *a = g->sp - 2;
952 PyrSlot* b = g->sp - 1;
953 PyrSlot* c = g->sp;
954
955 int err, inputIndex, uid;
956 err = slotIntVal(b, &inputIndex);
957 if (err)
958 return err;
959
960 err = slotIntVal(c, &uid);
961 if (err)
962 return err;
963
964 return connectMIDIOut(inputIndex, uid);
965 }
966
967 int prDisconnectMIDIOut(struct VMGlobals* g, int numArgsPushed);
prDisconnectMIDIOut(struct VMGlobals * g,int numArgsPushed)968 int prDisconnectMIDIOut(struct VMGlobals* g, int numArgsPushed) {
969 PyrSlot* b = g->sp - 1;
970 PyrSlot* c = g->sp;
971
972 int err, inputIndex, uid;
973 err = slotIntVal(b, &inputIndex);
974 if (err)
975 return err;
976
977 err = slotIntVal(c, &uid);
978 if (err)
979 return err;
980
981 return disconnectMIDIOut(inputIndex, uid);
982 }
983
984 int prSendMIDIOut(struct VMGlobals* g, int numArgsPushed);
prSendMIDIOut(struct VMGlobals * g,int numArgsPushed)985 int prSendMIDIOut(struct VMGlobals* g, int numArgsPushed) {
986 // port, uid, len, hiStatus, loStatus, a, b, latency
987 // PyrSlot *m = g->sp - 8;
988 PyrSlot* p = g->sp - 7;
989
990 PyrSlot* u = g->sp - 6;
991 PyrSlot* l = g->sp - 5;
992
993 PyrSlot* his = g->sp - 4;
994 PyrSlot* los = g->sp - 3;
995
996 PyrSlot* a = g->sp - 2;
997 PyrSlot* b = g->sp - 1;
998 PyrSlot* plat = g->sp;
999
1000 int err, outputIndex, uid, length, hiStatus, loStatus, aval, bval;
1001 float late;
1002 err = slotIntVal(p, &outputIndex);
1003 if (err)
1004 return err;
1005
1006 err = slotIntVal(u, &uid);
1007 if (err)
1008 return err;
1009
1010 err = slotIntVal(l, &length);
1011 if (err)
1012 return err;
1013
1014 err = slotIntVal(his, &hiStatus);
1015 if (err)
1016 return err;
1017
1018 err = slotIntVal(los, &loStatus);
1019 if (err)
1020 return err;
1021
1022 err = slotIntVal(a, &aval);
1023 if (err)
1024 return err;
1025
1026 err = slotIntVal(b, &bval);
1027 if (err)
1028 return err;
1029
1030 err = slotFloatVal(plat, &late);
1031 if (err)
1032 return err;
1033
1034 return sendMIDI(outputIndex, uid, length, hiStatus, loStatus, aval, bval, late);
1035 }
1036
prSendSysex(VMGlobals * g,int numArgsPushed)1037 int prSendSysex(VMGlobals* g, int numArgsPushed) {
1038 int err, uid, outputIndex;
1039 PyrInt8Array* packet;
1040
1041 // rcvr, uid, packet
1042 PyrSlot* args = g->sp - 2;
1043
1044 int MIDIOut_port_index = instVarOffset("MIDIOut", "port");
1045
1046 err = slotIntVal(slotRawObject(args)->slots + MIDIOut_port_index, &outputIndex);
1047 if (err)
1048 return err;
1049
1050 err = slotIntVal(args + 1, &uid);
1051 if (err)
1052 return err;
1053
1054 if (!isKindOfSlot(args + 2, s_int8array->u.classobj))
1055 return errWrongType;
1056
1057 packet = slotRawInt8Array(&args[2]);
1058
1059 return sendMIDISysex(outputIndex, uid, packet->size, packet->b);
1060 }
1061
prGetMIDIClientID(VMGlobals * g,int numArgsPushed)1062 int prGetMIDIClientID(VMGlobals* g, int numArgsPushed) {
1063 PyrSlot* args = g->sp;
1064 if (!gMIDIClient.mHandle)
1065 return errFailed;
1066
1067
1068 SetInt(args, gMIDIClient.mClientID);
1069
1070 return errNone;
1071 }
1072
initMIDIPrimitives()1073 void initMIDIPrimitives() {
1074 int base, index;
1075
1076 base = nextPrimitiveIndex();
1077 index = 0;
1078
1079 s_midiin = getsym("MIDIIn");
1080
1081 s_domidiaction = getsym("doAction");
1082 s_midiNoteOnAction = getsym("doNoteOnAction");
1083 s_midiNoteOffAction = getsym("doNoteOffAction");
1084 s_midiTouchAction = getsym("doTouchAction");
1085 s_midiControlAction = getsym("doControlAction");
1086 s_midiPolyTouchAction = getsym("doPolyTouchAction");
1087 s_midiProgramAction = getsym("doProgramAction");
1088 s_midiBendAction = getsym("doBendAction");
1089 s_midiSysexAction = getsym("doSysexAction");
1090 s_midiSysrtAction = getsym("doSysrtAction");
1091 s_midiSMPTEAction = getsym("doSMPTEaction");
1092
1093 definePrimitive(base, index++, "_InitMIDI", prInitMIDI, 3, 0);
1094 definePrimitive(base, index++, "_InitMIDIClient", prInitMIDIClient, 1, 0);
1095 definePrimitive(base, index++, "_RestartMIDI", prRestartMIDI, 1, 0);
1096 definePrimitive(base, index++, "_DisposeMIDIClient", prDisposeMIDIClient, 1, 0);
1097
1098 definePrimitive(base, index++, "_ListMIDIEndpoints", prListMIDIEndpoints, 1, 0);
1099 definePrimitive(base, index++, "_ConnectMIDIIn", prConnectMIDIIn, 3, 0);
1100 definePrimitive(base, index++, "_DisconnectMIDIIn", prDisconnectMIDIIn, 3, 0);
1101 definePrimitive(base, index++, "_ConnectMIDIOut", prConnectMIDIOut, 3, 0);
1102 definePrimitive(base, index++, "_DisconnectMIDIOut", prDisconnectMIDIOut, 3, 0);
1103
1104 definePrimitive(base, index++, "_SendMIDIOut", prSendMIDIOut, 9, 0);
1105 definePrimitive(base, index++, "_SendSysex", prSendSysex, 3, 0); // MIDIOut.sysex patch 2007-01-16
1106
1107 definePrimitive(base, index++, "_GetMIDIClientID", prGetMIDIClientID, 1, 0);
1108
1109 cleanUpMIDI();
1110 }
1111
deinitMIDIPrimitives()1112 void deinitMIDIPrimitives() { cleanUpMIDI(); }
1113 // EOF
1114