2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 changes by charles picasso 14/april/2008 (sysex parsing + added running status)
23 changes by jan trutzschler v. f. 9/9/2002
24 the midiReadProc calls doAction in the class MIDIIn.
25 with the arguments: inUid, status, chan, val1, val2
26 added prDisposeMIDIClient
28 19/9 call different actions,disconnect midiInPort, midiout: sendmidi
29 04/feb/03 prListMIDIEndpoints modification by Ron Kuivila added jt.
31 #include <CoreAudio/HostTime.h>
32 #include <Carbon/Carbon.h>
33 #include <CoreMIDI/CoreMIDI.h>
36 #include "VMGlobals.h"
37 #include "PyrSymbolTable.h"
38 #include "PyrInterpreter.h"
39 #include "PyrKernel.h"
41 #include "PyrPrimitive.h"
42 #include "PyrObjectProto.h"
43 #include "PyrPrimitiveProto.h"
44 #include "PyrKernelProto.h"
45 #include "SC_InlineUnaryOp.h"
46 #include "SC_InlineBinaryOp.h"
50 PyrSymbol
* s_domidiaction
;
51 PyrSymbol
* s_midiNoteOnAction
;
52 PyrSymbol
* s_midiNoteOffAction
;
53 PyrSymbol
* s_midiTouchAction
;
54 PyrSymbol
* s_midiControlAction
;
55 PyrSymbol
* s_midiPolyTouchAction
;
56 PyrSymbol
* s_midiProgramAction
;
57 PyrSymbol
* s_midiBendAction
;
58 PyrSymbol
* s_midiSysexAction
;
59 PyrSymbol
* s_midiInvalidSysexAction
;
60 PyrSymbol
* s_midiSysrtAction
;
61 PyrSymbol
* s_midiSMPTEAction
;
64 PyrSymbol
* s_numMIDIDev
;
65 PyrSymbol
* s_midiclient
;
66 const int kMaxMidiPorts
= 16;
67 MIDIClientRef gMIDIClient
= 0;
68 MIDIPortRef gMIDIInPort
[kMaxMidiPorts
], gMIDIOutPort
[kMaxMidiPorts
];
69 int gNumMIDIInPorts
= 0, gNumMIDIOutPorts
= 0;
70 bool gMIDIInitialized
= false;
72 static bool gSysexFlag
= false;
73 static Byte gRunningStatus
= 0;
74 std::vector
<Byte
> gSysexData
;
76 void midiNotifyProc(const MIDINotification
*msg
, void* refCon
)
80 extern bool compiledOK
;
83 static void dumpSysexData() {
84 if(gSysexData
.size() <= 0)
86 std::vector
<Byte
>::const_iterator iter
= gSysexData
.begin(), end
= gSysexData
.end();
89 if((i
% 16) == 0 && (i
> 0))
92 printf("%02X ", *iter
++);
95 printf("sysex data dump size: %i bytes.\n", gSysexData
.size());
99 static void sysexBegin() {
100 gRunningStatus
= 0; // clear running status
105 static void scCallSysexAction(PyrSymbol
* action
, int recoverFromUID
) {
106 VMGlobals
*g
= gMainVMGlobals
;
107 if(recoverFromUID
) { // rebuild the VM so sc won't crash with two following calls
108 ++g
->sp
; SetObject(g
->sp
, s_midiin
->u
.classobj
); // Set the class MIDIIn
109 ++g
->sp
; SetInt(g
->sp
, recoverFromUID
); //src
112 PyrInt8Array
* sysexArray
= newPyrInt8Array(g
->gc
, gSysexData
.size(), 0, true);
113 sysexArray
->size
= gSysexData
.size();
114 std::copy(gSysexData
.begin(), gSysexData
.end(), sysexArray
->b
);
115 SetObject(g
->sp
, (PyrObject
*) sysexArray
); // chan argument unneeded as there
116 runInterpreter(g
, action
, 3 ); // special sysex action in the lang
119 static void sysexEnd(int lastUID
) {
121 scCallSysexAction(s_midiSysexAction
, lastUID
);
124 static void sysexEndInvalid() {
126 scCallSysexAction(s_midiInvalidSysexAction
, 0);
129 static int midiProcessSystemPacket(MIDIPacket
*pkt
, int chan
) {
131 VMGlobals
*g
= gMainVMGlobals
;
133 case 7: // added cp: Sysex EOX must be taken into account if first on data packet
138 Byte
* p_pkt
= pkt
->data
;
143 if(pktval
& 0x80) { // status byte
144 if(pktval
== 0xF7) { // end packet
145 gSysexData
.push_back(pktval
); // add EOX
147 sysexEnd(last_uid
); // if last_uid != 0 rebuild the VM.
149 sysexEndInvalid(); // invalid 1 byte with only EOX can happen
152 else if(pktval
== 0xF0) { // new packet
153 if(gSysexFlag
) {// invalid new one/should not happen -- but handle in case
154 // store the last uid value previous to invalid data to rebuild VM after sysexEndInvalid call
155 // since it may call sysexEnd() just after it !
156 if(slotIntVal(g
->sp
-1, &last_uid
)) {
157 post("error: failed retrieving uid value !");
162 sysexBegin(); // new sysex in
163 gSysexData
.push_back(pktval
); // add SOX
165 else {// abnormal data in middle of sysex packet
166 gSysexData
.push_back(pktval
); // add it as an abort message
167 sysexEndInvalid(); // flush invalid
168 m
= 0; // discard all packet
173 gSysexData
.push_back(pktval
); // add Byte
174 else // garbage - handle in case - discard it
177 return (pkt
->length
-m
);
182 index
= pkt
->data
[1] >> 4;
183 data
= pkt
->data
[1] & 0xf;
184 switch (index
) { case 1: case 3: case 5: case 7: { data
= data
<< 4; } }
185 SetInt(g
->sp
, index
); // chan unneeded
186 ++g
->sp
; SetInt(g
->sp
, data
); // special smpte action in the lang
187 runInterpreter(g
, s_midiSMPTEAction
, 4 );
191 ++g
->sp
; SetInt(g
->sp
, (pkt
->data
[2] << 7) | pkt
->data
[1]); //val1
192 runInterpreter(g
, s_midiSysrtAction
, 4);
195 case 3 : // song select
196 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[1]); //val1
197 runInterpreter(g
, s_midiSysrtAction
, 4);
205 gRunningStatus
= 0; // clear running status
206 runInterpreter(g
, s_midiSysrtAction
, 3);
210 g
->sp
-= 3; // nevermind
217 static void midiProcessPacket(MIDIPacket
*pkt
, size_t uid
)
221 pthread_mutex_lock (&gLangMutex
); //dont know if this is really needed/seems to be more stable..
222 // it is needed -jamesmcc
224 VMGlobals
*g
= gMainVMGlobals
;
226 int i
= 0; //cp : changed uint8 to int if packet->length >= 256 bug:(infinite loop)
227 while (i
< pkt
->length
) {
228 uint8 status
= pkt
->data
[i
] & 0xF0;
229 uint8 chan
= pkt
->data
[i
] & 0x0F;
230 g
->canCallOS
= false; // cannot call the OS
232 ++g
->sp
; SetObject(g
->sp
, s_midiin
->u
.classobj
); // Set the class MIDIIn
234 ++g
->sp
; SetInt(g
->sp
, uid
); //src
235 // ++g->sp; SetInt(g->sp, status); //status
236 ++g
->sp
; SetInt(g
->sp
, chan
); //chan
238 if(status
& 0x80) // set the running status for voice messages
239 gRunningStatus
= ((status
>> 4) == 0xF) ? 0 : pkt
->data
[i
]; // keep also additional info
242 case 0x80 : //noteOff
243 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
244 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
245 runInterpreter(g
, s_midiNoteOffAction
, 5);
249 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
250 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
251 runInterpreter(g
, pkt
->data
[i
+2] ? s_midiNoteOnAction
: s_midiNoteOffAction
, 5);
254 case 0xA0 : //polytouch
255 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
256 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
257 runInterpreter(g
, s_midiPolyTouchAction
, 5);
260 case 0xB0 : //control
261 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
262 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
263 runInterpreter(g
, s_midiControlAction
, 5);
266 case 0xC0 : //program
267 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
268 runInterpreter(g
, s_midiProgramAction
, 4);
272 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
273 runInterpreter(g
, s_midiTouchAction
, 4);
277 ++g
->sp
; SetInt(g
->sp
, (pkt
->data
[i
+2] << 7) | pkt
->data
[i
+1]); //val1
278 runInterpreter(g
, s_midiBendAction
, 4);
282 i
+= midiProcessSystemPacket(pkt
, chan
);
284 default : // data byte => continuing sysex message
285 if(gRunningStatus
&& !gSysexFlag
) { // modified cp: handling running status. may be we should here
286 status
= gRunningStatus
& 0xF0; // accept running status only inside a packet beginning
287 chan
= gRunningStatus
& 0x0F; // with a valid status byte ?
290 goto L
; // parse again with running status set
293 i
+= midiProcessSystemPacket(pkt
, chan
);
297 g
->canCallOS
= false;
299 pthread_mutex_unlock (&gLangMutex
);
303 static void midiReadProc(const MIDIPacketList
*pktlist
, void* readProcRefCon
, void* srcConnRefCon
)
305 MIDIPacket
*pkt
= (MIDIPacket
*)pktlist
->packet
;
306 size_t uid
= (size_t) srcConnRefCon
;
307 for (uint32 i
=0; i
<pktlist
->numPackets
; ++i
) {
308 midiProcessPacket(pkt
, uid
);
309 pkt
= MIDIPacketNext(pkt
);
315 int initMIDI(int numIn
, int numOut
)
318 numIn
= sc_clip(numIn
, 1, kMaxMidiPorts
);
319 numOut
= sc_clip(numOut
, 1, kMaxMidiPorts
);
321 int enc
= kCFStringEncodingMacRoman
;
322 CFAllocatorRef alloc
= CFAllocatorGetDefault();
324 CFStringRef clientName
= CFStringCreateWithCString(alloc
, "SuperCollider", enc
);
326 OSStatus err
= MIDIClientCreate(clientName
, midiNotifyProc
, nil
, &gMIDIClient
);
328 post("Could not create MIDI client. error %d\n", err
);
331 CFRelease(clientName
);
333 for (int i
=0; i
<numIn
; ++i
) {
335 sprintf(str
, "in%d\n", i
);
336 CFStringRef inputPortName
= CFStringCreateWithCString(alloc
, str
, enc
);
338 err
= MIDIInputPortCreate(gMIDIClient
, inputPortName
, midiReadProc
, &i
, gMIDIInPort
+i
);
341 post("Could not create MIDI port %s. error %d\n", str
, err
);
344 CFRelease(inputPortName
);
347 /*int n = MIDIGetNumberOfSources();
348 printf("%d sources\n", n);
349 for (i = 0; i < n; ++i) {
350 MIDIEndpointRef src = MIDIGetSource(i);
351 MIDIPortConnectSource(inPort, src, NULL);
354 gNumMIDIInPorts
= numIn
;
356 for (int i
=0; i
<numOut
; ++i
) {
358 sprintf(str
, "out%d\n", i
);
359 CFStringRef outputPortName
= CFStringCreateWithCString(alloc
, str
, enc
);
361 err
= MIDIOutputPortCreate(gMIDIClient
, outputPortName
, gMIDIOutPort
+i
);
363 gNumMIDIOutPorts
= i
;
364 post("Could not create MIDI out port. error %d\n", err
);
368 CFRelease(outputPortName
);
370 gNumMIDIOutPorts
= numOut
;
377 * do not catch errors when disposing ports
378 * MIDIClientDispose should normally dispose the ports attached to it
379 * but clean up the pointers in case
382 for (i
=0; i
<gNumMIDIOutPorts
; ++i
) {
383 MIDIPortDispose(gMIDIOutPort
[i
]);
386 gNumMIDIOutPorts
= 0;
388 for (i
=0; i
<gNumMIDIInPorts
; ++i
) {
389 MIDIPortDispose(gMIDIInPort
[i
]);
395 if( MIDIClientDispose(gMIDIClient
) ) {
396 post( "Error: failed to dispose MIDIClient\n" );
405 void midiListEndpoints()
411 int prListMIDIEndpoints(struct VMGlobals
*g
, int numArgsPushed
);
412 int prListMIDIEndpoints(struct VMGlobals
*g
, int numArgsPushed
)
416 int numSrc
= MIDIGetNumberOfSources();
417 int numDst
= MIDIGetNumberOfDestinations();
419 PyrObject
* idarray
= newPyrArray(g
->gc
, 6 * sizeof(PyrObject
), 0 , true);
420 SetObject(a
, idarray
);
422 PyrObject
* idarraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(SInt32
), 0 , true);
423 SetObject(idarray
->slots
+idarray
->size
++, idarraySo
);
424 g
->gc
->GCWrite(idarray
, idarraySo
);
426 PyrObject
* devarraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(PyrObject
), 0 , true);
427 SetObject(idarray
->slots
+idarray
->size
++, devarraySo
);
428 g
->gc
->GCWrite(idarray
, devarraySo
);
430 PyrObject
* namearraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(PyrObject
), 0 , true);
431 SetObject(idarray
->slots
+idarray
->size
++, namearraySo
);
432 g
->gc
->GCWrite(idarray
, namearraySo
);
434 PyrObject
* idarrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(SInt32
), 0 , true);
435 SetObject(idarray
->slots
+idarray
->size
++, idarrayDe
);
436 g
->gc
->GCWrite(idarray
, idarrayDe
);
438 PyrObject
* namearrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(PyrObject
), 0 , true);
439 SetObject(idarray
->slots
+idarray
->size
++, namearrayDe
);
440 g
->gc
->GCWrite(idarray
, namearrayDe
);
442 PyrObject
* devarrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(PyrObject
), 0 , true);
443 SetObject(idarray
->slots
+idarray
->size
++, devarrayDe
);
444 g
->gc
->GCWrite(idarray
, devarrayDe
);
447 for (int i
=0; i
<numSrc
; ++i
) {
448 MIDIEndpointRef src
= MIDIGetSource(i
);
450 MIDIObjectGetIntegerProperty(src
, kMIDIPropertyUniqueID
, &id
);
453 error
= MIDIEndpointGetEntity(src
, &ent
);
455 CFStringRef devname
, endname
;
456 char cendname
[1024], cdevname
[1024];
458 // Virtual sources don't have entities
461 MIDIObjectGetStringProperty(src
, kMIDIPropertyName
, &devname
);
462 MIDIObjectGetStringProperty(src
, kMIDIPropertyName
, &endname
);
463 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
464 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
470 MIDIEntityGetDevice(ent
, &dev
);
471 MIDIObjectGetStringProperty(dev
, kMIDIPropertyName
, &devname
);
472 MIDIObjectGetStringProperty(src
, kMIDIPropertyName
, &endname
);
473 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
474 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
477 PyrString
*string
= newPyrString(g
->gc
, cendname
, 0, true);
478 SetObject(namearraySo
->slots
+i
, string
);
480 g
->gc
->GCWrite(namearraySo
, (PyrObject
*)string
);
482 PyrString
*devstring
= newPyrString(g
->gc
, cdevname
, 0, true);
483 SetObject(devarraySo
->slots
+i
, devstring
);
485 g
->gc
->GCWrite(devarraySo
, (PyrObject
*)devstring
);
487 SetInt(idarraySo
->slots
+i
, id
);
496 // post("numDst %d\n", numDst);
497 for (int i
=0; i
<numDst
; ++i
) {
498 MIDIEndpointRef dst
= MIDIGetDestination(i
);
500 MIDIObjectGetIntegerProperty(dst
, kMIDIPropertyUniqueID
, &id
);
503 error
= MIDIEndpointGetEntity(dst
, &ent
);
505 CFStringRef devname
, endname
;
506 char cendname
[1024], cdevname
[1024];
508 // Virtual destinations don't have entities either
511 MIDIObjectGetStringProperty(dst
, kMIDIPropertyName
, &devname
);
512 MIDIObjectGetStringProperty(dst
, kMIDIPropertyName
, &endname
);
513 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
514 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
521 MIDIEntityGetDevice(ent
, &dev
);
522 MIDIObjectGetStringProperty(dev
, kMIDIPropertyName
, &devname
);
523 MIDIObjectGetStringProperty(dst
, kMIDIPropertyName
, &endname
);
524 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
525 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
528 PyrString
*string
= newPyrString(g
->gc
, cendname
, 0, true);
529 SetObject(namearrayDe
->slots
+namearrayDe
->size
++, string
);
530 g
->gc
->GCWrite(namearrayDe
, (PyrObject
*)string
);
532 PyrString
*devstring
= newPyrString(g
->gc
, cdevname
, 0, true);
534 SetObject(devarrayDe
->slots
+devarrayDe
->size
++, devstring
);
535 g
->gc
->GCWrite(devarrayDe
, (PyrObject
*)devstring
);
537 SetInt(idarrayDe
->slots
+idarrayDe
->size
++, id
);
548 int prConnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
);
549 int prConnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
)
551 //PyrSlot *a = g->sp - 2;
552 PyrSlot
*b
= g
->sp
- 1;
555 int err
, inputIndex
, uid
;
556 err
= slotIntVal(b
, &inputIndex
);
557 if (err
) return errWrongType
;
558 if (inputIndex
< 0 || inputIndex
>= gNumMIDIInPorts
) return errIndexOutOfRange
;
560 err
= slotIntVal(c
, &uid
);
561 if (err
) return errWrongType
;
564 MIDIEndpointRef src
=0;
565 MIDIObjectType mtype
;
566 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&src
, &mtype
);
567 if (mtype
!= kMIDIObjectType_Source
) return errFailed
;
569 //pass the uid to the midiReadProc to identify the src
571 MIDIPortConnectSource(gMIDIInPort
[inputIndex
], src
, (void*)uid
);
575 int prDisconnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
);
576 int prDisconnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
)
578 PyrSlot
*b
= g
->sp
- 1;
581 int err
, inputIndex
, uid
;
582 err
= slotIntVal(b
, &inputIndex
);
584 if (inputIndex
< 0 || inputIndex
>= gNumMIDIInPorts
) return errIndexOutOfRange
;
585 err
= slotIntVal(c
, &uid
);
588 MIDIEndpointRef src
=0;
589 MIDIObjectType mtype
;
590 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&src
, &mtype
);
591 if (mtype
!= kMIDIObjectType_Source
) return errFailed
;
593 MIDIPortDisconnectSource(gMIDIInPort
[inputIndex
], src
);
598 int prInitMIDI(struct VMGlobals
*g
, int numArgsPushed
);
599 int prInitMIDI(struct VMGlobals
*g
, int numArgsPushed
)
601 //PyrSlot *a = g->sp - 2;
602 PyrSlot
*b
= g
->sp
- 1;
605 int err
, numIn
, numOut
;
606 err
= slotIntVal(b
, &numIn
);
607 if (err
) return errWrongType
;
609 err
= slotIntVal(c
, &numOut
);
610 if (err
) return errWrongType
;
612 return initMIDI(numIn
, numOut
);
614 int prDisposeMIDIClient(VMGlobals
*g
, int numArgsPushed
);
615 int prDisposeMIDIClient(VMGlobals
*g
, int numArgsPushed
)
617 return midiCleanUp();
619 int prRestartMIDI(VMGlobals
*g
, int numArgsPushed
);
620 int prRestartMIDI(VMGlobals
*g
, int numArgsPushed
)
626 void freeSysex(MIDISysexSendRequest
* pk
);
627 void freeSysex(MIDISysexSendRequest
* pk
)
633 int prSendSysex(VMGlobals
*g
, int numArgsPushed
);
634 int prSendSysex(VMGlobals
*g
, int numArgsPushed
)
638 if( !isKindOfSlot(g
->sp
, s_int8array
->u
.classobj
) )
641 PyrInt8Array
* packet
= slotRawInt8Array(g
->sp
);
644 PyrSlot
*u
= g
->sp
- 1;
645 err
= slotIntVal(u
, &uid
);
648 MIDIEndpointRef dest
;
649 MIDIObjectType mtype
;
650 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&dest
, &mtype
);
651 if (mtype
!= kMIDIObjectType_Destination
) return errFailed
;
652 if (!dest
) return errFailed
;
654 MIDISysexSendRequest
*pk
= (MIDISysexSendRequest
*) malloc (sizeof(MIDISysexSendRequest
) + size
);
655 Byte
*data
= (Byte
*)pk
+ sizeof(MIDISysexSendRequest
);
657 memcpy(data
,packet
->b
, size
);
658 pk
->complete
= false;
659 pk
-> destination
= dest
;
661 pk
-> bytesToSend
= size
;
662 pk
->completionProc
= freeSysex
;
663 pk
->completionRefCon
= 0;
665 return ((MIDISendSysex(pk
) == (OSStatus
)0) ? errNone
: errFailed
);
668 void sendmidi(int port
, MIDIEndpointRef dest
, int length
, int hiStatus
, int loStatus
, int aval
, int bval
, float late
);
669 void sendmidi(int port
, MIDIEndpointRef dest
, int length
, int hiStatus
, int loStatus
, int aval
, int bval
, float late
)
671 MIDIPacketList mpktlist
;
672 MIDIPacketList
* pktlist
= &mpktlist
;
673 MIDIPacket
* pk
= MIDIPacketListInit(pktlist
);
674 //lets add some latency
675 float latency
= 1000000000 * late
; //secs to nano
676 UInt64 utime
= AudioConvertNanosToHostTime( AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) + (UInt64
)latency
);
677 ByteCount nData
= (ByteCount
) length
;
678 pk
->data
[0] = (Byte
) (hiStatus
& 0xF0) | (loStatus
& 0x0F);
679 pk
->data
[1] = (Byte
) aval
;
680 pk
->data
[2] = (Byte
) bval
;
681 pk
= MIDIPacketListAdd(pktlist
, sizeof(struct MIDIPacketList
) , pk
,(MIDITimeStamp
) utime
,nData
,pk
->data
);
682 /*OSStatus error =*/ MIDISend(gMIDIOutPort
[port
], dest
, pktlist
);
685 int prSendMIDIOut(struct VMGlobals
*g
, int numArgsPushed
);
686 int prSendMIDIOut(struct VMGlobals
*g
, int numArgsPushed
)
688 //port, uid, len, hiStatus, loStatus, a, b, latency
689 //PyrSlot *m = g->sp - 8;
690 PyrSlot
*p
= g
->sp
- 7;
692 PyrSlot
*u
= g
->sp
- 6;
693 PyrSlot
*l
= g
->sp
- 5;
695 PyrSlot
*his
= g
->sp
- 4;
696 PyrSlot
*los
= g
->sp
- 3;
698 PyrSlot
*a
= g
->sp
- 2;
699 PyrSlot
*b
= g
->sp
- 1;
700 PyrSlot
*plat
= g
->sp
;
703 int err
, outputIndex
, uid
, length
, hiStatus
, loStatus
, aval
, bval
;
705 err
= slotIntVal(p
, &outputIndex
);
707 if (outputIndex
< 0 || outputIndex
>= gNumMIDIInPorts
) return errIndexOutOfRange
;
709 err
= slotIntVal(u
, &uid
);
711 err
= slotIntVal(l
, &length
);
713 err
= slotIntVal(his
, &hiStatus
);
715 err
= slotIntVal(los
, &loStatus
);
717 err
= slotIntVal(a
, &aval
);
719 err
= slotIntVal(b
, &bval
);
721 err
= slotFloatVal(plat
, &late
);
724 MIDIEndpointRef dest
;
725 MIDIObjectType mtype
;
726 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&dest
, &mtype
);
727 if (mtype
!= kMIDIObjectType_Destination
) return errFailed
;
729 if (!dest
) return errFailed
;
731 sendmidi(outputIndex
, dest
, length
, hiStatus
, loStatus
, aval
, bval
, late
);
735 // not needed in CoreMIDI:
740 int prInitMIDIClient(struct VMGlobals
*g
, int numArgsPushed
);
741 int prInitMIDIClient(struct VMGlobals
*g
, int numArgsPushed
)
743 return initMIDIClient();
747 void initMIDIPrimitives()
751 base
= nextPrimitiveIndex();
753 gSysexData
.reserve(1024);
755 s_midiin
= getsym("MIDIIn");
756 s_domidiaction
= getsym("doAction");
757 s_midiNoteOnAction
= getsym("doNoteOnAction");
758 s_midiNoteOffAction
= getsym("doNoteOffAction");
759 s_midiTouchAction
= getsym("doTouchAction");
760 s_midiControlAction
= getsym("doControlAction");
761 s_midiPolyTouchAction
= getsym("doPolyTouchAction");
762 s_midiProgramAction
= getsym("doProgramAction");
763 s_midiBendAction
= getsym("doBendAction");
764 s_midiSysexAction
= getsym("doSysexAction");
765 s_midiInvalidSysexAction
= getsym("doInvalidSysexAction"); // client can handle incorrect case
766 s_midiSysrtAction
= getsym("doSysrtAction");
767 s_midiSMPTEAction
= getsym("doSMPTEaction");
768 s_numMIDIDev
= getsym("prSetNumberOfDevices");
769 s_midiclient
= getsym("MIDIClient");
771 definePrimitive(base
, index
++, "_ListMIDIEndpoints", prListMIDIEndpoints
, 1, 0);
772 definePrimitive(base
, index
++, "_InitMIDI", prInitMIDI
, 3, 0);
773 definePrimitive(base
, index
++, "_InitMIDIClient", prInitMIDIClient
, 1, 0);
774 definePrimitive(base
, index
++, "_ConnectMIDIIn", prConnectMIDIIn
, 3, 0);
775 definePrimitive(base
, index
++, "_DisconnectMIDIIn", prDisconnectMIDIIn
, 3, 0);
776 definePrimitive(base
, index
++, "_DisposeMIDIClient", prDisposeMIDIClient
, 1, 0);
777 definePrimitive(base
, index
++, "_RestartMIDI", prRestartMIDI
, 1, 0);
778 definePrimitive(base
, index
++, "_SendMIDIOut", prSendMIDIOut
, 9, 0);
779 definePrimitive(base
, index
++, "_SendSysex", prSendSysex
, 3, 0);
780 if(gMIDIClient
) midiCleanUp();