bumping version to 3.5-rc1
[supercollider.git] / lang / LangPrimSource / SC_AlsaMIDI.cpp
blob6fcedd982f44644c0e360d09fe895663737e4cb5
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"
40 #include "SC_LanguageClient.h"
42 PyrSymbol* s_midiin;
43 PyrSymbol* s_domidiaction;
44 PyrSymbol* s_midiNoteOnAction;
45 PyrSymbol* s_midiNoteOffAction;
46 PyrSymbol* s_midiTouchAction;
47 PyrSymbol* s_midiControlAction;
48 PyrSymbol* s_midiPolyTouchAction;
49 PyrSymbol* s_midiProgramAction;
50 PyrSymbol* s_midiBendAction;
51 PyrSymbol* s_midiSysexAction;
52 PyrSymbol* s_midiSysrtAction;
53 PyrSymbol* s_midiSMPTEAction;
55 static int g_ivx_MIDIOut_port;
57 const int kMaxMidiPorts = 16;
58 bool gMIDIInitialized = false;
60 extern bool compiledOK;
62 // =====================================================================
63 // Platform declarations (interface routines)
64 // =====================================================================
66 static int initClient();
67 static int initMIDI(int numIn, int numOut);
68 static int disposeMIDI();
69 static int restartMIDI();
70 static void cleanUpMIDI();
72 static int listMIDIEndpoints(struct VMGlobals *g, PyrSlot *a);
73 static int connectMIDIIn(int inputIndex, int uid);
74 static int disconnectMIDIIn(int inputIndex, int uid);
75 static int connectMIDIOut(int outputIndex, int uid);
76 static int disconnectMIDIOut(int outputIndex, int uid);
78 static int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late);
79 static int sendMIDISysex(int port, int destId, int length, uint8* data);
81 // =====================================================================
82 // Platform declarations (ALSA)
83 // =====================================================================
85 #include <alsa/asoundlib.h>
86 #include <pthread.h>
87 #include <vector>
88 #include <string.h>
90 static const size_t kAlsaMaxPacketSize = 3;
91 static const size_t kAlsaMaxPortNameLen = 256;
93 // MIDI packet
94 struct SC_AlsaMidiPacket
96 uint8 data[kAlsaMaxPacketSize];
99 // MIDI client state
100 struct SC_AlsaMidiClient
102 snd_seq_t* mHandle;
103 int mQueue;
104 int mNumInPorts;
105 int mInPorts[kMaxMidiPorts];
106 int mNumOutPorts;
107 int mOutPorts[kMaxMidiPorts];
108 snd_midi_event_t* mEventToMidi;
109 snd_midi_event_t* mMidiToEvent;
110 pthread_t mInputThread;
111 bool mShouldBeRunning;
113 static void* inputThreadFunc(void*);
114 void processEvent(snd_seq_event_t* evt);
116 int connectInput(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName);
117 int connectOutput(int outputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName);
118 int sendEvent(int outputIndex, int uid, snd_seq_event_t* evt, float late=0.f);
121 static SC_AlsaMidiClient gMIDIClient;
123 // Port description
124 struct SC_AlsaMidiPort
126 SC_AlsaMidiPort()
127 : uid(0)
128 { *name = 0; }
130 char name[kAlsaMaxPortNameLen];
131 int32 uid;
134 // =====================================================================
135 // Platform implementation (ALSA)
136 // =====================================================================
138 static inline int SC_AlsaMakeUID(int clientID, int portID)
140 return (clientID << 16) | (portID & 0xFFFF);
143 static inline void SC_AlsaParseUID(int uid, int& clientID, int& portID)
145 clientID = uid >> 16;
146 portID = uid & 0xFFFF;
149 void SC_AlsaMidiClient::processEvent(snd_seq_event_t* evt)
151 int status = lockLanguageOrQuit(mShouldBeRunning);
152 if (status == EINTR)
153 return;
154 if (status) {
155 postfl("error when locking language (%d)\n", status);
156 return;
159 if (compiledOK) {
160 VMGlobals* g = gMainVMGlobals;
161 PyrInt8Array* sysexArray;
163 SC_AlsaMidiPacket pkt;
165 g->canCallOS = false; // cannot call the OS
167 // class MIDIIn
168 ++g->sp; SetObject(g->sp, s_midiin->u.classobj);
169 // source
170 ++g->sp; SetInt(g->sp, SC_AlsaMakeUID(evt->source.client, evt->source.port));
172 switch (evt->type) {
173 // midi events
174 case SND_SEQ_EVENT_NOTEOFF: // noteOff
175 ++g->sp; SetInt(g->sp, evt->data.note.channel);
176 ++g->sp; SetInt(g->sp, evt->data.note.note);
177 ++g->sp; SetInt(g->sp, evt->data.note.velocity);
178 runInterpreter(g, s_midiNoteOffAction, 5);
179 break;
180 case SND_SEQ_EVENT_NOTEON: // noteOn
181 ++g->sp; SetInt(g->sp, evt->data.note.channel);
182 ++g->sp; SetInt(g->sp, evt->data.note.note);
183 ++g->sp; SetInt(g->sp, evt->data.note.velocity);
184 runInterpreter(g, evt->data.note.velocity ? s_midiNoteOnAction : s_midiNoteOffAction, 5);
185 break;
186 case SND_SEQ_EVENT_KEYPRESS: // polytouch
187 ++g->sp; SetInt(g->sp, evt->data.note.channel);
188 ++g->sp; SetInt(g->sp, evt->data.note.note);
189 ++g->sp; SetInt(g->sp, evt->data.note.velocity);
190 runInterpreter(g, s_midiPolyTouchAction, 5);
191 break;
192 case SND_SEQ_EVENT_CONTROLLER: // control
193 ++g->sp; SetInt(g->sp, evt->data.control.channel);
194 ++g->sp; SetInt(g->sp, evt->data.control.param);
195 ++g->sp; SetInt(g->sp, evt->data.control.value);
196 runInterpreter(g, s_midiControlAction, 5);
197 break;
198 case SND_SEQ_EVENT_PGMCHANGE: // program
199 ++g->sp; SetInt(g->sp, evt->data.control.channel);
200 ++g->sp; SetInt(g->sp, evt->data.control.value);
201 runInterpreter(g, s_midiProgramAction, 4);
202 break;
203 case SND_SEQ_EVENT_CHANPRESS: // touch
204 ++g->sp; SetInt(g->sp, evt->data.control.channel);
205 ++g->sp; SetInt(g->sp, evt->data.control.value);
206 runInterpreter(g, s_midiTouchAction, 4);
207 break;
208 case SND_SEQ_EVENT_PITCHBEND: // bend
209 ++g->sp; SetInt(g->sp, evt->data.control.channel);
210 ++g->sp; SetInt(g->sp, evt->data.control.value + 8192);
211 runInterpreter(g, s_midiBendAction, 4);
212 break;
213 // system common events
214 case SND_SEQ_EVENT_QFRAME: // mtc quarter frame
216 int index = evt->data.control.value >> 4;
217 int data = evt->data.control.value & 0xf;
219 #if 0
220 post(
221 "mtc qframe: byte 0x%x index 0x%x data 0x%x\n",
222 evt->data.control.value,
223 index, data
225 #endif
227 switch (index) {
228 case 1: case 3:
229 case 5: case 7:
230 data = data << 4;
233 ++g->sp; SetInt(g->sp, index);
234 ++g->sp; SetInt(g->sp, data);
236 runInterpreter(g, s_midiSMPTEAction, 4);
237 break;
238 case SND_SEQ_EVENT_SONGPOS: // song ptr
239 ++g->sp; SetInt(g->sp, evt->data.control.channel);
240 ++g->sp; SetInt(g->sp, (evt->data.control.value << 7) | evt->data.control.param);
241 runInterpreter(g, s_midiSysrtAction, 4);
242 break;
243 case SND_SEQ_EVENT_SONGSEL: // song sel
244 ++g->sp; SetInt(g->sp, evt->data.control.channel);
245 ++g->sp; SetInt(g->sp, evt->data.control.param);
246 runInterpreter(g, s_midiSysrtAction, 4);
247 break;
248 // system realtime events
249 case SND_SEQ_EVENT_CLOCK: // clock
250 ++g->sp; SetInt(g->sp, 0x8);
251 ++g->sp; SetInt(g->sp, 0);
252 runInterpreter(g, s_midiSysrtAction, 4);
253 break;
254 case SND_SEQ_EVENT_START: // start
255 ++g->sp; SetInt(g->sp, 0xA);
256 ++g->sp; SetInt(g->sp, 0);
257 runInterpreter(g, s_midiSysrtAction, 4);
258 break;
259 case SND_SEQ_EVENT_CONTINUE: // continue
260 ++g->sp; SetInt(g->sp, 0xB);
261 ++g->sp; SetInt(g->sp, 0);
262 runInterpreter(g, s_midiSysrtAction, 4);
263 break;
264 case SND_SEQ_EVENT_STOP: // stop
265 ++g->sp; SetInt(g->sp, 0xC);
266 ++g->sp; SetInt(g->sp, 0);
267 runInterpreter(g, s_midiSysrtAction, 4);
268 break;
269 case SND_SEQ_EVENT_SENSING: // active sensing
270 ++g->sp; SetInt(g->sp, 0xE);
271 ++g->sp; SetInt(g->sp, 0);
272 runInterpreter(g, s_midiSysrtAction, 4);
273 break;
274 case SND_SEQ_EVENT_RESET: // system reset
275 ++g->sp; SetInt(g->sp, 0xF);
276 ++g->sp; SetInt(g->sp, 0);
277 runInterpreter(g, s_midiSysrtAction, 4);
278 break;
279 // sysex events
280 case SND_SEQ_EVENT_SYSEX: // sysex
281 sysexArray = newPyrInt8Array(g->gc, evt->data.ext.len, 0, true);
282 memcpy(sysexArray->b, evt->data.ext.ptr, evt->data.ext.len);
283 sysexArray->size = evt->data.ext.len;
284 ++g->sp; SetObject(g->sp, (PyrObject*)sysexArray);
285 runInterpreter(g, s_midiSysexAction, 3);
286 break;
287 default:
288 // unknown: convert to midi packet
289 snd_midi_event_reset_decode(mEventToMidi);
290 memset(pkt.data, 0, kAlsaMaxPacketSize);
291 if (snd_midi_event_decode(mEventToMidi, pkt.data, kAlsaMaxPacketSize, evt) > 0) {
292 for (size_t i=0; i < kAlsaMaxPacketSize; i++) {
293 ++g->sp; SetInt(g->sp, pkt.data[i]);
295 runInterpreter(g, s_domidiaction, 2+kAlsaMaxPacketSize);
296 } else {
297 g->sp -= 2;
300 g->canCallOS = false;
302 pthread_mutex_unlock (&gLangMutex);
305 void* SC_AlsaMidiClient::inputThreadFunc(void* arg)
307 SC_AlsaMidiClient* client = (SC_AlsaMidiClient*)arg;
308 snd_seq_t* handle = client->mHandle;
309 int npfd = snd_seq_poll_descriptors_count(handle, POLLIN);
310 struct pollfd pfd[npfd];
312 snd_seq_poll_descriptors(handle, pfd, npfd, POLLIN);
314 while (client->mShouldBeRunning) {
315 if (poll(pfd, npfd, 2000) > 0) { // 2s timeout
316 for (int i=0; i < npfd; i++) {
317 if (pfd[i].revents > 0) {
318 do {
319 snd_seq_event_t* evt;
320 snd_seq_event_input(handle, &evt);
321 client->processEvent(evt);
322 snd_seq_free_event(evt);
323 } while (snd_seq_event_input_pending(handle, 0) > 0);
329 return 0;
332 int SC_AlsaMidiClient::connectInput(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName)
334 snd_seq_t* seq = mHandle;
335 snd_seq_client_info_t* cinfo;
336 snd_seq_port_subscribe_t* subs;
337 snd_seq_addr_t src, dst;
338 int cid, pid;
340 if ((inputIndex < 0) || (inputIndex >= mNumInPorts)) return errIndexOutOfRange;
342 snd_seq_client_info_alloca(&cinfo);
343 if (snd_seq_get_client_info(seq, cinfo) < 0) {
344 post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
345 return errFailed;
348 dst.client = snd_seq_client_info_get_client(cinfo);
349 dst.port = mInPorts[inputIndex];
350 SC_AlsaParseUID(uid, cid, pid);
351 src.client = cid;
352 src.port = pid;
354 //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);
356 snd_seq_port_subscribe_alloca(&subs);
357 snd_seq_port_subscribe_set_sender(subs, &src);
358 snd_seq_port_subscribe_set_dest(subs, &dst);
360 if ((*action)(seq, subs) < 0) {
361 post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
362 return errFailed;
365 return errNone;
368 int SC_AlsaMidiClient::connectOutput(int outputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName)
370 snd_seq_t* seq = mHandle;
371 snd_seq_client_info_t* cinfo;
372 snd_seq_port_subscribe_t* subs;
373 snd_seq_addr_t src, dst;
374 int cid, pid;
376 if ((outputIndex < 0) || (outputIndex >= mNumOutPorts)) return errIndexOutOfRange;
378 snd_seq_client_info_alloca(&cinfo);
379 if (snd_seq_get_client_info(seq, cinfo) < 0) {
380 post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
381 return errFailed;
384 src.client = snd_seq_client_info_get_client(cinfo);
385 src.port = mOutPorts[outputIndex];
386 SC_AlsaParseUID(uid, cid, pid);
387 dst.client = cid;
388 dst.port = pid;
390 // 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);
392 snd_seq_port_subscribe_alloca(&subs);
393 snd_seq_port_subscribe_set_sender(subs, &src);
394 snd_seq_port_subscribe_set_dest(subs, &dst);
396 if ((*action)(seq, subs) < 0) {
397 post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
398 return errFailed;
401 return errNone;
404 int SC_AlsaMidiClient::sendEvent(int outputIndex, int uid, snd_seq_event_t* evt, float late)
406 snd_seq_real_time time;
408 if ((outputIndex < 0) || (outputIndex >= mNumOutPorts)) return errIndexOutOfRange;
410 snd_seq_ev_set_source(evt, mOutPorts[outputIndex]);
411 if (uid == 0) {
412 // send to all subscribed ports
413 snd_seq_ev_set_subs(evt);
414 } else {
415 // send to specific port
416 int cid, pid;
417 SC_AlsaParseUID(uid, cid, pid);
418 snd_seq_ev_set_dest(evt, cid, pid);
421 // long latelong;
422 if (late > 0.f) {
423 // latelong = (long) (late * 1000000000);
424 // new time calculation. The old one was not correct
425 // time.tv_sec = (long)(latelong / 1000000000); // seconds
426 // time.tv_nsec = (long)(latelong % 1000000000); // nanoseconds
427 time.tv_sec = (long)(floorf (late));
428 time.tv_nsec = (long)((late - time.tv_sec) * 1e9f);
429 } else {
430 time.tv_sec = time.tv_nsec = 0;
433 // evt->flags = evt->flags | SND_SEQ_TIME_STAMP_REAL;
435 // post("MIDI (ALSA): sending event, time %i, %i, late %f, latelong %i\n", time.tv_sec, time.tv_nsec, late, latelong);
437 snd_seq_ev_schedule_real(evt, mQueue, 1, &time);
438 snd_seq_event_output_direct(mHandle, evt);
439 // snd_seq_event_output(mHandle, evt);
441 // snd_seq_continue_queue(mHandle, mQueue, 0);
442 // snd_seq_drain_output(mHandle);
444 return errNone;
447 int initMIDI(int numIn, int numOut)
449 SC_AlsaMidiClient* client = &gMIDIClient;
450 int i;
452 if (client->mHandle) cleanUpMIDI();
454 numIn = sc_clip(numIn, 1, kMaxMidiPorts);
455 numOut = sc_clip(numOut, 1, kMaxMidiPorts);
457 // initialize client handle
458 if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
459 client->mHandle = 0;
460 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
461 return errFailed;
464 snd_seq_set_client_name(client->mHandle, "SuperCollider");
466 // allocate i/o ports
467 for (i=0; i < numIn; i++) {
468 char str[32];
469 int port;
471 sprintf(str, "in%d", i);
473 port = snd_seq_create_simple_port(
474 client->mHandle, str,
475 SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
476 SND_SEQ_PORT_TYPE_APPLICATION);
478 if (port < 0) {
479 post("MIDI (ALSA): could not create MIDI in port %d: %s\n", i, snd_strerror(errno));
480 break;
483 client->mInPorts[i] = port;
486 client->mNumInPorts = i;
488 for (i=0; i < numOut; i++) {
489 char str[32];
490 int port;
492 sprintf(str, "out%d", i);
494 port = snd_seq_create_simple_port(
495 client->mHandle, str,
496 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
497 SND_SEQ_PORT_TYPE_APPLICATION);
499 if (port < 0) {
500 post("MIDI (ALSA): could not create MIDI out port %d: %s\n", i, snd_strerror(errno));
501 break;
504 client->mOutPorts[i] = port;
507 client->mNumOutPorts = i;
509 // initialize queue
510 client->mQueue = snd_seq_alloc_queue(client->mHandle);
511 snd_seq_start_queue(client->mHandle, client->mQueue, 0);
512 snd_seq_drain_output(client->mHandle);
513 // snd_seq_set_client_pool_output(seqHandle, ??);
515 // initialize event en-/decoders
516 if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
517 client->mEventToMidi = 0;
518 post("MIDI (ALSA): could not create MIDI decoder\n");
519 return errFailed;
522 if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
523 client->mMidiToEvent = 0;
524 post("MIDI (ALSA): could not create MIDI encoder\n");
525 return errFailed;
528 snd_midi_event_no_status(client->mEventToMidi, 1);
529 snd_midi_event_no_status(client->mMidiToEvent, 1);
531 // start input thread
532 client->mShouldBeRunning = true;
533 if (pthread_create(&client->mInputThread, 0, &SC_AlsaMidiClient::inputThreadFunc, client) != 0) {
534 post("MIDI (ALSA): could not start input thread\n");
535 return errFailed;
538 return errNone;
541 int initMIDIClient()
543 SC_AlsaMidiClient* client = &gMIDIClient;
545 if (client->mHandle) return errNone;
547 // initialize client handle
548 if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
549 client->mHandle = 0;
550 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
551 return errFailed;
554 snd_seq_set_client_name(client->mHandle, "SuperCollider");
556 // initialize queue
557 client->mQueue = snd_seq_alloc_queue(client->mHandle);
558 snd_seq_start_queue(client->mHandle, client->mQueue, 0);
559 snd_seq_drain_output(client->mHandle);
560 // snd_seq_set_client_pool_output(seqHandle, ??);
562 // initialize event en-/decoders
563 if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
564 client->mEventToMidi = 0;
565 post("MIDI (ALSA): could not create MIDI decoder\n");
566 return errFailed;
569 if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
570 client->mMidiToEvent = 0;
571 post("MIDI (ALSA): could not create MIDI encoder\n");
572 return errFailed;
575 snd_midi_event_no_status(client->mEventToMidi, 1);
576 snd_midi_event_no_status(client->mMidiToEvent, 1);
578 // start input thread
579 client->mShouldBeRunning = true;
580 if (pthread_create(&client->mInputThread, 0, &SC_AlsaMidiClient::inputThreadFunc, client) != 0) {
581 post("MIDI (ALSA): could not start input thread\n");
582 return errFailed;
585 return errNone;
588 int disposeMIDI()
590 cleanUpMIDI();
591 return errNone;
594 int restartMIDI()
596 return errNone;
599 void cleanUpMIDI()
601 SC_AlsaMidiClient* client = &gMIDIClient;
603 if (client->mHandle) {
604 client->mShouldBeRunning = false;
605 pthread_join(client->mInputThread, 0);
607 snd_seq_remove_events_t *revt;
608 snd_seq_remove_events_malloc(&revt);
609 snd_seq_remove_events_set_queue(revt, client->mQueue);
610 snd_seq_remove_events_set_condition(revt, SND_SEQ_REMOVE_OUTPUT|SND_SEQ_REMOVE_IGNORE_OFF);
611 snd_seq_remove_events(client->mHandle, revt);
612 snd_seq_remove_events_free(revt);
614 snd_seq_stop_queue(client->mHandle, client->mQueue, 0);
615 snd_seq_free_queue(client->mHandle, client->mQueue);
617 if (client->mEventToMidi) {
618 snd_midi_event_free(client->mEventToMidi);
621 if (client->mMidiToEvent) {
622 snd_midi_event_free(client->mMidiToEvent);
625 snd_seq_close(client->mHandle);
626 client->mHandle = 0;
630 inline static bool SC_AlsaCheckPerm(snd_seq_port_info_t* pinfo, int bits)
632 int cap = snd_seq_port_info_get_capability(pinfo);
633 return ((cap & bits) == bits) && !(cap & SND_SEQ_PORT_CAP_NO_EXPORT);
636 int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* a)
638 snd_seq_t* seq;
639 snd_seq_client_info_t* cinfo;
640 snd_seq_port_info_t* pinfo;
642 if (!gMIDIClient.mHandle) return errFailed;
644 seq = gMIDIClient.mHandle;
646 snd_seq_client_info_alloca(&cinfo);
647 snd_seq_port_info_alloca(&pinfo);
648 snd_seq_client_info_set_client(cinfo, -1);
650 std::vector<SC_AlsaMidiPort> srcPorts;
651 std::vector<SC_AlsaMidiPort> dstPorts;
653 while (snd_seq_query_next_client(seq, cinfo) >= 0) {
654 int cid = snd_seq_client_info_get_client(cinfo);
655 const char* cname = snd_seq_client_info_get_name(cinfo);
657 if ((cid < 0) || (cid > 0xffff)) {
658 post("MIDI (ALSA): client ID out of range.\n");
659 return errFailed;
662 snd_seq_port_info_set_client(pinfo, cid);
663 snd_seq_port_info_set_port(pinfo, -1);
665 while (snd_seq_query_next_port(seq, pinfo) >= 0) {
666 int pid = snd_seq_port_info_get_port(pinfo);
667 const char* pname = snd_seq_port_info_get_name(pinfo);
669 if ((pid < 0) || (pid > 0xffff)) {
670 post("MIDI (ALSA): port ID out of range.\n");
671 return errFailed;
674 if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) {
675 // src port
676 srcPorts.push_back(SC_AlsaMidiPort());
677 snprintf(srcPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
678 srcPorts.back().uid = SC_AlsaMakeUID(cid, pid);
679 //post("MIDI (ALSA): src %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
682 if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) {
683 // dst port
684 dstPorts.push_back(SC_AlsaMidiPort());
685 snprintf(dstPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
686 dstPorts.back().uid = SC_AlsaMakeUID(cid, pid);
687 //post("MIDI (ALSA): dst %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
692 int numSrc = srcPorts.size();
693 int numDst = dstPorts.size();
695 PyrObject* idarray = newPyrArray(g->gc, 6 * sizeof(PyrObject), 0 , true);
696 SetObject(a, idarray);
698 PyrObject* idarraySo = newPyrArray(g->gc, numSrc * sizeof(int32), 0 , true);
699 SetObject(idarray->slots+idarray->size++, idarraySo);
700 g->gc->GCWrite(idarray, idarraySo);
702 PyrObject* devarraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
703 SetObject(idarray->slots+idarray->size++, devarraySo);
704 g->gc->GCWrite(idarray, devarraySo);
706 PyrObject* namearraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
707 SetObject(idarray->slots+idarray->size++, namearraySo);
708 g->gc->GCWrite(idarray, namearraySo);
710 PyrObject* idarrayDe = newPyrArray(g->gc, numDst * sizeof(int32), 0 , true);
711 SetObject(idarray->slots+idarray->size++, idarrayDe);
712 g->gc->GCWrite(idarray, idarrayDe);
714 PyrObject* namearrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
715 SetObject(idarray->slots+idarray->size++, namearrayDe);
716 g->gc->GCWrite(idarray, namearrayDe);
718 PyrObject* devarrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
719 SetObject(idarray->slots+idarray->size++, devarrayDe);
720 g->gc->GCWrite(idarray, devarrayDe);
723 for (int i=0; i<numSrc; ++i) {
724 char* name = srcPorts[i].name;
726 PyrString *string = newPyrString(g->gc, name, 0, true);
727 SetObject(namearraySo->slots+i, string);
728 namearraySo->size++;
729 g->gc->GCWrite(namearraySo, (PyrObject*)string);
731 PyrString *devstring = newPyrString(g->gc, name, 0, true);
732 SetObject(devarraySo->slots+i, devstring);
733 devarraySo->size++;
734 g->gc->GCWrite(devarraySo, (PyrObject*)devstring);
736 SetInt(idarraySo->slots+i, srcPorts[i].uid);
737 idarraySo->size++;
740 for (int i=0; i<numDst; ++i) {
741 char* name = dstPorts[i].name;
743 PyrString *string = newPyrString(g->gc, name, 0, true);
744 SetObject(namearrayDe->slots+namearrayDe->size++, string);
745 g->gc->GCWrite(namearrayDe, (PyrObject*)string);
747 PyrString *devstring = newPyrString(g->gc, name, 0, true);
748 SetObject(devarrayDe->slots+i, devstring);
749 devarrayDe->size++;
750 g->gc->GCWrite(devarrayDe, (PyrObject*)devstring);
752 SetInt(idarrayDe->slots+i, dstPorts[i].uid);
753 idarrayDe->size++;
756 return errNone;
759 int connectMIDIIn(int inputIndex, int uid)
761 if (!gMIDIClient.mHandle) return errFailed;
762 return gMIDIClient.connectInput(inputIndex, uid, &snd_seq_subscribe_port, "connect");
765 int disconnectMIDIIn(int inputIndex, int uid)
767 if (!gMIDIClient.mHandle) return errFailed;
768 return gMIDIClient.connectInput(inputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
771 int connectMIDIOut(int outputIndex, int uid)
773 if (!gMIDIClient.mHandle) return errFailed;
774 return gMIDIClient.connectOutput(outputIndex, uid, &snd_seq_subscribe_port, "connect");
777 int disconnectMIDIOut(int outputIndex, int uid)
779 if (!gMIDIClient.mHandle) return errFailed;
780 return gMIDIClient.connectOutput(outputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
783 int sendMIDI(int port, int uid, int length, int hiStatus, int loStatus, int aval, int bval, float late)
785 if (!gMIDIClient.mHandle) return errFailed;
787 // post("MIDI (ALSA): send %x %x %d %d %i\n", hiStatus>>4, loStatus, aval, bval, gMIDIClient.mMidiToEvent);
789 snd_seq_event_t evt;
790 SC_AlsaMidiPacket pkt;
792 snd_seq_ev_clear(&evt);
793 pkt.data[0] = (hiStatus & 0xF0) | (loStatus & 0x0F);
794 pkt.data[1] = (uint8)aval;
795 pkt.data[2] = (uint8)bval;
797 snd_midi_event_reset_encode(gMIDIClient.mMidiToEvent);
799 if (snd_midi_event_encode(gMIDIClient.mMidiToEvent, pkt.data, length, &evt) < 0) {
800 post("MIDI (ALSA): could not encode midi data: %s\n", snd_strerror(errno));
801 return errFailed;
804 return gMIDIClient.sendEvent(port, uid, &evt, late);
807 int sendMIDISysex(int port, int uid, int length, uint8* data)
809 if (!gMIDIClient.mHandle) return errFailed;
810 snd_seq_event_t evt;
811 evt.type = SND_SEQ_EVENT_SYSEX; // MIDIOut.sysex patch 2007-01-16
812 snd_seq_ev_set_variable(&evt, length, data);
813 return gMIDIClient.sendEvent(port, uid, &evt, 0.f);
816 // =====================================================================
817 // Primitives
818 // =====================================================================
820 int prInitMIDI(struct VMGlobals *g, int numArgsPushed);
821 int prInitMIDI(struct VMGlobals *g, int numArgsPushed)
823 //PyrSlot *a = g->sp - 2;
824 PyrSlot *b = g->sp - 1;
825 PyrSlot *c = g->sp;
827 int err, numIn, numOut;
828 err = slotIntVal(b, &numIn);
829 if (err) return errWrongType;
831 err = slotIntVal(c, &numOut);
832 if (err) return errWrongType;
834 return initMIDI(numIn, numOut);
837 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed);
838 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed)
840 return initMIDIClient();
843 int prDisposeMIDIClient(VMGlobals *g, int numArgsPushed);
844 int prDisposeMIDIClient(VMGlobals *g, int numArgsPushed)
846 return disposeMIDI();
849 int prRestartMIDI(VMGlobals *g, int numArgsPushed);
850 int prRestartMIDI(VMGlobals *g, int numArgsPushed)
852 return restartMIDI();
855 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed);
856 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed)
858 return listMIDIEndpoints(g, g->sp);
861 int prConnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
862 int prConnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
864 //PyrSlot *a = g->sp - 2;
865 PyrSlot *b = g->sp - 1;
866 PyrSlot *c = g->sp;
868 int err, inputIndex, uid;
869 err = slotIntVal(b, &inputIndex);
870 if (err) return err;
872 err = slotIntVal(c, &uid);
873 if (err) return err;
875 return connectMIDIIn(inputIndex, uid);
878 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
879 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
881 PyrSlot *b = g->sp - 1;
882 PyrSlot *c = g->sp;
884 int err, inputIndex, uid;
885 err = slotIntVal(b, &inputIndex);
886 if (err) return err;
888 err = slotIntVal(c, &uid);
889 if (err) return err;
891 return disconnectMIDIIn(inputIndex, uid);
894 int prConnectMIDIOut(struct VMGlobals *g, int numArgsPushed);
895 int prConnectMIDIOut(struct VMGlobals *g, int numArgsPushed)
897 //PyrSlot *a = g->sp - 2;
898 PyrSlot *b = g->sp - 1;
899 PyrSlot *c = g->sp;
901 int err, inputIndex, uid;
902 err = slotIntVal(b, &inputIndex);
903 if (err) return err;
905 err = slotIntVal(c, &uid);
906 if (err) return err;
908 return connectMIDIOut(inputIndex, uid);
911 int prDisconnectMIDIOut(struct VMGlobals *g, int numArgsPushed);
912 int prDisconnectMIDIOut(struct VMGlobals *g, int numArgsPushed)
914 PyrSlot *b = g->sp - 1;
915 PyrSlot *c = g->sp;
917 int err, inputIndex, uid;
918 err = slotIntVal(b, &inputIndex);
919 if (err) return err;
921 err = slotIntVal(c, &uid);
922 if (err) return err;
924 return disconnectMIDIOut(inputIndex, uid);
927 int prSendMIDIOut(struct VMGlobals *g, int numArgsPushed);
928 int prSendMIDIOut(struct VMGlobals *g, int numArgsPushed)
930 //port, uid, len, hiStatus, loStatus, a, b, latency
931 //PyrSlot *m = g->sp - 8;
932 PyrSlot *p = g->sp - 7;
934 PyrSlot *u = g->sp - 6;
935 PyrSlot *l = g->sp - 5;
937 PyrSlot *his = g->sp - 4;
938 PyrSlot *los = g->sp - 3;
940 PyrSlot *a = g->sp - 2;
941 PyrSlot *b = g->sp - 1;
942 PyrSlot *plat = g->sp;
944 int err, outputIndex, uid, length, hiStatus, loStatus, aval, bval;
945 float late;
946 err = slotIntVal(p, &outputIndex);
947 if (err) return err;
949 err = slotIntVal(u, &uid);
950 if (err) return err;
952 err = slotIntVal(l, &length);
953 if (err) return err;
955 err = slotIntVal(his, &hiStatus);
956 if (err) return err;
958 err = slotIntVal(los, &loStatus);
959 if (err) return err;
961 err = slotIntVal(a, &aval);
962 if (err) return err;
964 err = slotIntVal(b, &bval);
965 if (err) return err;
967 err = slotFloatVal(plat, &late);
968 if (err) return err;
970 return sendMIDI(outputIndex, uid, length, hiStatus, loStatus, aval, bval, late);
973 int prSendSysex(VMGlobals *g, int numArgsPushed);
974 int prSendSysex(VMGlobals *g, int numArgsPushed)
976 int err, uid, outputIndex;
977 PyrInt8Array* packet;
979 // rcvr, uid, packet
980 PyrSlot* args = g->sp - 2;
982 err = slotIntVal(slotRawObject(args)->slots + g_ivx_MIDIOut_port, &outputIndex);
983 if (err) return err;
985 err = slotIntVal(args+1, &uid);
986 if (err) return err;
988 if( !isKindOfSlot(args+2, s_int8array->u.classobj) )
989 return errWrongType;
991 packet = slotRawInt8Array(&args[2]);
993 return sendMIDISysex(outputIndex, uid, packet->size, packet->b);
996 void initMIDIPrimitives()
998 int base, index;
1000 base = nextPrimitiveIndex();
1001 index = 0;
1003 s_midiin = getsym("MIDIIn");
1005 s_domidiaction = getsym("doAction");
1006 s_midiNoteOnAction = getsym("doNoteOnAction");
1007 s_midiNoteOffAction = getsym("doNoteOffAction");
1008 s_midiTouchAction = getsym("doTouchAction");
1009 s_midiControlAction = getsym("doControlAction");
1010 s_midiPolyTouchAction = getsym("doPolyTouchAction");
1011 s_midiProgramAction = getsym("doProgramAction");
1012 s_midiBendAction = getsym("doBendAction");
1013 s_midiSysexAction = getsym("doSysexAction");
1014 s_midiSysrtAction = getsym("doSysrtAction");
1015 s_midiSMPTEAction = getsym("doSMPTEaction");
1017 g_ivx_MIDIOut_port = instVarOffset("MIDIOut", "port");
1019 definePrimitive(base, index++, "_InitMIDI", prInitMIDI, 3, 0);
1020 definePrimitive(base, index++, "_InitMIDIClient", prInitMIDIClient, 1, 0);
1021 definePrimitive(base, index++, "_RestartMIDI", prRestartMIDI, 1, 0);
1022 definePrimitive(base, index++, "_DisposeMIDIClient", prDisposeMIDIClient, 1, 0);
1024 definePrimitive(base, index++, "_ListMIDIEndpoints", prListMIDIEndpoints, 1, 0);
1025 definePrimitive(base, index++, "_ConnectMIDIIn", prConnectMIDIIn, 3, 0);
1026 definePrimitive(base, index++, "_DisconnectMIDIIn", prDisconnectMIDIIn, 3, 0);
1027 definePrimitive(base, index++, "_ConnectMIDIOut", prConnectMIDIOut, 3, 0);
1028 definePrimitive(base, index++, "_DisconnectMIDIOut", prDisconnectMIDIOut, 3, 0);
1030 definePrimitive(base, index++, "_SendMIDIOut", prSendMIDIOut, 9, 0);
1031 definePrimitive(base, index++, "_SendSysex", prSendSysex, 3, 0); // MIDIOut.sysex patch 2007-01-16
1033 cleanUpMIDI();
1036 // EOF