Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / lang / LangPrimSource / SC_CoreMIDI.cpp
blob9431ac3fbb3499c54ba14155a3133dd4c99409a3
1 /*
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
27 added prRestartMIDI
28 19/9 call different actions,disconnect midiInPort, midiout: sendmidi
29 04/feb/03 prListMIDIEndpoints modification by Ron Kuivila added jt.
31 #if SC_IPHONE
32 #include <mach/mach_time.h>
33 #else
34 #include <CoreAudio/HostTime.h>
35 #include <Carbon/Carbon.h>
36 #endif
37 #include <CoreMIDI/CoreMIDI.h>
38 #include <vector>
39 #include "SCBase.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"
51 #include "PyrSched.h"
52 #include "GC.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;
66 //jt
67 PyrSymbol * s_midiin;
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;
75 //cp
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;
86 #if 0
87 static void dumpSysexData() {
88 if(gSysexData.size() <= 0)
89 return;
90 std::vector<Byte>::const_iterator iter = gSysexData.begin(), end = gSysexData.end();
91 int i=0;
92 while(iter != end) {
93 if((i % 16) == 0 && (i > 0))
94 printf("\n");
95 ++i;
96 printf("%02X ", *iter++);
98 printf("\n");
99 printf("sysex data dump size: %i bytes.\n", gSysexData.size());
101 #endif
103 static void sysexBegin() {
104 gRunningStatus = 0; // clear running status
105 gSysexData.clear();
106 gSysexFlag = true;
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
114 ++g->sp;
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) {
124 gSysexFlag = false;
125 scCallSysexAction(s_midiSysexAction, lastUID);
128 static void sysexEndInvalid() {
129 gSysexFlag = false;
130 scCallSysexAction(s_midiInvalidSysexAction, 0);
133 static int midiProcessSystemPacket(MIDIPacket *pkt, int chan) {
134 int index, data;
135 VMGlobals *g = gMainVMGlobals;
136 switch (chan) {
137 case 7: // added cp: Sysex EOX must be taken into account if first on data packet
138 case 0:
140 int last_uid = 0;
141 int m = pkt->length;
142 Byte* p_pkt = pkt->data;
143 Byte pktval;
145 while(m--) {
146 pktval = *p_pkt++;
147 if(pktval & 0x80) { // status byte
148 if(pktval == 0xF7) { // end packet
149 gSysexData.push_back(pktval); // add EOX
150 if(gSysexFlag)
151 sysexEnd(last_uid); // if last_uid != 0 rebuild the VM.
152 else
153 sysexEndInvalid(); // invalid 1 byte with only EOX can happen
154 break;
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 !");
162 last_uid = -1;
164 sysexEndInvalid();
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
173 break;
176 else if(gSysexFlag)
177 gSysexData.push_back(pktval); // add Byte
178 else // garbage - handle in case - discard it
179 break;
181 return (pkt->length-m);
183 break;
185 case 1 :
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 );
192 return 2;
194 case 2 : //songptr
195 ++g->sp; SetInt(g->sp, (pkt->data[2] << 7) | pkt->data[1]); //val1
196 runInterpreter(g, s_midiSysrtAction, 4);
197 return 3;
199 case 3 : // song select
200 ++g->sp; SetInt(g->sp, pkt->data[1]); //val1
201 runInterpreter(g, s_midiSysrtAction, 4);
202 return 2;
204 case 8 : //clock
205 case 10: //start
206 case 11: //continue
207 case 12: //stop
208 case 15: //reset
209 gRunningStatus = 0; // clear running status
210 runInterpreter(g, s_midiSysrtAction, 3);
211 return 1;
213 default:
214 g->sp -= 3; // nevermind
215 break;
218 return (1);
221 static void midiProcessPacket(MIDIPacket *pkt, size_t uid)
223 //jt
224 if(pkt) {
225 pthread_mutex_lock (&gLangMutex); //dont know if this is really needed/seems to be more stable..
226 // it is needed -jamesmcc
227 if (compiledOK) {
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
237 //set arguments:
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
245 switch (status) {
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);
250 i += 3;
251 break;
252 case 0x90 : //noteOn
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);
256 i += 3;
257 break;
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);
262 i += 3;
263 break;
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);
268 i += 3;
269 break;
270 case 0xC0 : //program
271 ++g->sp; SetInt(g->sp, pkt->data[i+1]); //val1
272 runInterpreter(g, s_midiProgramAction, 4);
273 i += 2;
274 break;
275 case 0xD0 : //touch
276 ++g->sp; SetInt(g->sp, pkt->data[i+1]); //val1
277 runInterpreter(g, s_midiTouchAction, 4);
278 i += 2;
279 break;
280 case 0xE0 : //bend
281 ++g->sp; SetInt(g->sp, (pkt->data[i+2] << 7) | pkt->data[i+1]); //val1
282 runInterpreter(g, s_midiBendAction, 4);
283 i += 3;
284 break;
285 case 0xF0 :
286 i += midiProcessSystemPacket(pkt, chan);
287 break;
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 ?
292 SetInt(g->sp, chan);
293 --i;
294 goto L; // parse again with running status set
296 chan = 0;
297 i += midiProcessSystemPacket(pkt, chan);
298 break;
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);
317 int midiCleanUp();
319 int initMIDI(int numIn, int numOut)
321 midiCleanUp();
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);
331 if (err) {
332 post("Could not create MIDI client. error %d\n", err);
333 return errFailed;
335 CFRelease(clientName);
337 for (int i=0; i<numIn; ++i) {
338 char str[32];
339 sprintf(str, "in%d\n", i);
340 CFStringRef inputPortName = CFStringCreateWithCString(alloc, str, enc);
342 err = MIDIInputPortCreate(gMIDIClient, inputPortName, midiReadProc, &i, gMIDIInPort+i);
343 if (err) {
344 gNumMIDIInPorts = i;
345 post("Could not create MIDI port %s. error %d\n", str, err);
346 return errFailed;
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) {
361 char str[32];
362 sprintf(str, "out%d\n", i);
363 CFStringRef outputPortName = CFStringCreateWithCString(alloc, str, enc);
365 err = MIDIOutputPortCreate(gMIDIClient, outputPortName, gMIDIOutPort+i);
366 if (err) {
367 gNumMIDIOutPorts = i;
368 post("Could not create MIDI out port. error %d\n", err);
369 return errFailed;
372 CFRelease(outputPortName);
374 gNumMIDIOutPorts = numOut;
375 return errNone;
378 int midiCleanUp()
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
385 int i = 0;
386 for (i=0; i<gNumMIDIOutPorts; ++i) {
387 MIDIPortDispose(gMIDIOutPort[i]);
388 gMIDIOutPort[i] = 0;
390 gNumMIDIOutPorts = 0;
392 for (i=0; i<gNumMIDIInPorts; ++i) {
393 MIDIPortDispose(gMIDIInPort[i]);
394 gMIDIInPort[i] = 0;
396 gNumMIDIInPorts = 0;
398 if (gMIDIClient) {
399 if( MIDIClientDispose(gMIDIClient) ) {
400 post( "Error: failed to dispose MIDIClient\n" );
401 return errFailed;
403 gMIDIClient = 0;
405 return errNone;
409 void midiListEndpoints()
415 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed);
416 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed)
418 OSStatus error;
419 PyrSlot *a = g->sp;
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);
453 SInt32 id;
454 MIDIObjectGetIntegerProperty(src, kMIDIPropertyUniqueID, &id);
456 MIDIEntityRef ent;
457 error = MIDIEndpointGetEntity(src, &ent);
459 CFStringRef devname, endname;
460 char cendname[1024], cdevname[1024];
462 // Virtual sources don't have entities
463 if(error)
465 MIDIObjectGetStringProperty(src, kMIDIPropertyName, &devname);
466 MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endname);
467 CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
468 CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
470 else
472 MIDIDeviceRef dev;
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);
483 namearraySo->size++;
484 g->gc->GCWrite(namearraySo, (PyrObject*)string);
486 PyrString *devstring = newPyrString(g->gc, cdevname, 0, true);
487 SetObject(devarraySo->slots+i, devstring);
488 devarraySo->size++;
489 g->gc->GCWrite(devarraySo, (PyrObject*)devstring);
491 SetInt(idarraySo->slots+i, id);
492 idarraySo->size++;
494 CFRelease(devname);
495 CFRelease(endname);
500 // post("numDst %d\n", numDst);
501 for (int i=0; i<numDst; ++i) {
502 MIDIEndpointRef dst = MIDIGetDestination(i);
503 SInt32 id;
504 MIDIObjectGetIntegerProperty(dst, kMIDIPropertyUniqueID, &id);
506 MIDIEntityRef ent;
507 error = MIDIEndpointGetEntity(dst, &ent);
509 CFStringRef devname, endname;
510 char cendname[1024], cdevname[1024];
512 // Virtual destinations don't have entities either
513 if(error)
515 MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &devname);
516 MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &endname);
517 CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
518 CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
521 else
523 MIDIDeviceRef dev;
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);
543 CFRelease(devname);
544 CFRelease(endname);
547 return errNone;
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;
557 PyrSlot *c = g->sp;
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);
577 return errNone;
579 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
580 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
582 PyrSlot *b = g->sp - 1;
583 PyrSlot *c = g->sp;
585 int err, inputIndex, uid;
586 err = slotIntVal(b, &inputIndex);
587 if (err) return err;
588 if (inputIndex < 0 || inputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
589 err = slotIntVal(c, &uid);
590 if (err) return err;
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);
599 return errNone;
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;
607 PyrSlot *c = g->sp;
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)
626 MIDIRestart();
627 return errNone;
630 void freeSysex(MIDISysexSendRequest* pk);
631 void freeSysex(MIDISysexSendRequest* pk)
633 free(pk);
637 int prSendSysex(VMGlobals *g, int numArgsPushed);
638 int prSendSysex(VMGlobals *g, int numArgsPushed)
640 int err, uid, size;
642 if( !isKindOfSlot(g->sp, s_int8array->u.classobj) )
643 return errWrongType;
645 PyrInt8Array* packet = slotRawInt8Array(g->sp);
646 size = packet->size;
648 PyrSlot *u = g->sp - 1;
649 err = slotIntVal(u, &uid);
650 if (err) return err;
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;
664 pk -> data = data;
665 pk -> bytesToSend = size;
666 pk->completionProc = freeSysex;
667 pk->completionRefCon = 0;
669 return ((MIDISendSysex(pk) == (OSStatus)0) ? errNone : errFailed);
672 #if SC_IPHONE
674 static struct mach_timebase_info machTimebaseInfo() {
675 struct mach_timebase_info info;
676 mach_timebase_info(&info);
677 return 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;
689 #else
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);
698 #endif
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;
733 float late;
734 err = slotIntVal(p, &outputIndex);
735 if (err) return err;
736 if (outputIndex < 0 || outputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
738 err = slotIntVal(u, &uid);
739 if (err) return err;
740 err = slotIntVal(l, &length);
741 if (err) return err;
742 err = slotIntVal(his, &hiStatus);
743 if (err) return err;
744 err = slotIntVal(los, &loStatus);
745 if (err) return err;
746 err = slotIntVal(a, &aval);
747 if (err) return err;
748 err = slotIntVal(b, &bval);
749 if (err) return err;
750 err = slotFloatVal(plat, &late);
751 if (err) return err;
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);
761 return errNone;
764 // not needed in CoreMIDI:
765 int initMIDIClient()
767 return errNone;
769 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed);
770 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed)
772 return initMIDIClient();
774 //--------------
776 void initMIDIPrimitives()
778 int base, index;
780 base = nextPrimitiveIndex();
781 index = 0;
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();