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"
40 #include "SC_LanguageClient.h"
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>
90 static const size_t kAlsaMaxPacketSize
= 3;
91 static const size_t kAlsaMaxPortNameLen
= 256;
94 struct SC_AlsaMidiPacket
96 uint8 data
[kAlsaMaxPacketSize
];
100 struct SC_AlsaMidiClient
105 int mInPorts
[kMaxMidiPorts
];
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
;
124 struct SC_AlsaMidiPort
130 char name
[kAlsaMaxPortNameLen
];
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
);
155 postfl("error when locking language (%d)\n", status
);
160 VMGlobals
* g
= gMainVMGlobals
;
161 PyrInt8Array
* sysexArray
;
163 SC_AlsaMidiPacket pkt
;
165 g
->canCallOS
= false; // cannot call the OS
168 ++g
->sp
; SetObject(g
->sp
, s_midiin
->u
.classobj
);
170 ++g
->sp
; SetInt(g
->sp
, SC_AlsaMakeUID(evt
->source
.client
, evt
->source
.port
));
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);
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);
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);
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);
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);
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);
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);
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;
221 "mtc qframe: byte 0x%x index 0x%x data 0x%x\n",
222 evt
->data
.control
.value
,
233 ++g
->sp
; SetInt(g
->sp
, index
);
234 ++g
->sp
; SetInt(g
->sp
, data
);
236 runInterpreter(g
, s_midiSMPTEAction
, 4);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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
);
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) {
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);
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
;
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
));
348 dst
.client
= snd_seq_client_info_get_client(cinfo
);
349 dst
.port
= mInPorts
[inputIndex
];
350 SC_AlsaParseUID(uid
, cid
, 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
));
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
;
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
));
384 src
.client
= snd_seq_client_info_get_client(cinfo
);
385 src
.port
= mOutPorts
[outputIndex
];
386 SC_AlsaParseUID(uid
, cid
, 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
));
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
]);
412 // send to all subscribed ports
413 snd_seq_ev_set_subs(evt
);
415 // send to specific port
417 SC_AlsaParseUID(uid
, cid
, pid
);
418 snd_seq_ev_set_dest(evt
, cid
, pid
);
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
);
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);
447 int initMIDI(int numIn
, int numOut
)
449 SC_AlsaMidiClient
* client
= &gMIDIClient
;
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) {
460 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno
));
464 snd_seq_set_client_name(client
->mHandle
, "SuperCollider");
466 // allocate i/o ports
467 for (i
=0; i
< numIn
; i
++) {
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
);
479 post("MIDI (ALSA): could not create MIDI in port %d: %s\n", i
, snd_strerror(errno
));
483 client
->mInPorts
[i
] = port
;
486 client
->mNumInPorts
= i
;
488 for (i
=0; i
< numOut
; i
++) {
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
);
500 post("MIDI (ALSA): could not create MIDI out port %d: %s\n", i
, snd_strerror(errno
));
504 client
->mOutPorts
[i
] = port
;
507 client
->mNumOutPorts
= i
;
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");
522 if (snd_midi_event_new(32, &client
->mMidiToEvent
) < 0) {
523 client
->mMidiToEvent
= 0;
524 post("MIDI (ALSA): could not create MIDI encoder\n");
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");
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) {
550 post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno
));
554 snd_seq_set_client_name(client
->mHandle
, "SuperCollider");
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");
569 if (snd_midi_event_new(32, &client
->mMidiToEvent
) < 0) {
570 client
->mMidiToEvent
= 0;
571 post("MIDI (ALSA): could not create MIDI encoder\n");
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");
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
);
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
)
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");
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");
674 if (SC_AlsaCheckPerm(pinfo
, SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
)) {
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
)) {
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
);
729 g
->gc
->GCWrite(namearraySo
, (PyrObject
*)string
);
731 PyrString
*devstring
= newPyrString(g
->gc
, name
, 0, true);
732 SetObject(devarraySo
->slots
+i
, devstring
);
734 g
->gc
->GCWrite(devarraySo
, (PyrObject
*)devstring
);
736 SetInt(idarraySo
->slots
+i
, srcPorts
[i
].uid
);
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
);
750 g
->gc
->GCWrite(devarrayDe
, (PyrObject
*)devstring
);
752 SetInt(idarrayDe
->slots
+i
, dstPorts
[i
].uid
);
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);
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
));
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
;
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 // =====================================================================
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;
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;
868 int err
, inputIndex
, uid
;
869 err
= slotIntVal(b
, &inputIndex
);
872 err
= slotIntVal(c
, &uid
);
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;
884 int err
, inputIndex
, uid
;
885 err
= slotIntVal(b
, &inputIndex
);
888 err
= slotIntVal(c
, &uid
);
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;
901 int err
, inputIndex
, uid
;
902 err
= slotIntVal(b
, &inputIndex
);
905 err
= slotIntVal(c
, &uid
);
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;
917 int err
, inputIndex
, uid
;
918 err
= slotIntVal(b
, &inputIndex
);
921 err
= slotIntVal(c
, &uid
);
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
;
946 err
= slotIntVal(p
, &outputIndex
);
949 err
= slotIntVal(u
, &uid
);
952 err
= slotIntVal(l
, &length
);
955 err
= slotIntVal(his
, &hiStatus
);
958 err
= slotIntVal(los
, &loStatus
);
961 err
= slotIntVal(a
, &aval
);
964 err
= slotIntVal(b
, &bval
);
967 err
= slotFloatVal(plat
, &late
);
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
;
980 PyrSlot
* args
= g
->sp
- 2;
982 err
= slotIntVal(slotRawObject(args
)->slots
+ g_ivx_MIDIOut_port
, &outputIndex
);
985 err
= slotIntVal(args
+1, &uid
);
988 if( !isKindOfSlot(args
+2, s_int8array
->u
.classobj
) )
991 packet
= slotRawInt8Array(&args
[2]);
993 return sendMIDISysex(outputIndex
, uid
, packet
->size
, packet
->b
);
996 void initMIDIPrimitives()
1000 base
= nextPrimitiveIndex();
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