libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

cmMidiOsx.c 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. #include <Carbon/Carbon.h>
  2. #include <CoreAudio/CoreAudio.h>
  3. #include <CoreMIDI/CoreMIDI.h>
  4. #include <mach/mach_time.h>
  5. #include "cmPrefix.h"
  6. #include "cmGlobal.h"
  7. #include "cmRpt.h"
  8. #include "cmErr.h"
  9. #include "cmCtx.h"
  10. #include "cmMem.h"
  11. #include "cmMallocDebug.h"
  12. #include "cmMidi.h"
  13. #include "cmMidiPort.h"
  14. typedef struct
  15. {
  16. bool inputFl;
  17. char* nameStr;
  18. SInt32 uid;
  19. MIDIEndpointRef epr;
  20. cmMpParserH_t parserH;
  21. double prevMicroSecs;
  22. } cmMpPort;
  23. typedef struct
  24. {
  25. char* nameStr;
  26. unsigned iPortCnt;
  27. cmMpPort* iPortArray;
  28. unsigned oPortCnt;
  29. cmMpPort* oPortArray;
  30. } cmMpDev;
  31. typedef struct
  32. {
  33. cmErr_t err;
  34. unsigned devCnt;
  35. cmMpDev* devArray;
  36. cmMpCallback_t cbFunc;
  37. void* cbDataPtr;
  38. MIDIClientRef clientRef;
  39. MIDIPortRef inPortRef;
  40. MIDIPortRef outPortRef;
  41. mach_timebase_info_data_t timeBaseInfo;
  42. } cmMpRoot;
  43. cmMpRoot _cmMpRoot;
  44. cmMpRoot* _cmMpRtPtr = NULL;
  45. cmMpRC_t _cmMpError( cmErr_t* errPtr, cmMpRC_t rc, OSStatus err, const char* fmt, ... )
  46. {
  47. va_list vl0;
  48. va_list vl1;
  49. va_start(vl0,fmt);
  50. va_copy(vl1,vl0);
  51. unsigned n0 = vsnprintf(NULL,0,fmt,vl0);
  52. unsigned n1 = 0;
  53. unsigned n2 = 0;
  54. const char* fmt2 = "OS Status:%i";
  55. va_end(vl0);
  56. if( err != noErr )
  57. n1 = snprintf(NULL,0,fmt2,err);
  58. unsigned bufCharCnt = n0 + n1;
  59. char buf[ bufCharCnt + 1];
  60. vsnprintf(buf,n0,fmt,vl1);
  61. n2 = strlen(buf);
  62. snprintf(buf+n2,bufCharCnt-n2,fmt2,err);
  63. buf[bufCharCnt]=0;
  64. cmErrMsg(errPtr,rc,buf);
  65. va_end(vl1);
  66. return rc;
  67. }
  68. char* _cmMpAllocStringProperty( cmErr_t* errPtr, MIDIObjectRef obj, CFStringRef propId )
  69. {
  70. OSStatus err;
  71. CFStringRef cfStrRef = NULL;
  72. char* strPtr = NULL;
  73. if(( err = MIDIObjectGetStringProperty(obj,propId,&cfStrRef)) != noErr )
  74. {
  75. _cmMpError(errPtr,kCfStringErrMpRC,err,"Get property failed.");
  76. return NULL;
  77. }
  78. unsigned charCnt = CFStringGetLength( cfStrRef ) + 1;
  79. if( charCnt > 0 )
  80. {
  81. strPtr = cmMemAllocZ( char, charCnt );
  82. if( !CFStringGetCString( cfStrRef, strPtr, charCnt, kCFStringEncodingASCII))
  83. {
  84. _cmMpError(errPtr,kCfStringErrMpRC,noErr,"CFstring to cstring failed.");
  85. cmMemFree(strPtr);
  86. strPtr = NULL;
  87. }
  88. }
  89. CFRelease(cfStrRef);
  90. return strPtr;
  91. }
  92. void _cmMpDeviceInit( cmMpDev* drp )
  93. {
  94. drp->nameStr = NULL;
  95. drp->iPortCnt = 0;
  96. drp->oPortCnt = 0;
  97. drp->iPortArray = NULL;
  98. drp->oPortArray = NULL;
  99. }
  100. void _cmMpDeviceFree( cmMpDev* drp )
  101. {
  102. unsigned i;
  103. for(i=0; i<drp->iPortCnt; ++i)
  104. {
  105. cmMemPtrFree(&drp->iPortArray[i].nameStr);
  106. cmMpParserDestroy( &drp->iPortArray[i].parserH );
  107. }
  108. for(i=0; i<drp->oPortCnt; ++i)
  109. cmMemPtrFree(&drp->oPortArray[i].nameStr);
  110. cmMemPtrFree(&drp->iPortArray);
  111. cmMemPtrFree(&drp->oPortArray);
  112. cmMemPtrFree(&drp->nameStr);
  113. _cmMpDeviceInit(drp);
  114. }
  115. void _cmMpDevicePrint( cmMpDev* drp, unsigned devIdx, cmRpt_t* rpt )
  116. {
  117. unsigned i,j;
  118. cmRptPrintf(rpt,"%i '%s'\n",devIdx,drp->nameStr);
  119. for(j=0; j<2; ++j)
  120. {
  121. unsigned portCnt = j==0 ? drp->iPortCnt : drp->oPortCnt;
  122. for(i=0; i<portCnt; ++i)
  123. {
  124. const cmMpPort* p = j==0 ? drp->iPortArray + i : drp->oPortArray + i ;
  125. cmRptPrintf(rpt," port:%s %i id:0x:%x '%s'\n", p->inputFl ? "in " : "out",i, p->uid, p->nameStr );
  126. }
  127. }
  128. }
  129. char* _cmMpFormLabel( cmErr_t* errPtr, MIDIObjectRef obj )
  130. {
  131. char* modelStr = _cmMpAllocStringProperty( errPtr, obj, kMIDIPropertyManufacturer );
  132. char* nameStr = _cmMpAllocStringProperty( errPtr, obj, kMIDIPropertyModel );
  133. char* labelStr = cmMemAllocZ( char, strlen(modelStr) + strlen(nameStr) + 4 );
  134. strcpy(labelStr,modelStr);
  135. strcat(labelStr," - ");
  136. strcat(labelStr,nameStr);
  137. cmMemFree(modelStr);
  138. cmMemFree(nameStr);
  139. return labelStr;
  140. }
  141. char* _cmMpFormPortLabel( cmErr_t* errPtr, MIDIEntityRef mer, ItemCount pi, bool inputFl )
  142. {
  143. MIDIEndpointRef epr;
  144. if( inputFl )
  145. epr = MIDIEntityGetSource( mer, pi);
  146. else
  147. epr = MIDIEntityGetDestination( mer, pi);
  148. if(epr == 0 )
  149. {
  150. _cmMpError(errPtr,kSysErrMpRC,noErr,"Get %s endpoiint ref %i failed.", inputFl ? "source" : "destination",pi);
  151. return NULL;
  152. }
  153. return _cmMpAllocStringProperty( errPtr, (MIDIObjectRef)epr, kMIDIPropertyName );
  154. }
  155. // Fill id array with the unique id of each endpoint or 0 if the endpoint is offline.
  156. cmMpRC_t _cmMpGetEntityUniqueIdArray( MIDIEntityRef mer, SInt32* idArray, unsigned idCnt, bool inputFl, ItemCount* activeCntPtr, cmErr_t* errPtr )
  157. {
  158. cmMpRC_t rc = kOkMpRC;
  159. MIDIEndpointRef epr;
  160. unsigned pi;
  161. char* dirLabel = inputFl ? "source" : "dest";
  162. SInt32 offline;
  163. OSStatus err;
  164. *activeCntPtr = 0;
  165. for(pi=0; pi<idCnt; ++pi)
  166. {
  167. epr = inputFl ? MIDIEntityGetSource( mer, pi) : MIDIEntityGetDestination( mer, pi);
  168. if(epr == 0 )
  169. {
  170. rc = _cmMpError(errPtr,kSysErrMpRC,noErr,"Get %s endpoiint ref %i failed.",dirLabel,pi);
  171. goto errLabel;
  172. }
  173. offline = 1;
  174. if((err = MIDIObjectGetIntegerProperty( epr, kMIDIPropertyOffline, &offline)) != noErr )
  175. {
  176. _cmMpError(errPtr,kSysErrMpRC,err,"Get online status on %s endpoint %i failed.",dirLabel,pi);
  177. //goto errLabel; kpl 09/05/13 - report error and fall through and report the device as 'offline'.
  178. // This should be a warning rather than a error.
  179. }
  180. if( offline )
  181. idArray[pi] = 0;
  182. else
  183. {
  184. ++*activeCntPtr;
  185. if(( err = MIDIObjectGetIntegerProperty( epr, kMIDIPropertyUniqueID, idArray + pi)) != noErr )
  186. {
  187. rc = _cmMpError(errPtr,kSysErrMpRC,err,"Get unique id on %s endpoint %i failed.",dirLabel,pi);
  188. goto errLabel;
  189. }
  190. }
  191. }
  192. errLabel:
  193. return rc;
  194. }
  195. cmMpRC_t _cmMpInitPortArray( unsigned devIdx, MIDIPortRef mpr, MIDIEntityRef mer, cmMpPort* portArray, SInt32* idArray, unsigned portCnt, bool inputFl, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, cmErr_t* errPtr )
  196. {
  197. cmMpRC_t rc = kOkMpRC;
  198. unsigned pi;
  199. for(pi=0; pi<portCnt; ++pi)
  200. {
  201. MIDIEndpointRef epr = inputFl ? MIDIEntityGetSource(mer,pi) : MIDIEntityGetDestination(mer,pi);
  202. if( epr == 0 )
  203. return _cmMpError(errPtr,kSysErrMpRC,noErr,"Get %s endpoiint ref %i failed.",inputFl ? "source" : "destination", pi);
  204. // if this is an active input port then connect it to the source port
  205. if( inputFl && (idArray[pi] != 0) )
  206. {
  207. OSStatus err = noErr;
  208. if((err = MIDIPortConnectSource( mpr, epr, portArray + pi )) != noErr )
  209. return _cmMpError(errPtr,kSysErrMpRC,err,"Source connect failed on port:%i",pi);
  210. portArray[pi].parserH = cmMpParserCreate( devIdx, pi, cbFunc, cbDataPtr, parserBufByteCnt, errPtr->rpt );
  211. }
  212. portArray[pi].uid = idArray[pi];
  213. portArray[pi].inputFl = inputFl;
  214. portArray[pi].nameStr = _cmMpFormPortLabel(errPtr, mer, pi, inputFl );
  215. portArray[pi].epr = epr;
  216. portArray[pi].prevMicroSecs = 0;
  217. }
  218. return rc;
  219. }
  220. cmMpRC_t _cmMpGetActiveEntityPortCount( MIDIEntityRef mer, bool inputFl, ItemCount* portCntPtr, ItemCount* activePortCntPtr, cmErr_t* errPtr )
  221. {
  222. *portCntPtr = inputFl ? MIDIEntityGetNumberOfSources( mer ) : MIDIEntityGetNumberOfDestinations( mer );
  223. SInt32 idArray[ *portCntPtr ];
  224. return _cmMpGetEntityUniqueIdArray( mer, idArray, *portCntPtr, inputFl, activePortCntPtr, errPtr );
  225. }
  226. cmMpRC_t _cmMpIsDeviceActive( unsigned devIdx, MIDIDeviceRef mdr, ItemCount* srcCntPtr, ItemCount* dstCntPtr, bool* activeFlPtr, cmErr_t* errPtr )
  227. {
  228. cmMpRC_t rc = kOkMpRC;
  229. ItemCount entityCnt = MIDIDeviceGetNumberOfEntities(mdr);
  230. unsigned ei;
  231. *srcCntPtr = 0;
  232. *dstCntPtr = 0;
  233. *activeFlPtr = false;
  234. for(ei=0; ei<entityCnt; ++ei)
  235. {
  236. ItemCount srcCnt = 0;
  237. ItemCount dstCnt = 0;
  238. ItemCount activeCnt = 0;
  239. MIDIEntityRef mer;
  240. if((mer = MIDIDeviceGetEntity( mdr, ei)) == 0 )
  241. return _cmMpError( errPtr,kSysErrMpRC,noErr,"Get midi device %i entity %i failed.",devIdx,ei);
  242. if((rc= _cmMpGetActiveEntityPortCount(mer,true,&srcCnt,&activeCnt,errPtr)) != kOkMpRC )
  243. return _cmMpError( errPtr, rc, noErr, "Error retrieving source port information for device:%i entity:%i",devIdx,ei);
  244. *srcCntPtr += srcCnt;
  245. if( activeCnt )
  246. *activeFlPtr = true;
  247. activeCnt = 0;
  248. if((rc= _cmMpGetActiveEntityPortCount(mer,false,&dstCnt,&activeCnt,errPtr)) != kOkMpRC )
  249. return _cmMpError( errPtr, rc, noErr, "Error retrieving destination port information for device:%i entity:%i",devIdx,ei);
  250. *dstCntPtr += dstCnt;
  251. if( activeCnt )
  252. *activeFlPtr = true;
  253. }
  254. return rc;
  255. }
  256. cmMpRC_t _cmMpCreateDevice( unsigned devIdx, cmMpDev* drp, MIDIPortRef inPortRef, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, cmErr_t* errPtr )
  257. {
  258. cmMpRC_t rc = kOkMpRC;
  259. MIDIDeviceRef mdr;
  260. ItemCount devSrcCnt = 0;
  261. ItemCount devDstCnt = 0;
  262. bool activeFl = false;
  263. unsigned ei;
  264. // zero the device record
  265. _cmMpDeviceInit(drp);
  266. // get the device ref
  267. if((mdr = MIDIGetDevice(devIdx)) == 0 )
  268. return _cmMpError(errPtr,kSysErrMpRC,noErr,"Get midi device %i failed.",devIdx);
  269. // determine if the device port count and whether it is active
  270. if((rc = _cmMpIsDeviceActive(devIdx, mdr, &devSrcCnt, &devDstCnt, &activeFl, errPtr )) != kOkMpRC )
  271. return rc;
  272. // if the device is not active return with the device record empty
  273. if( !activeFl)
  274. return kOkMpRC;
  275. // allocate the input port array
  276. drp->iPortCnt = devSrcCnt;
  277. drp->iPortArray = cmMemAllocZ( cmMpPort, devSrcCnt );
  278. // allocate the output port array
  279. drp->oPortCnt = devDstCnt;
  280. drp->oPortArray = cmMemAllocZ( cmMpPort, devDstCnt );
  281. // form the device name string
  282. drp->nameStr = _cmMpFormLabel( errPtr, (MIDIObjectRef)mdr);
  283. unsigned entityCnt = MIDIDeviceGetNumberOfEntities(mdr);
  284. unsigned iPortCnt = 0;
  285. unsigned oPortCnt = 0;
  286. // for each entity on this device
  287. for(ei=0; ei<entityCnt; ++ei)
  288. {
  289. MIDIEntityRef mer;
  290. unsigned i;
  291. // get the entity reference
  292. if((mer = MIDIDeviceGetEntity( mdr, ei)) == 0 )
  293. {
  294. rc = _cmMpError(errPtr,kSysErrMpRC,noErr,"Get midi device %i entity %i failed.",devIdx,ei);
  295. goto errLabel;
  296. }
  297. // for each input and output on this entity
  298. for( i=0; i<2; ++i)
  299. {
  300. bool inputFl = i==0;
  301. ItemCount activeCnt = 0;
  302. unsigned portCnt = inputFl ? iPortCnt : oPortCnt;
  303. cmMpPort* portArray = inputFl ? drp->iPortArray : drp->oPortArray;
  304. ItemCount entityPortCnt = inputFl ? MIDIEntityGetNumberOfSources( mer ) : MIDIEntityGetNumberOfDestinations( mer );
  305. SInt32 idArray[ entityPortCnt ];
  306. // fill idArray with the unique id's of the active ports on this entity
  307. if((rc = _cmMpGetEntityUniqueIdArray(mer,idArray,entityPortCnt,inputFl,&activeCnt,errPtr)) != kOkMpRC )
  308. {
  309. rc = _cmMpError(errPtr,rc,noErr,"Unable to locate unique source ids on device:%i entity:%i",devIdx,ei);
  310. goto errLabel;
  311. }
  312. // assign the unique id's to each cmMpPort assoc'd with this entity
  313. _cmMpInitPortArray(devIdx, inPortRef, mer, portArray + portCnt, idArray, entityPortCnt, inputFl, cbFunc, cbDataPtr, parserBufByteCnt, errPtr );
  314. if( inputFl )
  315. iPortCnt += entityPortCnt;
  316. else
  317. oPortCnt += entityPortCnt;
  318. }
  319. }
  320. assert( iPortCnt == devSrcCnt );
  321. assert( oPortCnt == devDstCnt );
  322. errLabel:
  323. if( rc != kOkMpRC )
  324. _cmMpDeviceFree(drp);
  325. return rc;
  326. }
  327. void _cmMpDestroyDeviceArray( cmMpRoot* rp )
  328. {
  329. unsigned i;
  330. for(i=0; i<rp->devCnt; ++i)
  331. _cmMpDeviceFree( rp->devArray + i );
  332. cmMemPtrFree(&rp->devArray);
  333. rp->devCnt = 0;
  334. }
  335. cmMpRC_t _cmMpCreateDeviceArray( cmMpRoot* rp, unsigned parserBufByteCnt )
  336. {
  337. cmMpRC_t rc = kOkMpRC;
  338. ItemCount devCnt = MIDIGetNumberOfDevices();
  339. ItemCount di = 0;
  340. rp->devCnt = 0;
  341. rp->devArray = cmMemAllocZ( cmMpDev, devCnt );
  342. for(di=0; di<devCnt; ++di)
  343. {
  344. cmMpDev* drp = rp->devArray + rp->devCnt;
  345. // attempt to create a device record for device di
  346. if((rc = _cmMpCreateDevice( di, drp, rp->inPortRef, rp->cbFunc, rp->cbDataPtr, parserBufByteCnt, &rp->err )) != kOkMpRC )
  347. goto errLabel;
  348. // if the device di is active
  349. if( drp->iPortCnt || drp->oPortCnt )
  350. ++rp->devCnt;
  351. }
  352. errLabel:
  353. if( rc != kOkMpRC )
  354. _cmMpDestroyDeviceArray(rp);
  355. return rc;
  356. }
  357. void _cmMpMidiSystemNotifyProc( const MIDINotification* notifyMsgPtr, void *refCon )
  358. {
  359. // cmMpRoot* p = (mdRoot*)refCon;
  360. // if(p != NULL )
  361. // ((fw::osxMidiPorts*)refCon)->onNotify(notifyMsgPtr);
  362. }
  363. void _cmMpMIDISystemReadProc( const MIDIPacketList *pktListPtr, void* readProcRefCon, void* srcConnRefCon )
  364. {
  365. //cmMpRoot* rp = (cmMpRoot*)readProcRefCon;
  366. cmMpPort* pp = (cmMpPort*)srcConnRefCon;
  367. unsigned i;
  368. if( pktListPtr == NULL /*|| rp == NULL*/ || pp == NULL /*|| rp->cbFunc == NULL*/ )
  369. return;
  370. const MIDIPacket* packetPtr = &pktListPtr->packet[0];
  371. for(i=0; i < pktListPtr->numPackets; ++i)
  372. {
  373. /*
  374. unsigned j;
  375. for(j=0; j<packetPtr->length; ++j)
  376. printf("0x%x ", packetPtr->data[j]);
  377. printf("\n");
  378. */
  379. // About converting mach_absolute_time() to seconds: http://lists.apple.com/archives/carbon-dev/2007/Jan//msg00282.html
  380. // About MIDIPacket.timeStamp: http://developer.apple.com/library/ios/#documentation/CoreMidi/Reference/MIDIServices_Reference/Reference/reference.html
  381. double nano = 1e-9 * ( (double) _cmMpRoot.timeBaseInfo.numer) / ((double) _cmMpRoot.timeBaseInfo.denom);
  382. // so here's the delta in nanoseconds:
  383. double nanoSeconds = ((double) packetPtr->timeStamp) * nano;
  384. // 1000 times that for microSeconds:
  385. double microSecs = 1000.0f * nanoSeconds;
  386. double deltaMicroSecs = microSecs - pp->prevMicroSecs;
  387. pp->prevMicroSecs = microSecs;
  388. assert( pp->inputFl == true );
  389. cmMpParseMidiData( pp->parserH, (unsigned)deltaMicroSecs, packetPtr->data, packetPtr->length );
  390. packetPtr = MIDIPacketNext(packetPtr);
  391. }
  392. }
  393. cmMpPort* _cmMpGetPort( cmMpDev* drp, unsigned portIdx, unsigned flags )
  394. {
  395. assert( flags != 0 );
  396. bool inputFl = cmIsFlag(flags, kInMpFl);
  397. assert( portIdx < (inputFl ? drp->iPortCnt : drp->oPortCnt));
  398. return inputFl ? drp->iPortArray + portIdx : drp->oPortArray + portIdx;
  399. }
  400. cmMpRC_t cmMpInitialize( cmCtx_t* c, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, const char* appNameStr )
  401. {
  402. cmMpRC_t rc = kOkMpRC;
  403. OSStatus err = noErr;
  404. CFStringRef clientNameStrRef = NULL;
  405. CFStringRef inPortNameStrRef = NULL;
  406. CFStringRef outPortNameStrRef = NULL;
  407. cmRpt_t* rpt = &c->rpt;
  408. if( _cmMpRtPtr == NULL )
  409. {
  410. memset(&_cmMpRoot,0,sizeof(_cmMpRoot));
  411. _cmMpRtPtr = &_cmMpRoot;
  412. }
  413. if((rc = cmMpFinalize()) != kOkMpRC )
  414. return rc;
  415. cmErrSetup(&_cmMpRoot.err,rpt,"MIDI Port");
  416. _cmMpRoot.cbFunc = cbFunc;
  417. _cmMpRoot.cbDataPtr = cbDataPtr;
  418. if(( clientNameStrRef = CFStringCreateWithCString(NULL,"MIDI",kCFStringEncodingASCII)) == NULL )
  419. {
  420. rc = _cmMpError(&_cmMpRoot.err,kCfStringErrMpRC,noErr,"Client name create failed.");
  421. goto errLabel;
  422. }
  423. if(( err = MIDIClientCreate( clientNameStrRef, _cmMpMidiSystemNotifyProc, &_cmMpRoot, &_cmMpRoot.clientRef )) != noErr )
  424. {
  425. rc = _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"Client create failed.");
  426. goto errLabel;
  427. }
  428. if((inPortNameStrRef = CFStringCreateWithCString(NULL,"in",kCFStringEncodingASCII)) == NULL )
  429. {
  430. rc = _cmMpError(&_cmMpRoot.err,kCfStringErrMpRC,noErr,"In port name string create failed.");
  431. goto errLabel;
  432. }
  433. if((err = MIDIInputPortCreate( _cmMpRoot.clientRef, inPortNameStrRef, _cmMpMIDISystemReadProc, &_cmMpRoot, &_cmMpRoot.inPortRef)) != noErr )
  434. {
  435. rc = _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"In port create failed.");
  436. goto errLabel;
  437. }
  438. if((outPortNameStrRef = CFStringCreateWithCString(NULL,"out",kCFStringEncodingASCII)) == NULL )
  439. {
  440. rc = _cmMpError(&_cmMpRoot.err,kCfStringErrMpRC,noErr,"Out port name string create failed.");
  441. goto errLabel;
  442. }
  443. if((err = MIDIOutputPortCreate( _cmMpRoot.clientRef, outPortNameStrRef, &_cmMpRoot.outPortRef)) != noErr )
  444. {
  445. rc = _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"Out port create failed.");
  446. goto errLabel;
  447. }
  448. if((rc = _cmMpCreateDeviceArray(&_cmMpRoot,parserBufByteCnt)) != kOkMpRC )
  449. goto errLabel;
  450. mach_timebase_info(&_cmMpRoot.timeBaseInfo);
  451. errLabel:
  452. if( clientNameStrRef != NULL )
  453. CFRelease(clientNameStrRef);
  454. if( inPortNameStrRef != NULL )
  455. CFRelease(inPortNameStrRef);
  456. if( outPortNameStrRef != NULL )
  457. CFRelease(outPortNameStrRef);
  458. if( rc != kOkMpRC )
  459. cmMpFinalize();
  460. return rc;
  461. }
  462. cmMpRC_t cmMpFinalize()
  463. {
  464. OSStatus err = noErr;
  465. cmMpRC_t rc = kOkMpRC;
  466. if( _cmMpRoot.inPortRef != 0 )
  467. {
  468. if((err = MIDIPortDispose(_cmMpRoot.inPortRef)) != noErr )
  469. rc = _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"MIDIPortDispose() failed on the input port.");
  470. else
  471. _cmMpRoot.inPortRef = 0;
  472. }
  473. if( _cmMpRoot.outPortRef != 0 )
  474. {
  475. if((err = MIDIPortDispose(_cmMpRoot.outPortRef)) != noErr )
  476. rc = _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"MIDIPortDispose() failed on the output port.");
  477. else
  478. _cmMpRoot.outPortRef = 0;
  479. }
  480. if( _cmMpRoot.clientRef != 0 )
  481. {
  482. if((err = MIDIClientDispose(_cmMpRoot.clientRef)) != noErr )
  483. rc = _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"MIDIClientDispose() failed.");
  484. else
  485. _cmMpRoot.clientRef = 0;
  486. }
  487. _cmMpDestroyDeviceArray(&_cmMpRoot);
  488. _cmMpRoot.devCnt = 0;
  489. _cmMpRoot.devArray = NULL;
  490. _cmMpRoot.cbFunc = NULL;
  491. _cmMpRoot.cbDataPtr = NULL;
  492. _cmMpRoot.clientRef = 0;
  493. _cmMpRoot.inPortRef = 0;
  494. _cmMpRoot.outPortRef = 0;
  495. return rc;
  496. }
  497. bool cmMpIsInitialized()
  498. { return _cmMpRoot.clientRef != 0; }
  499. unsigned cmMpDeviceCount()
  500. {
  501. return _cmMpRoot.devCnt;
  502. }
  503. const char* cmMpDeviceName( unsigned devIdx )
  504. {
  505. assert( devIdx < _cmMpRoot.devCnt );
  506. return _cmMpRoot.devArray[devIdx].nameStr;
  507. }
  508. unsigned cmMpDevicePortCount( unsigned devIdx, unsigned flags )
  509. {
  510. assert( devIdx < _cmMpRoot.devCnt );
  511. return cmIsFlag(flags,kInMpFl) ? _cmMpRoot.devArray[devIdx].iPortCnt : _cmMpRoot.devArray[devIdx].oPortCnt;
  512. }
  513. const char* cmMpDevicePortName( unsigned devIdx, unsigned flags, unsigned portIdx )
  514. {
  515. assert( devIdx < _cmMpRoot.devCnt );
  516. cmMpDev* drp = _cmMpRoot.devArray + devIdx;
  517. assert( portIdx < (cmIsFlag(flags,kInMpFl) ? drp->iPortCnt : drp->oPortCnt));
  518. return _cmMpGetPort( drp, portIdx, flags )->nameStr;
  519. }
  520. cmMpRC_t cmMpDeviceSend( unsigned devIdx, unsigned portIdx, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 )
  521. {
  522. assert( devIdx < _cmMpRoot.devCnt );
  523. cmMpDev* drp = _cmMpRoot.devArray + devIdx;
  524. cmMpPort* pp = _cmMpGetPort( drp, portIdx, kOutMpFl);
  525. OSStatus err = noErr;
  526. MIDIPacketList mpl;
  527. unsigned byteCnt = cmMidiStatusToByteCount(status & 0xf0 );
  528. if( byteCnt == 0 )
  529. return _cmMpError(&_cmMpRoot.err,kInvalidArgMpRC,noErr,"Invalid status byte (0x%x) on send to device:%i port:%i",status,devIdx,portIdx);
  530. mpl.numPackets = 1;
  531. mpl.packet[0].timeStamp = 0;
  532. mpl.packet[0].length = byteCnt+1;
  533. mpl.packet[0].data[0] = status;
  534. mpl.packet[0].data[1] = d0;
  535. mpl.packet[0].data[2] = d1;
  536. //printf("%i %i %i %i\n",status,d0,d1,byteCnt);
  537. if(( err = MIDISend( _cmMpRoot.outPortRef, pp->epr, &mpl)) != noErr )
  538. return _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"Send on device:%i port:%i failed.",devIdx,portIdx);
  539. return kOkMpRC;
  540. }
  541. cmMpRC_t cmMpDeviceSendData( unsigned devIdx, unsigned portIdx, const cmMidiByte_t* dataPtr, unsigned byteCnt )
  542. {
  543. assert( devIdx < _cmMpRoot.devCnt );
  544. cmMpDev* drp = _cmMpRoot.devArray + devIdx;
  545. cmMpPort* pp = _cmMpGetPort( drp, portIdx, kOutMpFl);
  546. OSStatus err = noErr;
  547. MIDIPacketList mpl;
  548. unsigned i;
  549. if( byteCnt > 256 )
  550. return _cmMpError(&_cmMpRoot.err,kInvalidArgMpRC,noErr,"mdDeviceSendData() only support data buffers up to 256 bytes.");
  551. mpl.numPackets = 1;
  552. mpl.packet[0].timeStamp = 0;
  553. mpl.packet[0].length = byteCnt;
  554. for(i=0; i<byteCnt; i++)
  555. mpl.packet[0].data[i] = dataPtr[i];
  556. if(( err = MIDISend( _cmMpRoot.outPortRef, pp->epr, &mpl)) != noErr )
  557. return _cmMpError(&_cmMpRoot.err,kSysErrMpRC,err,"Data Send on device:%i port:%i failed.",devIdx,portIdx);
  558. return kOkMpRC;
  559. }
  560. cmMpRC_t cmMpInstallCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr )
  561. {
  562. cmMpRC_t rc = kOkMpRC;
  563. unsigned di;
  564. unsigned dn = cmMpDeviceCount();
  565. for(di=0; di<dn; ++di)
  566. if( di==devIdx || devIdx == -1 )
  567. {
  568. unsigned pi;
  569. unsigned pn = cmMpDevicePortCount(di,kInMpFl);
  570. for(pi=0; pi<pn; ++pi)
  571. if( pi==portIdx || portIdx == -1 )
  572. if( cmMpParserInstallCallback( _cmMpRoot.devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) != kOkMpRC )
  573. goto errLabel;
  574. }
  575. errLabel:
  576. return rc;
  577. }
  578. cmMpRC_t cmMpRemoveCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr )
  579. {
  580. cmMpRC_t rc = kOkMpRC;
  581. unsigned di;
  582. unsigned dn = cmMpDeviceCount();
  583. unsigned remCnt = 0;
  584. for(di=0; di<dn; ++di)
  585. if( di==devIdx || devIdx == -1 )
  586. {
  587. unsigned pi;
  588. unsigned pn = cmMpDevicePortCount(di,kInMpFl);
  589. for(pi=0; pi<pn; ++pi)
  590. if( pi==portIdx || portIdx == -1 )
  591. if( cmMpParserHasCallback( _cmMpRoot.devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) )
  592. {
  593. if( cmMpParserRemoveCallback( _cmMpRoot.devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) != kOkMpRC )
  594. goto errLabel;
  595. else
  596. ++remCnt;
  597. }
  598. }
  599. if( remCnt == 0 && dn > 0 )
  600. rc = _cmMpError(&_cmMpRoot.err,kCbNotFoundMpRC,0,"The callback was not found on any of the specified devices or ports.");
  601. errLabel:
  602. return rc;
  603. }
  604. bool cmMpUsesCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr )
  605. {
  606. unsigned di;
  607. unsigned dn = cmMpDeviceCount();
  608. for(di=0; di<dn; ++di)
  609. if( di==devIdx || devIdx == -1 )
  610. {
  611. unsigned pi;
  612. unsigned pn = cmMpDevicePortCount(di,kInMpFl);
  613. for(pi=0; pi<pn; ++pi)
  614. if( pi==portIdx || portIdx == -1 )
  615. if( cmMpParserHasCallback( _cmMpRoot.devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) )
  616. return true;
  617. }
  618. return false;
  619. }
  620. void cmMpReport( cmRpt_t* rpt )
  621. {
  622. unsigned i;
  623. for(i=0; i<_cmMpRoot.devCnt; ++i)
  624. _cmMpDevicePrint(_cmMpRoot.devArray + i, i, rpt );
  625. }