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