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.
32 #include <mach/mach_time.h>
34 #include <CoreAudio/HostTime.h>
35 #include <Carbon/Carbon.h>
37 #include <CoreMIDI/CoreMIDI.h>
40 #include "VMGlobals.h"
41 #include "PyrSymbolTable.h"
42 #include "PyrInterpreter.h"
43 #include "PyrKernel.h"
45 #include "PyrPrimitive.h"
46 #include "PyrObjectProto.h"
47 #include "PyrPrimitiveProto.h"
48 #include "PyrKernelProto.h"
49 #include "SC_InlineUnaryOp.h"
50 #include "SC_InlineBinaryOp.h"
54 PyrSymbol
* s_domidiaction
;
55 PyrSymbol
* s_midiNoteOnAction
;
56 PyrSymbol
* s_midiNoteOffAction
;
57 PyrSymbol
* s_midiTouchAction
;
58 PyrSymbol
* s_midiControlAction
;
59 PyrSymbol
* s_midiPolyTouchAction
;
60 PyrSymbol
* s_midiProgramAction
;
61 PyrSymbol
* s_midiBendAction
;
62 PyrSymbol
* s_midiSysexAction
;
63 PyrSymbol
* s_midiInvalidSysexAction
;
64 PyrSymbol
* s_midiSysrtAction
;
65 PyrSymbol
* s_midiSMPTEAction
;
68 PyrSymbol
* s_numMIDIDev
;
69 PyrSymbol
* s_midiclient
;
70 const int kMaxMidiPorts
= 16;
71 MIDIClientRef gMIDIClient
= 0;
72 MIDIPortRef gMIDIInPort
[kMaxMidiPorts
], gMIDIOutPort
[kMaxMidiPorts
];
73 int gNumMIDIInPorts
= 0, gNumMIDIOutPorts
= 0;
74 bool gMIDIInitialized
= false;
76 static bool gSysexFlag
= false;
77 static Byte gRunningStatus
= 0;
78 std::vector
<Byte
> gSysexData
;
80 void midiNotifyProc(const MIDINotification
*msg
, void* refCon
)
84 extern bool compiledOK
;
87 static void dumpSysexData() {
88 if(gSysexData
.size() <= 0)
90 std::vector
<Byte
>::const_iterator iter
= gSysexData
.begin(), end
= gSysexData
.end();
93 if((i
% 16) == 0 && (i
> 0))
96 printf("%02X ", *iter
++);
99 printf("sysex data dump size: %i bytes.\n", gSysexData
.size());
103 static void sysexBegin() {
104 gRunningStatus
= 0; // clear running status
109 static void scCallSysexAction(PyrSymbol
* action
, int recoverFromUID
) {
110 VMGlobals
*g
= gMainVMGlobals
;
111 if(recoverFromUID
) { // rebuild the VM so sc won't crash with two following calls
112 ++g
->sp
; SetObject(g
->sp
, s_midiin
->u
.classobj
); // Set the class MIDIIn
113 ++g
->sp
; SetInt(g
->sp
, recoverFromUID
); //src
116 PyrInt8Array
* sysexArray
= newPyrInt8Array(g
->gc
, gSysexData
.size(), 0, true);
117 sysexArray
->size
= gSysexData
.size();
118 std::copy(gSysexData
.begin(), gSysexData
.end(), sysexArray
->b
);
119 SetObject(g
->sp
, (PyrObject
*) sysexArray
); // chan argument unneeded as there
120 runInterpreter(g
, action
, 3 ); // special sysex action in the lang
123 static void sysexEnd(int lastUID
) {
125 scCallSysexAction(s_midiSysexAction
, lastUID
);
128 static void sysexEndInvalid() {
130 scCallSysexAction(s_midiInvalidSysexAction
, 0);
133 static int midiProcessSystemPacket(MIDIPacket
*pkt
, int chan
) {
135 VMGlobals
*g
= gMainVMGlobals
;
137 case 7: // added cp: Sysex EOX must be taken into account if first on data packet
142 Byte
* p_pkt
= pkt
->data
;
147 if(pktval
& 0x80) { // status byte
148 if(pktval
== 0xF7) { // end packet
149 gSysexData
.push_back(pktval
); // add EOX
151 sysexEnd(last_uid
); // if last_uid != 0 rebuild the VM.
153 sysexEndInvalid(); // invalid 1 byte with only EOX can happen
156 else if(pktval
== 0xF0) { // new packet
157 if(gSysexFlag
) {// invalid new one/should not happen -- but handle in case
158 // store the last uid value previous to invalid data to rebuild VM after sysexEndInvalid call
159 // since it may call sysexEnd() just after it !
160 if(slotIntVal(g
->sp
-1, &last_uid
)) {
161 post("error: failed retrieving uid value !");
166 sysexBegin(); // new sysex in
167 gSysexData
.push_back(pktval
); // add SOX
169 else {// abnormal data in middle of sysex packet
170 gSysexData
.push_back(pktval
); // add it as an abort message
171 sysexEndInvalid(); // flush invalid
172 m
= 0; // discard all packet
177 gSysexData
.push_back(pktval
); // add Byte
178 else // garbage - handle in case - discard it
181 return (pkt
->length
-m
);
186 index
= pkt
->data
[1] >> 4;
187 data
= pkt
->data
[1] & 0xf;
188 switch (index
) { case 1: case 3: case 5: case 7: { data
= data
<< 4; } }
189 SetInt(g
->sp
, index
); // chan unneeded
190 ++g
->sp
; SetInt(g
->sp
, data
); // special smpte action in the lang
191 runInterpreter(g
, s_midiSMPTEAction
, 4 );
195 ++g
->sp
; SetInt(g
->sp
, (pkt
->data
[2] << 7) | pkt
->data
[1]); //val1
196 runInterpreter(g
, s_midiSysrtAction
, 4);
199 case 3 : // song select
200 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[1]); //val1
201 runInterpreter(g
, s_midiSysrtAction
, 4);
209 gRunningStatus
= 0; // clear running status
210 runInterpreter(g
, s_midiSysrtAction
, 3);
214 g
->sp
-= 3; // nevermind
221 static void midiProcessPacket(MIDIPacket
*pkt
, size_t uid
)
225 pthread_mutex_lock (&gLangMutex
); //dont know if this is really needed/seems to be more stable..
226 // it is needed -jamesmcc
228 VMGlobals
*g
= gMainVMGlobals
;
230 int i
= 0; //cp : changed uint8 to int if packet->length >= 256 bug:(infinite loop)
231 while (i
< pkt
->length
) {
232 uint8 status
= pkt
->data
[i
] & 0xF0;
233 uint8 chan
= pkt
->data
[i
] & 0x0F;
234 g
->canCallOS
= false; // cannot call the OS
236 ++g
->sp
; SetObject(g
->sp
, s_midiin
->u
.classobj
); // Set the class MIDIIn
238 ++g
->sp
; SetInt(g
->sp
, uid
); //src
239 // ++g->sp; SetInt(g->sp, status); //status
240 ++g
->sp
; SetInt(g
->sp
, chan
); //chan
242 if(status
& 0x80) // set the running status for voice messages
243 gRunningStatus
= ((status
>> 4) == 0xF) ? 0 : pkt
->data
[i
]; // keep also additional info
246 case 0x80 : //noteOff
247 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
248 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
249 runInterpreter(g
, s_midiNoteOffAction
, 5);
253 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
254 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
255 runInterpreter(g
, pkt
->data
[i
+2] ? s_midiNoteOnAction
: s_midiNoteOffAction
, 5);
258 case 0xA0 : //polytouch
259 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
260 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
261 runInterpreter(g
, s_midiPolyTouchAction
, 5);
264 case 0xB0 : //control
265 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
266 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+2]); //val2
267 runInterpreter(g
, s_midiControlAction
, 5);
270 case 0xC0 : //program
271 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
272 runInterpreter(g
, s_midiProgramAction
, 4);
276 ++g
->sp
; SetInt(g
->sp
, pkt
->data
[i
+1]); //val1
277 runInterpreter(g
, s_midiTouchAction
, 4);
281 ++g
->sp
; SetInt(g
->sp
, (pkt
->data
[i
+2] << 7) | pkt
->data
[i
+1]); //val1
282 runInterpreter(g
, s_midiBendAction
, 4);
286 i
+= midiProcessSystemPacket(pkt
, chan
);
288 default : // data byte => continuing sysex message
289 if(gRunningStatus
&& !gSysexFlag
) { // modified cp: handling running status. may be we should here
290 status
= gRunningStatus
& 0xF0; // accept running status only inside a packet beginning
291 chan
= gRunningStatus
& 0x0F; // with a valid status byte ?
294 goto L
; // parse again with running status set
297 i
+= midiProcessSystemPacket(pkt
, chan
);
301 g
->canCallOS
= false;
303 pthread_mutex_unlock (&gLangMutex
);
307 static void midiReadProc(const MIDIPacketList
*pktlist
, void* readProcRefCon
, void* srcConnRefCon
)
309 MIDIPacket
*pkt
= (MIDIPacket
*)pktlist
->packet
;
310 size_t uid
= (size_t) srcConnRefCon
;
311 for (uint32 i
=0; i
<pktlist
->numPackets
; ++i
) {
312 midiProcessPacket(pkt
, uid
);
313 pkt
= MIDIPacketNext(pkt
);
319 int initMIDI(int numIn
, int numOut
)
322 numIn
= sc_clip(numIn
, 1, kMaxMidiPorts
);
323 numOut
= sc_clip(numOut
, 1, kMaxMidiPorts
);
325 int enc
= kCFStringEncodingMacRoman
;
326 CFAllocatorRef alloc
= CFAllocatorGetDefault();
328 CFStringRef clientName
= CFStringCreateWithCString(alloc
, "SuperCollider", enc
);
330 OSStatus err
= MIDIClientCreate(clientName
, midiNotifyProc
, nil
, &gMIDIClient
);
332 post("Could not create MIDI client. error %d\n", err
);
335 CFRelease(clientName
);
337 for (int i
=0; i
<numIn
; ++i
) {
339 sprintf(str
, "in%d\n", i
);
340 CFStringRef inputPortName
= CFStringCreateWithCString(alloc
, str
, enc
);
342 err
= MIDIInputPortCreate(gMIDIClient
, inputPortName
, midiReadProc
, &i
, gMIDIInPort
+i
);
345 post("Could not create MIDI port %s. error %d\n", str
, err
);
348 CFRelease(inputPortName
);
351 /*int n = MIDIGetNumberOfSources();
352 printf("%d sources\n", n);
353 for (i = 0; i < n; ++i) {
354 MIDIEndpointRef src = MIDIGetSource(i);
355 MIDIPortConnectSource(inPort, src, NULL);
358 gNumMIDIInPorts
= numIn
;
360 for (int i
=0; i
<numOut
; ++i
) {
362 sprintf(str
, "out%d\n", i
);
363 CFStringRef outputPortName
= CFStringCreateWithCString(alloc
, str
, enc
);
365 err
= MIDIOutputPortCreate(gMIDIClient
, outputPortName
, gMIDIOutPort
+i
);
367 gNumMIDIOutPorts
= i
;
368 post("Could not create MIDI out port. error %d\n", err
);
372 CFRelease(outputPortName
);
374 gNumMIDIOutPorts
= numOut
;
381 * do not catch errors when disposing ports
382 * MIDIClientDispose should normally dispose the ports attached to it
383 * but clean up the pointers in case
386 for (i
=0; i
<gNumMIDIOutPorts
; ++i
) {
387 MIDIPortDispose(gMIDIOutPort
[i
]);
390 gNumMIDIOutPorts
= 0;
392 for (i
=0; i
<gNumMIDIInPorts
; ++i
) {
393 MIDIPortDispose(gMIDIInPort
[i
]);
399 if( MIDIClientDispose(gMIDIClient
) ) {
400 post( "Error: failed to dispose MIDIClient\n" );
409 void midiListEndpoints()
415 int prListMIDIEndpoints(struct VMGlobals
*g
, int numArgsPushed
);
416 int prListMIDIEndpoints(struct VMGlobals
*g
, int numArgsPushed
)
420 int numSrc
= MIDIGetNumberOfSources();
421 int numDst
= MIDIGetNumberOfDestinations();
423 PyrObject
* idarray
= newPyrArray(g
->gc
, 6 * sizeof(PyrObject
), 0 , true);
424 SetObject(a
, idarray
);
426 PyrObject
* idarraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(SInt32
), 0 , true);
427 SetObject(idarray
->slots
+idarray
->size
++, idarraySo
);
428 g
->gc
->GCWrite(idarray
, idarraySo
);
430 PyrObject
* devarraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(PyrObject
), 0 , true);
431 SetObject(idarray
->slots
+idarray
->size
++, devarraySo
);
432 g
->gc
->GCWrite(idarray
, devarraySo
);
434 PyrObject
* namearraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(PyrObject
), 0 , true);
435 SetObject(idarray
->slots
+idarray
->size
++, namearraySo
);
436 g
->gc
->GCWrite(idarray
, namearraySo
);
438 PyrObject
* idarrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(SInt32
), 0 , true);
439 SetObject(idarray
->slots
+idarray
->size
++, idarrayDe
);
440 g
->gc
->GCWrite(idarray
, idarrayDe
);
442 PyrObject
* namearrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(PyrObject
), 0 , true);
443 SetObject(idarray
->slots
+idarray
->size
++, namearrayDe
);
444 g
->gc
->GCWrite(idarray
, namearrayDe
);
446 PyrObject
* devarrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(PyrObject
), 0 , true);
447 SetObject(idarray
->slots
+idarray
->size
++, devarrayDe
);
448 g
->gc
->GCWrite(idarray
, devarrayDe
);
451 for (int i
=0; i
<numSrc
; ++i
) {
452 MIDIEndpointRef src
= MIDIGetSource(i
);
454 MIDIObjectGetIntegerProperty(src
, kMIDIPropertyUniqueID
, &id
);
457 error
= MIDIEndpointGetEntity(src
, &ent
);
459 CFStringRef devname
, endname
;
460 char cendname
[1024], cdevname
[1024];
462 // Virtual sources don't have entities
465 MIDIObjectGetStringProperty(src
, kMIDIPropertyName
, &devname
);
466 MIDIObjectGetStringProperty(src
, kMIDIPropertyName
, &endname
);
467 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
468 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
474 MIDIEntityGetDevice(ent
, &dev
);
475 MIDIObjectGetStringProperty(dev
, kMIDIPropertyName
, &devname
);
476 MIDIObjectGetStringProperty(src
, kMIDIPropertyName
, &endname
);
477 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
478 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
481 PyrString
*string
= newPyrString(g
->gc
, cendname
, 0, true);
482 SetObject(namearraySo
->slots
+i
, string
);
484 g
->gc
->GCWrite(namearraySo
, (PyrObject
*)string
);
486 PyrString
*devstring
= newPyrString(g
->gc
, cdevname
, 0, true);
487 SetObject(devarraySo
->slots
+i
, devstring
);
489 g
->gc
->GCWrite(devarraySo
, (PyrObject
*)devstring
);
491 SetInt(idarraySo
->slots
+i
, id
);
500 // post("numDst %d\n", numDst);
501 for (int i
=0; i
<numDst
; ++i
) {
502 MIDIEndpointRef dst
= MIDIGetDestination(i
);
504 MIDIObjectGetIntegerProperty(dst
, kMIDIPropertyUniqueID
, &id
);
507 error
= MIDIEndpointGetEntity(dst
, &ent
);
509 CFStringRef devname
, endname
;
510 char cendname
[1024], cdevname
[1024];
512 // Virtual destinations don't have entities either
515 MIDIObjectGetStringProperty(dst
, kMIDIPropertyName
, &devname
);
516 MIDIObjectGetStringProperty(dst
, kMIDIPropertyName
, &endname
);
517 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
518 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
525 MIDIEntityGetDevice(ent
, &dev
);
526 MIDIObjectGetStringProperty(dev
, kMIDIPropertyName
, &devname
);
527 MIDIObjectGetStringProperty(dst
, kMIDIPropertyName
, &endname
);
528 CFStringGetCString(devname
, cdevname
, 1024, kCFStringEncodingUTF8
);
529 CFStringGetCString(endname
, cendname
, 1024, kCFStringEncodingUTF8
);
532 PyrString
*string
= newPyrString(g
->gc
, cendname
, 0, true);
533 SetObject(namearrayDe
->slots
+namearrayDe
->size
++, string
);
534 g
->gc
->GCWrite(namearrayDe
, (PyrObject
*)string
);
536 PyrString
*devstring
= newPyrString(g
->gc
, cdevname
, 0, true);
538 SetObject(devarrayDe
->slots
+devarrayDe
->size
++, devstring
);
539 g
->gc
->GCWrite(devarrayDe
, (PyrObject
*)devstring
);
541 SetInt(idarrayDe
->slots
+idarrayDe
->size
++, id
);
552 int prConnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
);
553 int prConnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
)
555 //PyrSlot *a = g->sp - 2;
556 PyrSlot
*b
= g
->sp
- 1;
559 int err
, inputIndex
, uid
;
560 err
= slotIntVal(b
, &inputIndex
);
561 if (err
) return errWrongType
;
562 if (inputIndex
< 0 || inputIndex
>= gNumMIDIInPorts
) return errIndexOutOfRange
;
564 err
= slotIntVal(c
, &uid
);
565 if (err
) return errWrongType
;
568 MIDIEndpointRef src
=0;
569 MIDIObjectType mtype
;
570 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&src
, &mtype
);
571 if (mtype
!= kMIDIObjectType_Source
) return errFailed
;
573 //pass the uid to the midiReadProc to identify the src
575 MIDIPortConnectSource(gMIDIInPort
[inputIndex
], src
, (void*)uid
);
579 int prDisconnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
);
580 int prDisconnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
)
582 PyrSlot
*b
= g
->sp
- 1;
585 int err
, inputIndex
, uid
;
586 err
= slotIntVal(b
, &inputIndex
);
588 if (inputIndex
< 0 || inputIndex
>= gNumMIDIInPorts
) return errIndexOutOfRange
;
589 err
= slotIntVal(c
, &uid
);
592 MIDIEndpointRef src
=0;
593 MIDIObjectType mtype
;
594 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&src
, &mtype
);
595 if (mtype
!= kMIDIObjectType_Source
) return errFailed
;
597 MIDIPortDisconnectSource(gMIDIInPort
[inputIndex
], src
);
602 int prInitMIDI(struct VMGlobals
*g
, int numArgsPushed
);
603 int prInitMIDI(struct VMGlobals
*g
, int numArgsPushed
)
605 //PyrSlot *a = g->sp - 2;
606 PyrSlot
*b
= g
->sp
- 1;
609 int err
, numIn
, numOut
;
610 err
= slotIntVal(b
, &numIn
);
611 if (err
) return errWrongType
;
613 err
= slotIntVal(c
, &numOut
);
614 if (err
) return errWrongType
;
616 return initMIDI(numIn
, numOut
);
618 int prDisposeMIDIClient(VMGlobals
*g
, int numArgsPushed
);
619 int prDisposeMIDIClient(VMGlobals
*g
, int numArgsPushed
)
621 return midiCleanUp();
623 int prRestartMIDI(VMGlobals
*g
, int numArgsPushed
);
624 int prRestartMIDI(VMGlobals
*g
, int numArgsPushed
)
630 void freeSysex(MIDISysexSendRequest
* pk
);
631 void freeSysex(MIDISysexSendRequest
* pk
)
637 int prSendSysex(VMGlobals
*g
, int numArgsPushed
);
638 int prSendSysex(VMGlobals
*g
, int numArgsPushed
)
642 if( !isKindOfSlot(g
->sp
, s_int8array
->u
.classobj
) )
645 PyrInt8Array
* packet
= slotRawInt8Array(g
->sp
);
648 PyrSlot
*u
= g
->sp
- 1;
649 err
= slotIntVal(u
, &uid
);
652 MIDIEndpointRef dest
;
653 MIDIObjectType mtype
;
654 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&dest
, &mtype
);
655 if (mtype
!= kMIDIObjectType_Destination
) return errFailed
;
656 if (!dest
) return errFailed
;
658 MIDISysexSendRequest
*pk
= (MIDISysexSendRequest
*) malloc (sizeof(MIDISysexSendRequest
) + size
);
659 Byte
*data
= (Byte
*)pk
+ sizeof(MIDISysexSendRequest
);
661 memcpy(data
,packet
->b
, size
);
662 pk
->complete
= false;
663 pk
-> destination
= dest
;
665 pk
-> bytesToSend
= size
;
666 pk
->completionProc
= freeSysex
;
667 pk
->completionRefCon
= 0;
669 return ((MIDISendSysex(pk
) == (OSStatus
)0) ? errNone
: errFailed
);
674 static struct mach_timebase_info
machTimebaseInfo() {
675 struct mach_timebase_info info
;
676 mach_timebase_info(&info
);
680 static MIDITimeStamp
midiTime(float latencySeconds
)
682 // add the latency expressed in seconds, to the current host time base.
683 static struct mach_timebase_info info
= machTimebaseInfo(); // cache the timebase info.
684 Float64 latencyNanos
= 1000000000 * latencySeconds
;
685 MIDITimeStamp latencyMIDI
= (latencyNanos
/ (Float64
)info
.numer
) * (Float64
)info
.denom
;
686 return (MIDITimeStamp
)mach_absolute_time() + latencyMIDI
;
691 static MIDITimeStamp
midiTime(float latencySeconds
)
693 // add the latency expressed in seconds, to the current host time base.
694 UInt64 latencyNanos
= 1000000000 * latencySeconds
; //secs to nano
695 return (MIDITimeStamp
)AudioGetCurrentHostTime() + AudioConvertNanosToHostTime(latencyNanos
);
700 void sendmidi(int port
, MIDIEndpointRef dest
, int length
, int hiStatus
, int loStatus
, int aval
, int bval
, float late
);
701 void sendmidi(int port
, MIDIEndpointRef dest
, int length
, int hiStatus
, int loStatus
, int aval
, int bval
, float late
)
703 MIDIPacketList mpktlist
;
704 MIDIPacketList
* pktlist
= &mpktlist
;
705 MIDIPacket
* pk
= MIDIPacketListInit(pktlist
);
706 ByteCount nData
= (ByteCount
) length
;
707 pk
->data
[0] = (Byte
) (hiStatus
& 0xF0) | (loStatus
& 0x0F);
708 pk
->data
[1] = (Byte
) aval
;
709 pk
->data
[2] = (Byte
) bval
;
710 pk
= MIDIPacketListAdd(pktlist
, sizeof(struct MIDIPacketList
) , pk
, midiTime(late
), nData
, pk
->data
);
711 /*OSStatus error =*/ MIDISend(gMIDIOutPort
[port
], dest
, pktlist
);
714 int prSendMIDIOut(struct VMGlobals
*g
, int numArgsPushed
);
715 int prSendMIDIOut(struct VMGlobals
*g
, int numArgsPushed
)
717 //port, uid, len, hiStatus, loStatus, a, b, latency
718 //PyrSlot *m = g->sp - 8;
719 PyrSlot
*p
= g
->sp
- 7;
721 PyrSlot
*u
= g
->sp
- 6;
722 PyrSlot
*l
= g
->sp
- 5;
724 PyrSlot
*his
= g
->sp
- 4;
725 PyrSlot
*los
= g
->sp
- 3;
727 PyrSlot
*a
= g
->sp
- 2;
728 PyrSlot
*b
= g
->sp
- 1;
729 PyrSlot
*plat
= g
->sp
;
732 int err
, outputIndex
, uid
, length
, hiStatus
, loStatus
, aval
, bval
;
734 err
= slotIntVal(p
, &outputIndex
);
736 if (outputIndex
< 0 || outputIndex
>= gNumMIDIInPorts
) return errIndexOutOfRange
;
738 err
= slotIntVal(u
, &uid
);
740 err
= slotIntVal(l
, &length
);
742 err
= slotIntVal(his
, &hiStatus
);
744 err
= slotIntVal(los
, &loStatus
);
746 err
= slotIntVal(a
, &aval
);
748 err
= slotIntVal(b
, &bval
);
750 err
= slotFloatVal(plat
, &late
);
753 MIDIEndpointRef dest
;
754 MIDIObjectType mtype
;
755 MIDIObjectFindByUniqueID(uid
, (MIDIObjectRef
*)&dest
, &mtype
);
756 if (mtype
!= kMIDIObjectType_Destination
) return errFailed
;
758 if (!dest
) return errFailed
;
760 sendmidi(outputIndex
, dest
, length
, hiStatus
, loStatus
, aval
, bval
, late
);
764 // not needed in CoreMIDI:
769 int prInitMIDIClient(struct VMGlobals
*g
, int numArgsPushed
);
770 int prInitMIDIClient(struct VMGlobals
*g
, int numArgsPushed
)
772 return initMIDIClient();
776 void initMIDIPrimitives()
780 base
= nextPrimitiveIndex();
782 gSysexData
.reserve(1024);
784 s_midiin
= getsym("MIDIIn");
785 s_domidiaction
= getsym("doAction");
786 s_midiNoteOnAction
= getsym("doNoteOnAction");
787 s_midiNoteOffAction
= getsym("doNoteOffAction");
788 s_midiTouchAction
= getsym("doTouchAction");
789 s_midiControlAction
= getsym("doControlAction");
790 s_midiPolyTouchAction
= getsym("doPolyTouchAction");
791 s_midiProgramAction
= getsym("doProgramAction");
792 s_midiBendAction
= getsym("doBendAction");
793 s_midiSysexAction
= getsym("doSysexAction");
794 s_midiInvalidSysexAction
= getsym("doInvalidSysexAction"); // client can handle incorrect case
795 s_midiSysrtAction
= getsym("doSysrtAction");
796 s_midiSMPTEAction
= getsym("doSMPTEaction");
797 s_numMIDIDev
= getsym("prSetNumberOfDevices");
798 s_midiclient
= getsym("MIDIClient");
800 definePrimitive(base
, index
++, "_ListMIDIEndpoints", prListMIDIEndpoints
, 1, 0);
801 definePrimitive(base
, index
++, "_InitMIDI", prInitMIDI
, 3, 0);
802 definePrimitive(base
, index
++, "_InitMIDIClient", prInitMIDIClient
, 1, 0);
803 definePrimitive(base
, index
++, "_ConnectMIDIIn", prConnectMIDIIn
, 3, 0);
804 definePrimitive(base
, index
++, "_DisconnectMIDIIn", prDisconnectMIDIIn
, 3, 0);
805 definePrimitive(base
, index
++, "_DisposeMIDIClient", prDisposeMIDIClient
, 1, 0);
806 definePrimitive(base
, index
++, "_RestartMIDI", prRestartMIDI
, 1, 0);
807 definePrimitive(base
, index
++, "_SendMIDIOut", prSendMIDIOut
, 9, 0);
808 definePrimitive(base
, index
++, "_SendSysex", prSendSysex
, 3, 0);
809 if(gMIDIClient
) midiCleanUp();