class library: SynthDef - lazy implementation of removeUGen
[supercollider.git] / lang / LangPrimSource / SC_AlsaMIDI.cpp
blobae11dbce1ae0a72bda0301449a236f303095ad3c
1 /*
2 Alsa MIDI/Sequencer support.
3 Copyright (c) 2004 stefan kersten.
5 ====================================================================
7 SuperCollider real time audio synthesis system
8 Copyright (c) 2002 James McCartney. All rights reserved.
9 http://www.audiosynth.com
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.
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.
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
26 #include "SCBase.h"
27 #include "VMGlobals.h"
28 #include "PyrSymbolTable.h"
29 #include "PyrInterpreter.h"
30 #include "PyrKernel.h"
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"
41 PyrSymbol* s_midiin;
42 PyrSymbol* s_domidiaction;
43 PyrSymbol* s_midiNoteOnAction;
44 PyrSymbol* s_midiNoteOffAction;
45 PyrSymbol* s_midiTouchAction;
46 PyrSymbol* s_midiControlAction;
47 PyrSymbol* s_midiPolyTouchAction;
48 PyrSymbol* s_midiProgramAction;
49 PyrSymbol* s_midiBendAction;
50 PyrSymbol* s_midiSysexAction;
51 PyrSymbol* s_midiSysrtAction;
52 PyrSymbol* s_midiSMPTEAction;
54 static int g_ivx_MIDIOut_port;
56 const int kMaxMidiPorts = 16;
57 bool gMIDIInitialized = false;
59 extern bool compiledOK;
61 // =====================================================================
62 // Platform declarations (interface routines)
63 // =====================================================================
65 static int initClient();
66 static int initMIDI(int numIn, int numOut);
67 static int disposeMIDI();
68 static int restartMIDI();
69 static void cleanUpMIDI();
71 static int listMIDIEndpoints(struct VMGlobals *g, PyrSlot *a);
72 static int connectMIDIIn(int inputIndex, int uid);
73 static int disconnectMIDIIn(int inputIndex, int uid);
74 static int connectMIDIOut(int outputIndex, int uid);
75 static int disconnectMIDIOut(int outputIndex, int uid);
77 static int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late);
78 static int sendMIDISysex(int port, int destId, int length, uint8* data);
80 // =====================================================================
81 // Platform declarations (ALSA)
82 // =====================================================================
84 #include <alsa/asoundlib.h>
85 #include <pthread.h>
86 #include <vector>
87 #include <string.h>
89 static const size_t kAlsaMaxPacketSize = 3;
90 static const size_t kAlsaMaxPortNameLen = 256;
92 // MIDI packet
93 struct SC_AlsaMidiPacket
95 uint8 data[kAlsaMaxPacketSize];
98 // MIDI client state
99 struct SC_AlsaMidiClient
101 snd_seq_t* mHandle;
102 int mQueue;
103 int mNumInPorts;
104 int mInPorts[kMaxMidiPorts];
105 int mNumOutPorts;
106 int mOutPorts[kMaxMidiPorts];
107 snd_midi_event_t* mEventToMidi;
108 snd_midi_event_t* mMidiToEvent;
109 pthread_t mInputThread;
110 bool mShouldBeRunning;
112 static void* inputThreadFunc(void*);
113 void processEvent(snd_seq_event_t* evt);
115 int connectInput(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName);
116 int connectOutput(int outputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName);
117 int sendEvent(int outputIndex, int uid, snd_seq_event_t* evt, float late=0.f);
120 static SC_AlsaMidiClient gMIDIClient;
122 // Port description
123 struct SC_AlsaMidiPort
125 SC_AlsaMidiPort()
126 : uid(0)
127 { *name = 0; }
129 char name[kAlsaMaxPortNameLen];
130 int32 uid;
133 // =====================================================================
134 // Platform implementation (ALSA)
135 // =====================================================================
137 static inline int SC_AlsaMakeUID(int clientID, int portID)
139 return (clientID << 16) | (portID & 0xFFFF);
142 static inline void SC_AlsaParseUID(int uid, int& clientID, int& portID)
144 clientID = uid >> 16;
145 portID = uid & 0xFFFF;
148 void SC_AlsaMidiClient::processEvent(snd_seq_event_t* evt)
150 pthread_mutex_lock (&gLangMutex);
151 if (compiledOK) {
152 VMGlobals* g = gMainVMGlobals;
153 PyrInt8Array* sysexArray;
155 SC_AlsaMidiPacket pkt;
157 g->canCallOS = false; // cannot call the OS
159 // class MIDIIn
160 ++g->sp; SetObject(g->sp, s_midiin->u.classobj);
161 // source
162 ++g->sp; SetInt(g->sp, SC_AlsaMakeUID(evt->source.client, evt->source.port));
164 switch (evt->type) {
165 // midi events
166 case SND_SEQ_EVENT_NOTEOFF: // noteOff
167 ++g->sp; SetInt(g->sp, evt->data.note.channel);
168 ++g->sp; SetInt(g->sp, evt->data.note.note);
169 ++g->sp; SetInt(g->sp, evt->data.note.velocity);
170 runInterpreter(g, s_midiNoteOffAction, 5);
171 break;
172 case SND_SEQ_EVENT_NOTEON: // noteOn
173 ++g->sp; SetInt(g->sp, evt->data.note.channel);
174 ++g->sp; SetInt(g->sp, evt->data.note.note);
175 ++g->sp; SetInt(g->sp, evt->data.note.velocity);
176 runInterpreter(g, evt->data.note.velocity ? s_midiNoteOnAction : s_midiNoteOffAction, 5);
177 break;
178 case SND_SEQ_EVENT_KEYPRESS: // polytouch
179 ++g->sp; SetInt(g->sp, evt->data.note.channel);
180 ++g->sp; SetInt(g->sp, evt->data.note.note);
181 ++g->sp; SetInt(g->sp, evt->data.note.velocity);
182 runInterpreter(g, s_midiPolyTouchAction, 5);
183 break;
184 case SND_SEQ_EVENT_CONTROLLER: // control
185 ++g->sp; SetInt(g->sp, evt->data.control.channel);
186 ++g->sp; SetInt(g->sp, evt->data.control.param);
187 ++g->sp; SetInt(g->sp, evt->data.control.value);
188 runInterpreter(g, s_midiControlAction, 5);
189 break;
190 case SND_SEQ_EVENT_PGMCHANGE: // program
191 ++g->sp; SetInt(g->sp, evt->data.control.channel);
192 ++g->sp; SetInt(g->sp, evt->data.control.value);
193 runInterpreter(g, s_midiProgramAction, 4);
194 break;
195 case SND_SEQ_EVENT_CHANPRESS: // touch
196 ++g->sp; SetInt(g->sp, evt->data.control.channel);
197 ++g->sp; SetInt(g->sp, evt->data.control.value);
198 runInterpreter(g, s_midiTouchAction, 4);
199 break;
200 case SND_SEQ_EVENT_PITCHBEND: // bend
201 ++g->sp; SetInt(g->sp, evt->data.control.channel);
202 ++g->sp; SetInt(g->sp, evt->data.control.value + 8192);
203 runInterpreter(g, s_midiBendAction, 4);
204 break;
205 // system common events
206 case SND_SEQ_EVENT_QFRAME: // mtc quarter frame
208 int index = evt->data.control.value >> 4;
209 int data = evt->data.control.value & 0xf;
211 #if 0
212 post(
213 "mtc qframe: byte 0x%x index 0x%x data 0x%x\n",
214 evt->data.control.value,
215 index, data
217 #endif
219 switch (index) {
220 case 1: case 3:
221 case 5: case 7:
222 data = data << 4;
225 ++g->sp; SetInt(g->sp, index);
226 ++g->sp; SetInt(g->sp, data);
228 runInterpreter(g, s_midiSMPTEAction, 4);
229 break;
230 case SND_SEQ_EVENT_SONGPOS: // song ptr
231 ++g->sp; SetInt(g->sp, evt->data.control.channel);
232 ++g->sp; SetInt(g->sp, (evt->data.control.value << 7) | evt->data.control.param);
233 runInterpreter(g, s_midiSysrtAction, 4);
234 break;
235 case SND_SEQ_EVENT_SONGSEL: // song sel
236 ++g->sp; SetInt(g->sp, evt->data.control.channel);
237 ++g->sp; SetInt(g->sp, evt->data.control.param);
238 runInterpreter(g, s_midiSysrtAction, 4);
239 break;
240 // system realtime events
241 case SND_SEQ_EVENT_CLOCK: // clock
242 ++g->sp; SetInt(g->sp, 0x8);
243 ++g->sp; SetInt(g->sp, 0);
244 runInterpreter(g, s_midiSysrtAction, 4);
245 break;
246 case SND_SEQ_EVENT_START: // start
247 ++g->sp; SetInt(g->sp, 0xA);
248 ++g->sp; SetInt(g->sp, 0);
249 runInterpreter(g, s_midiSysrtAction, 4);
250 break;
251 case SND_SEQ_EVENT_CONTINUE: // continue
252 ++g->sp; SetInt(g->sp, 0xB);
253 ++g->sp; SetInt(g->sp, 0);
254 runInterpreter(g, s_midiSysrtAction, 4);
255 break;
256 case SND_SEQ_EVENT_STOP: // stop
257 ++g->sp; SetInt(g->sp, 0xC);
258 ++g->sp; SetInt(g->sp, 0);
259 runInterpreter(g, s_midiSysrtAction, 4);
260 break;
261 case SND_SEQ_EVENT_SENSING: // active sensing
262 ++g->sp; SetInt(g->sp, 0xE);
263 ++g->sp; SetInt(g->sp, 0);
264 runInterpreter(g, s_midiSysrtAction, 4);
265 break;
266 case SND_SEQ_EVENT_RESET: // system reset
267 ++g->sp; SetInt(g->sp, 0xF);
268 ++g->sp; SetInt(g->sp, 0);
269 runInterpreter(g, s_midiSysrtAction, 4);
270 break;
271 // sysex events
272 case SND_SEQ_EVENT_SYSEX: // sysex
273 sysexArray = newPyrInt8Array(g->gc, evt->data.ext.len, 0, true);
274 memcpy(sysexArray->b, evt->data.ext.ptr, evt->data.ext.len);
275 sysexArray->size = evt->data.ext.len;
276 ++g->sp; SetObject(g->sp, (PyrObject*)sysexArray);
277 runInterpreter(g, s_midiSysexAction, 3);
278 break;
279 default:
280 // unknown: convert to midi packet
281 snd_midi_event_reset_decode(mEventToMidi);
282 memset(pkt.data, 0, kAlsaMaxPacketSize);
283 if (snd_midi_event_decode(mEventToMidi, pkt.data, kAlsaMaxPacketSize, evt) > 0) {
284 for (size_t i=0; i < kAlsaMaxPacketSize; i++) {
285 ++g->sp; SetInt(g->sp, pkt.data[i]);
287 runInterpreter(g, s_domidiaction, 2+kAlsaMaxPacketSize);
288 } else {
289 g->sp -= 2;
292 g->canCallOS = false;
294 pthread_mutex_unlock (&gLangMutex);
297 void* SC_AlsaMidiClient::inputThreadFunc(void* arg)
299 SC_AlsaMidiClient* client = (SC_AlsaMidiClient*)arg;
300 snd_seq_t* handle = client->mHandle;
301 int npfd = snd_seq_poll_descriptors_count(handle, POLLIN);
302 struct pollfd pfd[npfd];
304 snd_seq_poll_descriptors(handle, pfd, npfd, POLLIN);
306 while (client->mShouldBeRunning) {
307 if (poll(pfd, npfd, 2000) > 0) { // 2s timeout
308 for (int i=0; i < npfd; i++) {
309 if (pfd[i].revents > 0) {
310 do {
311 snd_seq_event_t* evt;
312 snd_seq_event_input(handle, &evt);
313 client->processEvent(evt);
314 snd_seq_free_event(evt);
315 } while (snd_seq_event_input_pending(handle, 0) > 0);
321 return 0;
324 int SC_AlsaMidiClient::connectInput(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName)
326 snd_seq_t* seq = mHandle;
327 snd_seq_client_info_t* cinfo;
328 snd_seq_port_subscribe_t* subs;
329 snd_seq_addr_t src, dst;
330 int cid, pid;
332 if ((inputIndex < 0) || (inputIndex >= mNumInPorts)) return errIndexOutOfRange;
334 snd_seq_client_info_alloca(&cinfo);
335 if (snd_seq_get_client_info(seq, cinfo) < 0) {
336 post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
337 return errFailed;
340 dst.client = snd_seq_client_info_get_client(cinfo);
341 dst.port = mInPorts[inputIndex];
342 SC_AlsaParseUID(uid, cid, pid);
343 src.client = cid;
344 src.port = pid;
346 //post("MIDI (ALSA): connect ndx %d uid %u dst %d:%d src %d:%d\n", inputIndex, uid, dst.client, dst.port, src.client, src.port);
348 snd_seq_port_subscribe_alloca(&subs);
349 snd_seq_port_subscribe_set_sender(subs, &src);
350 snd_seq_port_subscribe_set_dest(subs, &dst);
352 if ((*action)(seq, subs) < 0) {
353 post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
354 return errFailed;
357 return errNone;
360 int SC_AlsaMidiClient::connectOutput(int outputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName)
362 snd_seq_t* seq = mHandle;
363 snd_seq_client_info_t* cinfo;
364 snd_seq_port_subscribe_t* subs;
365 snd_seq_addr_t src, dst;
366 int cid, pid;
368 if ((outputIndex < 0) || (outputIndex >= mNumOutPorts)) return errIndexOutOfRange;
370 snd_seq_client_info_alloca(&cinfo);
371 if (snd_seq_get_client_info(seq, cinfo) < 0) {
372 post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
373 return errFailed;
376 src.client = snd_seq_client_info_get_client(cinfo);
377 src.port = mOutPorts[outputIndex];
378 SC_AlsaParseUID(uid, cid, pid);
379 dst.client = cid;
380 dst.port = pid;
382 // post("MIDI (ALSA): connect ndx %d uid %u dst %d:%d src %d:%d\n", outputIndex, uid, dst.client, dst.port, src.client, src.port);
384 snd_seq_port_subscribe_alloca(&subs);
385 snd_seq_port_subscribe_set_sender(subs, &src);
386 snd_seq_port_subscribe_set_dest(subs, &dst);
388 if ((*action)(seq, subs) < 0) {
389 post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
390 return errFailed;
393 return errNone;
396 int SC_AlsaMidiClient::sendEvent(int outputIndex, int uid, snd_seq_event_t* evt, float late)
398 snd_seq_real_time time;
400 if ((outputIndex < 0) || (outputIndex >= mNumOutPorts)) return errIndexOutOfRange;
402 snd_seq_ev_set_source(evt, mOutPorts[outputIndex]);
403 if (uid == 0) {
404 // send to all subscribed ports
405 snd_seq_ev_set_subs(evt);
406 } else {
407 // send to specific port
408 int cid, pid;
409 SC_AlsaParseUID(uid, cid, pid);
410 snd_seq_ev_set_dest(evt, cid, pid);
413 // long latelong;
414 if (late > 0.f) {
415 // latelong = (long) (late * 1000000000);
416 // new time calculation. The old one was not correct
417 // time.tv_sec = (long)(latelong / 1000000000); // seconds
418 // time.tv_nsec = (long)(latelong % 1000000000); // nanoseconds
419 time.tv_sec = (long)(floorf (late));
420 time.tv_nsec = (long)((late - time.tv_sec) * 1e9f);
421 } else {
422 time.tv_sec = time.tv_nsec = 0;
425 // evt->flags = evt->flags | SND_SEQ_TIME_STAMP_REAL;
427 // post("MIDI (ALSA): sending event, time %i, %i, late %f, latelong %i\n", time.tv_sec, time.tv_nsec, late, latelong);
429 snd_seq_ev_schedule_real(evt, mQueue, 1, &time);
430 snd_seq_event_output_direct(mHandle, evt);
431 // snd_seq_event_output(mHandle, evt);
433 // snd_seq_continue_queue(mHandle, mQueue, 0);
434 // snd_seq_drain_output(mHandle);
436 return errNone;
439 int initMIDI(int numIn, int numOut)
441 SC_AlsaMidiClient* client = &gMIDIClient;
442 int i;
444 if (client->mHandle) cleanUpMIDI();
446 numIn = sc_clip(numIn, 1, kMaxMidiPorts);
447 numOut = sc_clip(numOut, 1, kMaxMidiPorts);
449 // initialize client handle
450 if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
451 client->mHandle = 0;
452 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
453 return errFailed;
456 snd_seq_set_client_name(client->mHandle, "SuperCollider");
458 // allocate i/o ports
459 for (i=0; i < numIn; i++) {
460 char str[32];
461 int port;
463 sprintf(str, "in%d", i);
465 port = snd_seq_create_simple_port(
466 client->mHandle, str,
467 SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
468 SND_SEQ_PORT_TYPE_APPLICATION);
470 if (port < 0) {
471 post("MIDI (ALSA): could not create MIDI in port %d: %s\n", i, snd_strerror(errno));
472 break;
475 client->mInPorts[i] = port;
478 client->mNumInPorts = i;
480 for (i=0; i < numOut; i++) {
481 char str[32];
482 int port;
484 sprintf(str, "out%d", i);
486 port = snd_seq_create_simple_port(
487 client->mHandle, str,
488 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
489 SND_SEQ_PORT_TYPE_APPLICATION);
491 if (port < 0) {
492 post("MIDI (ALSA): could not create MIDI out port %d: %s\n", i, snd_strerror(errno));
493 break;
496 client->mOutPorts[i] = port;
499 client->mNumOutPorts = i;
501 // initialize queue
502 client->mQueue = snd_seq_alloc_queue(client->mHandle);
503 snd_seq_start_queue(client->mHandle, client->mQueue, 0);
504 snd_seq_drain_output(client->mHandle);
505 // snd_seq_set_client_pool_output(seqHandle, ??);
507 // initialize event en-/decoders
508 if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
509 client->mEventToMidi = 0;
510 post("MIDI (ALSA): could not create MIDI decoder\n");
511 return errFailed;
514 if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
515 client->mMidiToEvent = 0;
516 post("MIDI (ALSA): could not create MIDI encoder\n");
517 return errFailed;
520 snd_midi_event_no_status(client->mEventToMidi, 1);
521 snd_midi_event_no_status(client->mMidiToEvent, 1);
523 // start input thread
524 client->mShouldBeRunning = true;
525 if (pthread_create(&client->mInputThread, 0, &SC_AlsaMidiClient::inputThreadFunc, client) != 0) {
526 post("MIDI (ALSA): could not start input thread\n");
527 return errFailed;
530 return errNone;
533 int initMIDIClient()
535 SC_AlsaMidiClient* client = &gMIDIClient;
537 if (client->mHandle) return errNone;
539 // initialize client handle
540 if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
541 client->mHandle = 0;
542 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
543 return errFailed;
546 snd_seq_set_client_name(client->mHandle, "SuperCollider");
548 // initialize queue
549 client->mQueue = snd_seq_alloc_queue(client->mHandle);
550 snd_seq_start_queue(client->mHandle, client->mQueue, 0);
551 snd_seq_drain_output(client->mHandle);
552 // snd_seq_set_client_pool_output(seqHandle, ??);
554 // initialize event en-/decoders
555 if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
556 client->mEventToMidi = 0;
557 post("MIDI (ALSA): could not create MIDI decoder\n");
558 return errFailed;
561 if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
562 client->mMidiToEvent = 0;
563 post("MIDI (ALSA): could not create MIDI encoder\n");
564 return errFailed;
567 snd_midi_event_no_status(client->mEventToMidi, 1);
568 snd_midi_event_no_status(client->mMidiToEvent, 1);
570 // start input thread
571 client->mShouldBeRunning = true;
572 if (pthread_create(&client->mInputThread, 0, &SC_AlsaMidiClient::inputThreadFunc, client) != 0) {
573 post("MIDI (ALSA): could not start input thread\n");
574 return errFailed;
577 return errNone;
580 int disposeMIDI()
582 cleanUpMIDI();
583 return errNone;
586 int restartMIDI()
588 return errNone;
591 void cleanUpMIDI()
593 SC_AlsaMidiClient* client = &gMIDIClient;
595 if (client->mHandle) {
596 client->mShouldBeRunning = false;
597 pthread_join(client->mInputThread, 0);
599 snd_seq_remove_events_t *revt;
600 snd_seq_remove_events_malloc(&revt);
601 snd_seq_remove_events_set_queue(revt, client->mQueue);
602 snd_seq_remove_events_set_condition(revt, SND_SEQ_REMOVE_OUTPUT|SND_SEQ_REMOVE_IGNORE_OFF);
603 snd_seq_remove_events(client->mHandle, revt);
604 snd_seq_remove_events_free(revt);
606 snd_seq_stop_queue(client->mHandle, client->mQueue, 0);
607 snd_seq_free_queue(client->mHandle, client->mQueue);
609 if (client->mEventToMidi) {
610 snd_midi_event_free(client->mEventToMidi);
613 if (client->mMidiToEvent) {
614 snd_midi_event_free(client->mMidiToEvent);
617 snd_seq_close(client->mHandle);
618 client->mHandle = 0;
622 inline static bool SC_AlsaCheckPerm(snd_seq_port_info_t* pinfo, int bits)
624 int cap = snd_seq_port_info_get_capability(pinfo);
625 return ((cap & bits) == bits) && !(cap & SND_SEQ_PORT_CAP_NO_EXPORT);
628 int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* a)
630 snd_seq_t* seq;
631 snd_seq_client_info_t* cinfo;
632 snd_seq_port_info_t* pinfo;
634 if (!gMIDIClient.mHandle) return errFailed;
636 seq = gMIDIClient.mHandle;
638 snd_seq_client_info_alloca(&cinfo);
639 snd_seq_port_info_alloca(&pinfo);
640 snd_seq_client_info_set_client(cinfo, -1);
642 std::vector<SC_AlsaMidiPort> srcPorts;
643 std::vector<SC_AlsaMidiPort> dstPorts;
645 while (snd_seq_query_next_client(seq, cinfo) >= 0) {
646 int cid = snd_seq_client_info_get_client(cinfo);
647 const char* cname = snd_seq_client_info_get_name(cinfo);
649 if ((cid < 0) || (cid > 0xffff)) {
650 post("MIDI (ALSA): client ID out of range.\n");
651 return errFailed;
654 snd_seq_port_info_set_client(pinfo, cid);
655 snd_seq_port_info_set_port(pinfo, -1);
657 while (snd_seq_query_next_port(seq, pinfo) >= 0) {
658 int pid = snd_seq_port_info_get_port(pinfo);
659 const char* pname = snd_seq_port_info_get_name(pinfo);
661 if ((pid < 0) || (pid > 0xffff)) {
662 post("MIDI (ALSA): port ID out of range.\n");
663 return errFailed;
666 if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) {
667 // src port
668 srcPorts.push_back(SC_AlsaMidiPort());
669 snprintf(srcPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
670 srcPorts.back().uid = SC_AlsaMakeUID(cid, pid);
671 //post("MIDI (ALSA): src %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
674 if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) {
675 // dst port
676 dstPorts.push_back(SC_AlsaMidiPort());
677 snprintf(dstPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
678 dstPorts.back().uid = SC_AlsaMakeUID(cid, pid);
679 //post("MIDI (ALSA): dst %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
684 int numSrc = srcPorts.size();
685 int numDst = dstPorts.size();
687 PyrObject* idarray = newPyrArray(g->gc, 6 * sizeof(PyrObject), 0 , true);
688 SetObject(a, idarray);
690 PyrObject* idarraySo = newPyrArray(g->gc, numSrc * sizeof(int32), 0 , true);
691 SetObject(idarray->slots+idarray->size++, idarraySo);
692 g->gc->GCWrite(idarray, idarraySo);
694 PyrObject* devarraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
695 SetObject(idarray->slots+idarray->size++, devarraySo);
696 g->gc->GCWrite(idarray, devarraySo);
698 PyrObject* namearraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
699 SetObject(idarray->slots+idarray->size++, namearraySo);
700 g->gc->GCWrite(idarray, namearraySo);
702 PyrObject* idarrayDe = newPyrArray(g->gc, numDst * sizeof(int32), 0 , true);
703 SetObject(idarray->slots+idarray->size++, idarrayDe);
704 g->gc->GCWrite(idarray, idarrayDe);
706 PyrObject* namearrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
707 SetObject(idarray->slots+idarray->size++, namearrayDe);
708 g->gc->GCWrite(idarray, namearrayDe);
710 PyrObject* devarrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
711 SetObject(idarray->slots+idarray->size++, devarrayDe);
712 g->gc->GCWrite(idarray, devarrayDe);
715 for (int i=0; i<numSrc; ++i) {
716 char* name = srcPorts[i].name;
718 PyrString *string = newPyrString(g->gc, name, 0, true);
719 SetObject(namearraySo->slots+i, string);
720 namearraySo->size++;
721 g->gc->GCWrite(namearraySo, (PyrObject*)string);
723 PyrString *devstring = newPyrString(g->gc, name, 0, true);
724 SetObject(devarraySo->slots+i, devstring);
725 devarraySo->size++;
726 g->gc->GCWrite(devarraySo, (PyrObject*)devstring);
728 SetInt(idarraySo->slots+i, srcPorts[i].uid);
729 idarraySo->size++;
732 for (int i=0; i<numDst; ++i) {
733 char* name = dstPorts[i].name;
735 PyrString *string = newPyrString(g->gc, name, 0, true);
736 SetObject(namearrayDe->slots+namearrayDe->size++, string);
737 g->gc->GCWrite(namearrayDe, (PyrObject*)string);
739 PyrString *devstring = newPyrString(g->gc, name, 0, true);
740 SetObject(devarrayDe->slots+i, devstring);
741 devarrayDe->size++;
742 g->gc->GCWrite(devarrayDe, (PyrObject*)devstring);
744 SetInt(idarrayDe->slots+i, dstPorts[i].uid);
745 idarrayDe->size++;
748 return errNone;
751 int connectMIDIIn(int inputIndex, int uid)
753 if (!gMIDIClient.mHandle) return errFailed;
754 return gMIDIClient.connectInput(inputIndex, uid, &snd_seq_subscribe_port, "connect");
757 int disconnectMIDIIn(int inputIndex, int uid)
759 if (!gMIDIClient.mHandle) return errFailed;
760 return gMIDIClient.connectInput(inputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
763 int connectMIDIOut(int outputIndex, int uid)
765 if (!gMIDIClient.mHandle) return errFailed;
766 return gMIDIClient.connectOutput(outputIndex, uid, &snd_seq_subscribe_port, "connect");
769 int disconnectMIDIOut(int outputIndex, int uid)
771 if (!gMIDIClient.mHandle) return errFailed;
772 return gMIDIClient.connectOutput(outputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
775 int sendMIDI(int port, int uid, int length, int hiStatus, int loStatus, int aval, int bval, float late)
777 if (!gMIDIClient.mHandle) return errFailed;
779 // post("MIDI (ALSA): send %x %x %d %d %i\n", hiStatus>>4, loStatus, aval, bval, gMIDIClient.mMidiToEvent);
781 snd_seq_event_t evt;
782 SC_AlsaMidiPacket pkt;
784 snd_seq_ev_clear(&evt);
785 pkt.data[0] = (hiStatus & 0xF0) | (loStatus & 0x0F);
786 pkt.data[1] = (uint8)aval;
787 pkt.data[2] = (uint8)bval;
789 snd_midi_event_reset_encode(gMIDIClient.mMidiToEvent);
791 if (snd_midi_event_encode(gMIDIClient.mMidiToEvent, pkt.data, length, &evt) < 0) {
792 post("MIDI (ALSA): could not encode midi data: %s\n", snd_strerror(errno));
793 return errFailed;
796 return gMIDIClient.sendEvent(port, uid, &evt, late);
799 int sendMIDISysex(int port, int uid, int length, uint8* data)
801 if (!gMIDIClient.mHandle) return errFailed;
802 snd_seq_event_t evt;
803 evt.type = SND_SEQ_EVENT_SYSEX; // MIDIOut.sysex patch 2007-01-16
804 snd_seq_ev_set_variable(&evt, length, data);
805 return gMIDIClient.sendEvent(port, uid, &evt, 0.f);
808 // =====================================================================
809 // Primitives
810 // =====================================================================
812 int prInitMIDI(struct VMGlobals *g, int numArgsPushed);
813 int prInitMIDI(struct VMGlobals *g, int numArgsPushed)
815 //PyrSlot *a = g->sp - 2;
816 PyrSlot *b = g->sp - 1;
817 PyrSlot *c = g->sp;
819 int err, numIn, numOut;
820 err = slotIntVal(b, &numIn);
821 if (err) return errWrongType;
823 err = slotIntVal(c, &numOut);
824 if (err) return errWrongType;
826 return initMIDI(numIn, numOut);
829 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed);
830 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed)
832 return initMIDIClient();
835 int prDisposeMIDIClient(VMGlobals *g, int numArgsPushed);
836 int prDisposeMIDIClient(VMGlobals *g, int numArgsPushed)
838 return disposeMIDI();
841 int prRestartMIDI(VMGlobals *g, int numArgsPushed);
842 int prRestartMIDI(VMGlobals *g, int numArgsPushed)
844 return restartMIDI();
847 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed);
848 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed)
850 return listMIDIEndpoints(g, g->sp);
853 int prConnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
854 int prConnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
856 //PyrSlot *a = g->sp - 2;
857 PyrSlot *b = g->sp - 1;
858 PyrSlot *c = g->sp;
860 int err, inputIndex, uid;
861 err = slotIntVal(b, &inputIndex);
862 if (err) return err;
864 err = slotIntVal(c, &uid);
865 if (err) return err;
867 return connectMIDIIn(inputIndex, uid);
870 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
871 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
873 PyrSlot *b = g->sp - 1;
874 PyrSlot *c = g->sp;
876 int err, inputIndex, uid;
877 err = slotIntVal(b, &inputIndex);
878 if (err) return err;
880 err = slotIntVal(c, &uid);
881 if (err) return err;
883 return disconnectMIDIIn(inputIndex, uid);
886 int prConnectMIDIOut(struct VMGlobals *g, int numArgsPushed);
887 int prConnectMIDIOut(struct VMGlobals *g, int numArgsPushed)
889 //PyrSlot *a = g->sp - 2;
890 PyrSlot *b = g->sp - 1;
891 PyrSlot *c = g->sp;
893 int err, inputIndex, uid;
894 err = slotIntVal(b, &inputIndex);
895 if (err) return err;
897 err = slotIntVal(c, &uid);
898 if (err) return err;
900 return connectMIDIOut(inputIndex, uid);
903 int prDisconnectMIDIOut(struct VMGlobals *g, int numArgsPushed);
904 int prDisconnectMIDIOut(struct VMGlobals *g, int numArgsPushed)
906 PyrSlot *b = g->sp - 1;
907 PyrSlot *c = g->sp;
909 int err, inputIndex, uid;
910 err = slotIntVal(b, &inputIndex);
911 if (err) return err;
913 err = slotIntVal(c, &uid);
914 if (err) return err;
916 return disconnectMIDIOut(inputIndex, uid);
919 int prSendMIDIOut(struct VMGlobals *g, int numArgsPushed);
920 int prSendMIDIOut(struct VMGlobals *g, int numArgsPushed)
922 //port, uid, len, hiStatus, loStatus, a, b, latency
923 //PyrSlot *m = g->sp - 8;
924 PyrSlot *p = g->sp - 7;
926 PyrSlot *u = g->sp - 6;
927 PyrSlot *l = g->sp - 5;
929 PyrSlot *his = g->sp - 4;
930 PyrSlot *los = g->sp - 3;
932 PyrSlot *a = g->sp - 2;
933 PyrSlot *b = g->sp - 1;
934 PyrSlot *plat = g->sp;
936 int err, outputIndex, uid, length, hiStatus, loStatus, aval, bval;
937 float late;
938 err = slotIntVal(p, &outputIndex);
939 if (err) return err;
941 err = slotIntVal(u, &uid);
942 if (err) return err;
944 err = slotIntVal(l, &length);
945 if (err) return err;
947 err = slotIntVal(his, &hiStatus);
948 if (err) return err;
950 err = slotIntVal(los, &loStatus);
951 if (err) return err;
953 err = slotIntVal(a, &aval);
954 if (err) return err;
956 err = slotIntVal(b, &bval);
957 if (err) return err;
959 err = slotFloatVal(plat, &late);
960 if (err) return err;
962 return sendMIDI(outputIndex, uid, length, hiStatus, loStatus, aval, bval, late);
965 int prSendSysex(VMGlobals *g, int numArgsPushed);
966 int prSendSysex(VMGlobals *g, int numArgsPushed)
968 int err, uid, outputIndex;
969 PyrInt8Array* packet;
971 // rcvr, uid, packet
972 PyrSlot* args = g->sp - 2;
974 err = slotIntVal(slotRawObject(args)->slots + g_ivx_MIDIOut_port, &outputIndex);
975 if (err) return err;
977 err = slotIntVal(args+1, &uid);
978 if (err) return err;
980 if( !isKindOfSlot(args+2, s_int8array->u.classobj) )
981 return errWrongType;
983 packet = slotRawInt8Array(&args[2]);
985 return sendMIDISysex(outputIndex, uid, packet->size, packet->b);
988 void initMIDIPrimitives()
990 int base, index;
992 base = nextPrimitiveIndex();
993 index = 0;
995 s_midiin = getsym("MIDIIn");
997 s_domidiaction = getsym("doAction");
998 s_midiNoteOnAction = getsym("doNoteOnAction");
999 s_midiNoteOffAction = getsym("doNoteOffAction");
1000 s_midiTouchAction = getsym("doTouchAction");
1001 s_midiControlAction = getsym("doControlAction");
1002 s_midiPolyTouchAction = getsym("doPolyTouchAction");
1003 s_midiProgramAction = getsym("doProgramAction");
1004 s_midiBendAction = getsym("doBendAction");
1005 s_midiSysexAction = getsym("doSysexAction");
1006 s_midiSysrtAction = getsym("doSysrtAction");
1007 s_midiSMPTEAction = getsym("doSMPTEaction");
1009 g_ivx_MIDIOut_port = instVarOffset("MIDIOut", "port");
1011 definePrimitive(base, index++, "_InitMIDI", prInitMIDI, 3, 0);
1012 definePrimitive(base, index++, "_InitMIDIClient", prInitMIDIClient, 1, 0);
1013 definePrimitive(base, index++, "_RestartMIDI", prRestartMIDI, 1, 0);
1014 definePrimitive(base, index++, "_DisposeMIDIClient", prDisposeMIDIClient, 1, 0);
1016 definePrimitive(base, index++, "_ListMIDIEndpoints", prListMIDIEndpoints, 1, 0);
1017 definePrimitive(base, index++, "_ConnectMIDIIn", prConnectMIDIIn, 3, 0);
1018 definePrimitive(base, index++, "_DisconnectMIDIIn", prDisconnectMIDIIn, 3, 0);
1019 definePrimitive(base, index++, "_ConnectMIDIOut", prConnectMIDIOut, 3, 0);
1020 definePrimitive(base, index++, "_DisconnectMIDIOut", prDisconnectMIDIOut, 3, 0);
1022 definePrimitive(base, index++, "_SendMIDIOut", prSendMIDIOut, 9, 0);
1023 definePrimitive(base, index++, "_SendSysex", prSendSysex, 3, 0); // MIDIOut.sysex patch 2007-01-16
1025 cleanUpMIDI();
1028 // EOF