osx cmake: use CMAKE_INSTALL_PREFIX, which defaults to <build dir>/Install
[supercollider.git] / lang / LangPrimSource / SC_CoreMIDI.cpp
blobea9d006e521764433b6d103a681e67c3d92b576a
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 #include <CoreAudio/HostTime.h>
32 #include <Carbon/Carbon.h>
33 #include <CoreMIDI/CoreMIDI.h>
34 #include <vector>
35 #include "SCBase.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"
47 #include "PyrSched.h"
48 #include "GC.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;
62 //jt
63 PyrSymbol * s_midiin;
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;
71 //cp
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;
82 #if 0
83 static void dumpSysexData() {
84 if(gSysexData.size() <= 0)
85 return;
86 std::vector<Byte>::const_iterator iter = gSysexData.begin(), end = gSysexData.end();
87 int i=0;
88 while(iter != end) {
89 if((i % 16) == 0 && (i > 0))
90 printf("\n");
91 ++i;
92 printf("%02X ", *iter++);
94 printf("\n");
95 printf("sysex data dump size: %i bytes.\n", gSysexData.size());
97 #endif
99 static void sysexBegin() {
100 gRunningStatus = 0; // clear running status
101 gSysexData.clear();
102 gSysexFlag = true;
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
110 ++g->sp;
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) {
120 gSysexFlag = false;
121 scCallSysexAction(s_midiSysexAction, lastUID);
124 static void sysexEndInvalid() {
125 gSysexFlag = false;
126 scCallSysexAction(s_midiInvalidSysexAction, 0);
129 static int midiProcessSystemPacket(MIDIPacket *pkt, int chan) {
130 int index, data;
131 VMGlobals *g = gMainVMGlobals;
132 switch (chan) {
133 case 7: // added cp: Sysex EOX must be taken into account if first on data packet
134 case 0:
136 int last_uid = 0;
137 int m = pkt->length;
138 Byte* p_pkt = pkt->data;
139 Byte pktval;
141 while(m--) {
142 pktval = *p_pkt++;
143 if(pktval & 0x80) { // status byte
144 if(pktval == 0xF7) { // end packet
145 gSysexData.push_back(pktval); // add EOX
146 if(gSysexFlag)
147 sysexEnd(last_uid); // if last_uid != 0 rebuild the VM.
148 else
149 sysexEndInvalid(); // invalid 1 byte with only EOX can happen
150 break;
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 !");
158 last_uid = -1;
160 sysexEndInvalid();
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
169 break;
172 else if(gSysexFlag)
173 gSysexData.push_back(pktval); // add Byte
174 else // garbage - handle in case - discard it
175 break;
177 return (pkt->length-m);
179 break;
181 case 1 :
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 );
188 return 2;
190 case 2 : //songptr
191 ++g->sp; SetInt(g->sp, (pkt->data[2] << 7) | pkt->data[1]); //val1
192 runInterpreter(g, s_midiSysrtAction, 4);
193 return 3;
195 case 3 : // song select
196 ++g->sp; SetInt(g->sp, pkt->data[1]); //val1
197 runInterpreter(g, s_midiSysrtAction, 4);
198 return 2;
200 case 8 : //clock
201 case 10: //start
202 case 11: //continue
203 case 12: //stop
204 case 15: //reset
205 gRunningStatus = 0; // clear running status
206 runInterpreter(g, s_midiSysrtAction, 3);
207 return 1;
209 default:
210 g->sp -= 3; // nevermind
211 break;
214 return (1);
217 static void midiProcessPacket(MIDIPacket *pkt, size_t uid)
219 //jt
220 if(pkt) {
221 pthread_mutex_lock (&gLangMutex); //dont know if this is really needed/seems to be more stable..
222 // it is needed -jamesmcc
223 if (compiledOK) {
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
233 //set arguments:
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
241 switch (status) {
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);
246 i += 3;
247 break;
248 case 0x90 : //noteOn
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);
252 i += 3;
253 break;
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);
258 i += 3;
259 break;
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);
264 i += 3;
265 break;
266 case 0xC0 : //program
267 ++g->sp; SetInt(g->sp, pkt->data[i+1]); //val1
268 runInterpreter(g, s_midiProgramAction, 4);
269 i += 2;
270 break;
271 case 0xD0 : //touch
272 ++g->sp; SetInt(g->sp, pkt->data[i+1]); //val1
273 runInterpreter(g, s_midiTouchAction, 4);
274 i += 2;
275 break;
276 case 0xE0 : //bend
277 ++g->sp; SetInt(g->sp, (pkt->data[i+2] << 7) | pkt->data[i+1]); //val1
278 runInterpreter(g, s_midiBendAction, 4);
279 i += 3;
280 break;
281 case 0xF0 :
282 i += midiProcessSystemPacket(pkt, chan);
283 break;
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 ?
288 SetInt(g->sp, chan);
289 --i;
290 goto L; // parse again with running status set
292 chan = 0;
293 i += midiProcessSystemPacket(pkt, chan);
294 break;
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);
313 int midiCleanUp();
315 int initMIDI(int numIn, int numOut)
317 midiCleanUp();
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);
327 if (err) {
328 post("Could not create MIDI client. error %d\n", err);
329 return errFailed;
331 CFRelease(clientName);
333 for (int i=0; i<numIn; ++i) {
334 char str[32];
335 sprintf(str, "in%d\n", i);
336 CFStringRef inputPortName = CFStringCreateWithCString(alloc, str, enc);
338 err = MIDIInputPortCreate(gMIDIClient, inputPortName, midiReadProc, &i, gMIDIInPort+i);
339 if (err) {
340 gNumMIDIInPorts = i;
341 post("Could not create MIDI port %s. error %d\n", str, err);
342 return errFailed;
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) {
357 char str[32];
358 sprintf(str, "out%d\n", i);
359 CFStringRef outputPortName = CFStringCreateWithCString(alloc, str, enc);
361 err = MIDIOutputPortCreate(gMIDIClient, outputPortName, gMIDIOutPort+i);
362 if (err) {
363 gNumMIDIOutPorts = i;
364 post("Could not create MIDI out port. error %d\n", err);
365 return errFailed;
368 CFRelease(outputPortName);
370 gNumMIDIOutPorts = numOut;
371 return errNone;
374 int midiCleanUp()
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
381 int i = 0;
382 for (i=0; i<gNumMIDIOutPorts; ++i) {
383 MIDIPortDispose(gMIDIOutPort[i]);
384 gMIDIOutPort[i] = 0;
386 gNumMIDIOutPorts = 0;
388 for (i=0; i<gNumMIDIInPorts; ++i) {
389 MIDIPortDispose(gMIDIInPort[i]);
390 gMIDIInPort[i] = 0;
392 gNumMIDIInPorts = 0;
394 if (gMIDIClient) {
395 if( MIDIClientDispose(gMIDIClient) ) {
396 post( "Error: failed to dispose MIDIClient\n" );
397 return errFailed;
399 gMIDIClient = 0;
401 return errNone;
405 void midiListEndpoints()
411 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed);
412 int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed)
414 OSStatus error;
415 PyrSlot *a = g->sp;
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);
449 SInt32 id;
450 MIDIObjectGetIntegerProperty(src, kMIDIPropertyUniqueID, &id);
452 MIDIEntityRef ent;
453 error = MIDIEndpointGetEntity(src, &ent);
455 CFStringRef devname, endname;
456 char cendname[1024], cdevname[1024];
458 // Virtual sources don't have entities
459 if(error)
461 MIDIObjectGetStringProperty(src, kMIDIPropertyName, &devname);
462 MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endname);
463 CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
464 CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
466 else
468 MIDIDeviceRef dev;
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);
479 namearraySo->size++;
480 g->gc->GCWrite(namearraySo, (PyrObject*)string);
482 PyrString *devstring = newPyrString(g->gc, cdevname, 0, true);
483 SetObject(devarraySo->slots+i, devstring);
484 devarraySo->size++;
485 g->gc->GCWrite(devarraySo, (PyrObject*)devstring);
487 SetInt(idarraySo->slots+i, id);
488 idarraySo->size++;
490 CFRelease(devname);
491 CFRelease(endname);
496 // post("numDst %d\n", numDst);
497 for (int i=0; i<numDst; ++i) {
498 MIDIEndpointRef dst = MIDIGetDestination(i);
499 SInt32 id;
500 MIDIObjectGetIntegerProperty(dst, kMIDIPropertyUniqueID, &id);
502 MIDIEntityRef ent;
503 error = MIDIEndpointGetEntity(dst, &ent);
505 CFStringRef devname, endname;
506 char cendname[1024], cdevname[1024];
508 // Virtual destinations don't have entities either
509 if(error)
511 MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &devname);
512 MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &endname);
513 CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
514 CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
517 else
519 MIDIDeviceRef dev;
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);
539 CFRelease(devname);
540 CFRelease(endname);
543 return errNone;
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;
553 PyrSlot *c = g->sp;
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);
573 return errNone;
575 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
576 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
578 PyrSlot *b = g->sp - 1;
579 PyrSlot *c = g->sp;
581 int err, inputIndex, uid;
582 err = slotIntVal(b, &inputIndex);
583 if (err) return err;
584 if (inputIndex < 0 || inputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
585 err = slotIntVal(c, &uid);
586 if (err) return err;
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);
595 return errNone;
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;
603 PyrSlot *c = g->sp;
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)
622 MIDIRestart();
623 return errNone;
626 void freeSysex(MIDISysexSendRequest* pk);
627 void freeSysex(MIDISysexSendRequest* pk)
629 free(pk);
633 int prSendSysex(VMGlobals *g, int numArgsPushed);
634 int prSendSysex(VMGlobals *g, int numArgsPushed)
636 int err, uid, size;
638 if( !isKindOfSlot(g->sp, s_int8array->u.classobj) )
639 return errWrongType;
641 PyrInt8Array* packet = slotRawInt8Array(g->sp);
642 size = packet->size;
644 PyrSlot *u = g->sp - 1;
645 err = slotIntVal(u, &uid);
646 if (err) return err;
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;
660 pk -> data = data;
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;
704 float late;
705 err = slotIntVal(p, &outputIndex);
706 if (err) return err;
707 if (outputIndex < 0 || outputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
709 err = slotIntVal(u, &uid);
710 if (err) return err;
711 err = slotIntVal(l, &length);
712 if (err) return err;
713 err = slotIntVal(his, &hiStatus);
714 if (err) return err;
715 err = slotIntVal(los, &loStatus);
716 if (err) return err;
717 err = slotIntVal(a, &aval);
718 if (err) return err;
719 err = slotIntVal(b, &bval);
720 if (err) return err;
721 err = slotFloatVal(plat, &late);
722 if (err) return err;
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);
732 return errNone;
735 // not needed in CoreMIDI:
736 int initMIDIClient()
738 return errNone;
740 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed);
741 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed)
743 return initMIDIClient();
745 //--------------
747 void initMIDIPrimitives()
749 int base, index;
751 base = nextPrimitiveIndex();
752 index = 0;
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();