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
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"
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>
89 static const size_t kAlsaMaxPacketSize
= 3;
90 static const size_t kAlsaMaxPortNameLen
= 256;
93 struct SC_AlsaMidiPacket
95 uint8 data
[kAlsaMaxPacketSize
];
99 struct SC_AlsaMidiClient
104 int mInPorts
[kMaxMidiPorts
];
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
;
123 struct SC_AlsaMidiPort
129 char name
[kAlsaMaxPortNameLen
];
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
);
152 VMGlobals
* g
= gMainVMGlobals
;
153 PyrInt8Array
* sysexArray
;
155 SC_AlsaMidiPacket pkt
;
157 g
->canCallOS
= false; // cannot call the OS
160 ++g
->sp
; SetObject(g
->sp
, s_midiin
->u
.classobj
);
162 ++g
->sp
; SetInt(g
->sp
, SC_AlsaMakeUID(evt
->source
.client
, evt
->source
.port
));
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);
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);
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);
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);
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);
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);
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);
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;
213 "mtc qframe: byte 0x%x index 0x%x data 0x%x\n",
214 evt
->data
.control
.value
,
225 ++g
->sp
; SetInt(g
->sp
, index
);
226 ++g
->sp
; SetInt(g
->sp
, data
);
228 runInterpreter(g
, s_midiSMPTEAction
, 4);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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
);
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) {
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);
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
;
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
));
340 dst
.client
= snd_seq_client_info_get_client(cinfo
);
341 dst
.port
= mInPorts
[inputIndex
];
342 SC_AlsaParseUID(uid
, cid
, 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
));
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
;
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
));
376 src
.client
= snd_seq_client_info_get_client(cinfo
);
377 src
.port
= mOutPorts
[outputIndex
];
378 SC_AlsaParseUID(uid
, cid
, 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
));
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
]);
404 // send to all subscribed ports
405 snd_seq_ev_set_subs(evt
);
407 // send to specific port
409 SC_AlsaParseUID(uid
, cid
, pid
);
410 snd_seq_ev_set_dest(evt
, cid
, pid
);
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
);
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);
439 int initMIDI(int numIn
, int numOut
)
441 SC_AlsaMidiClient
* client
= &gMIDIClient
;
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) {
452 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno
));
456 snd_seq_set_client_name(client
->mHandle
, "SuperCollider");
458 // allocate i/o ports
459 for (i
=0; i
< numIn
; i
++) {
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
);
471 post("MIDI (ALSA): could not create MIDI in port %d: %s\n", i
, snd_strerror(errno
));
475 client
->mInPorts
[i
] = port
;
478 client
->mNumInPorts
= i
;
480 for (i
=0; i
< numOut
; i
++) {
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
);
492 post("MIDI (ALSA): could not create MIDI out port %d: %s\n", i
, snd_strerror(errno
));
496 client
->mOutPorts
[i
] = port
;
499 client
->mNumOutPorts
= i
;
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");
514 if (snd_midi_event_new(32, &client
->mMidiToEvent
) < 0) {
515 client
->mMidiToEvent
= 0;
516 post("MIDI (ALSA): could not create MIDI encoder\n");
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");
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) {
542 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno
));
546 snd_seq_set_client_name(client
->mHandle
, "SuperCollider");
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");
561 if (snd_midi_event_new(32, &client
->mMidiToEvent
) < 0) {
562 client
->mMidiToEvent
= 0;
563 post("MIDI (ALSA): could not create MIDI encoder\n");
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");
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
);
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
)
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");
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");
666 if (SC_AlsaCheckPerm(pinfo
, SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
)) {
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
)) {
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
);
721 g
->gc
->GCWrite(namearraySo
, (PyrObject
*)string
);
723 PyrString
*devstring
= newPyrString(g
->gc
, name
, 0, true);
724 SetObject(devarraySo
->slots
+i
, devstring
);
726 g
->gc
->GCWrite(devarraySo
, (PyrObject
*)devstring
);
728 SetInt(idarraySo
->slots
+i
, srcPorts
[i
].uid
);
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
);
742 g
->gc
->GCWrite(devarrayDe
, (PyrObject
*)devstring
);
744 SetInt(idarrayDe
->slots
+i
, dstPorts
[i
].uid
);
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);
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
));
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
;
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 // =====================================================================
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;
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;
860 int err
, inputIndex
, uid
;
861 err
= slotIntVal(b
, &inputIndex
);
864 err
= slotIntVal(c
, &uid
);
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;
876 int err
, inputIndex
, uid
;
877 err
= slotIntVal(b
, &inputIndex
);
880 err
= slotIntVal(c
, &uid
);
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;
893 int err
, inputIndex
, uid
;
894 err
= slotIntVal(b
, &inputIndex
);
897 err
= slotIntVal(c
, &uid
);
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;
909 int err
, inputIndex
, uid
;
910 err
= slotIntVal(b
, &inputIndex
);
913 err
= slotIntVal(c
, &uid
);
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
;
938 err
= slotIntVal(p
, &outputIndex
);
941 err
= slotIntVal(u
, &uid
);
944 err
= slotIntVal(l
, &length
);
947 err
= slotIntVal(his
, &hiStatus
);
950 err
= slotIntVal(los
, &loStatus
);
953 err
= slotIntVal(a
, &aval
);
956 err
= slotIntVal(b
, &bval
);
959 err
= slotFloatVal(plat
, &late
);
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
;
972 PyrSlot
* args
= g
->sp
- 2;
974 err
= slotIntVal(slotRawObject(args
)->slots
+ g_ivx_MIDIOut_port
, &outputIndex
);
977 err
= slotIntVal(args
+1, &uid
);
980 if( !isKindOfSlot(args
+2, s_int8array
->u
.classobj
) )
983 packet
= slotRawInt8Array(&args
[2]);
985 return sendMIDISysex(outputIndex
, uid
, packet
->size
, packet
->b
);
988 void initMIDIPrimitives()
992 base
= nextPrimitiveIndex();
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