libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmMidiOsx.c 23KB

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