libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmMidiPort.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmTime.h"
  9. #include "cmMidi.h"
  10. #include "cmMidiPort.h"
  11. //===================================================================================================
  12. //
  13. //
  14. enum
  15. {
  16. kBufByteCnt = 1024,
  17. kExpectStatusStId=0, // 0
  18. kExpectDataStId, // 1
  19. kExpectStatusOrDataStId, // 2
  20. kExpectEOXStId // 3
  21. };
  22. typedef struct cmMpParserCb_str
  23. {
  24. cmMpCallback_t cbFunc;
  25. void* cbDataPtr;
  26. struct cmMpParserCb_str* linkPtr;
  27. } cmMpParserCb_t;
  28. typedef struct
  29. {
  30. cmErr_t err;
  31. cmMpParserCb_t* cbChain;
  32. cmMidiPacket_t pkt;
  33. unsigned state; // parser state id
  34. unsigned errCnt; // accumlated error count
  35. cmMidiByte_t status; // running status
  36. cmMidiByte_t data0; // data byte 0
  37. unsigned dataCnt; // data byte cnt for current status
  38. unsigned dataIdx; // index (0 or 1) of next data byte
  39. cmMidiByte_t* buf; // output buffer
  40. unsigned bufByteCnt; // output buffer byte cnt
  41. unsigned bufIdx; // next output buffer index
  42. unsigned msgCnt; // count of channel messages in the buffer
  43. } cmMpParser_t;
  44. cmMpParser_t* _cmMpParserFromHandle( cmMpParserH_t h )
  45. {
  46. cmMpParser_t* p = (cmMpParser_t*)h.h;
  47. assert(p!=NULL);
  48. return p;
  49. }
  50. void _cmMpParserReport( cmMpParser_t* p )
  51. {
  52. cmRptPrintf(p->err.rpt,"state:%i st:0x%x d0:%i dcnt:%i didx:%i buf[%i]->%i msg:%i err:%i\n",p->state,p->status,p->data0,p->dataCnt,p->dataIdx,p->bufByteCnt,p->bufIdx,p->msgCnt,p->errCnt);
  53. }
  54. void _cmMpParserDestroy( cmMpParser_t* p )
  55. {
  56. cmMemPtrFree(&p->buf);
  57. cmMpParserCb_t* c = p->cbChain;
  58. while(c != NULL)
  59. {
  60. cmMpParserCb_t* nc = c->linkPtr;
  61. cmMemFree(c);
  62. c = nc;
  63. }
  64. cmMemPtrFree(&p);
  65. }
  66. cmMpParserH_t cmMpParserCreate( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned bufByteCnt, cmRpt_t* rpt )
  67. {
  68. cmMpRC_t rc = kOkMpRC;
  69. cmMpParserH_t h;
  70. cmMpParser_t* p = cmMemAllocZ( cmMpParser_t, 1 );
  71. cmErrSetup(&p->err,rpt,"MIDI Parser");
  72. p->pkt.devIdx = devIdx;
  73. p->pkt.portIdx = portIdx;
  74. //p->cbChain = cmMemAllocZ( cmMpParserCb_t, 1 );
  75. //p->cbChain->cbFunc = cbFunc;
  76. //p->cbChain->cbDataPtr = cbDataPtr;
  77. //p->cbChain->linkPtr = NULL;
  78. p->cbChain = NULL;
  79. p->buf = cmMemAllocZ( cmMidiByte_t, bufByteCnt );
  80. p->bufByteCnt = bufByteCnt;
  81. p->bufIdx = 0;
  82. p->msgCnt = 0;
  83. p->state = kExpectStatusStId;
  84. p->dataIdx = cmInvalidIdx;
  85. p->dataCnt = cmInvalidCnt;
  86. p->status = kInvalidStatusMdId;
  87. h.h = p;
  88. if( cbFunc != NULL )
  89. rc = cmMpParserInstallCallback(h, cbFunc, cbDataPtr );
  90. if( rc != kOkMpRC )
  91. {
  92. h.h = NULL;
  93. _cmMpParserDestroy(p);
  94. }
  95. return h;
  96. }
  97. void cmMpParserDestroy( cmMpParserH_t* hp )
  98. {
  99. if( hp==NULL || hp->h == NULL )
  100. return;
  101. cmMpParser_t* p = _cmMpParserFromHandle(*hp);
  102. _cmMpParserDestroy(p);
  103. hp->h = NULL;
  104. }
  105. unsigned cmMpParserErrorCount( cmMpParserH_t h )
  106. {
  107. cmMpParser_t* p = _cmMpParserFromHandle(h);
  108. if( p == NULL )
  109. return 0;
  110. return p->errCnt;
  111. }
  112. void _cmMpParserCb( cmMpParser_t* p, cmMidiPacket_t* pkt, unsigned pktCnt )
  113. {
  114. cmMpParserCb_t* c = p->cbChain;
  115. for(; c!=NULL; c=c->linkPtr)
  116. {
  117. pkt->cbDataPtr = c->cbDataPtr;
  118. c->cbFunc( pkt, pktCnt );
  119. }
  120. }
  121. void _cmMpTransmitChMsgs( cmMpParser_t* p )
  122. {
  123. if( p->msgCnt > 0 )
  124. {
  125. p->pkt.msgArray = (cmMidiMsg*)p->buf;
  126. p->pkt.msgCnt = p->msgCnt;
  127. p->pkt.sysExMsg = NULL;
  128. //p->cbFunc( &p->pkt, 1 );
  129. _cmMpParserCb(p,&p->pkt,1);
  130. p->bufIdx = 0;
  131. p->msgCnt = 0;
  132. }
  133. }
  134. void _cmMpTransmitSysEx( cmMpParser_t* p )
  135. {
  136. p->pkt.msgArray = NULL;
  137. p->pkt.sysExMsg = p->buf;
  138. p->pkt.msgCnt = p->bufIdx;
  139. //p->cbFunc( &p->pkt, 1 );
  140. _cmMpParserCb(p,&p->pkt,1);
  141. p->bufIdx = 0;
  142. }
  143. void _cmMpParserStoreChMsg( cmMpParser_t* p, const cmTimeSpec_t* timeStamp, cmMidiByte_t d )
  144. {
  145. // if there is not enough room left in the buffer then transmit
  146. // the current messages
  147. if( p->bufByteCnt - p->bufIdx < sizeof(cmMidiMsg) )
  148. _cmMpTransmitChMsgs(p);
  149. assert( p->bufByteCnt - p->bufIdx >= sizeof(cmMidiMsg) );
  150. // get a pointer to the next msg in the buffer
  151. cmMidiMsg* msgPtr = (cmMidiMsg*)(p->buf + p->bufIdx);
  152. // fill the buffer msg
  153. msgPtr->timeStamp = *timeStamp;
  154. msgPtr->status = p->status;
  155. switch( p->dataCnt )
  156. {
  157. case 0:
  158. break;
  159. case 1:
  160. msgPtr->d0 = d;
  161. msgPtr->d1 = 0;
  162. break;
  163. case 2:
  164. msgPtr->d0 = p->data0;
  165. msgPtr->d1 = d;
  166. break;
  167. default:
  168. assert(0);
  169. }
  170. // update the msg count and next buffer
  171. ++p->msgCnt;
  172. p->bufIdx += sizeof(cmMidiMsg);
  173. }
  174. void cmMpParseMidiData( cmMpParserH_t h, const cmTimeSpec_t* timeStamp, const cmMidiByte_t* iBuf, unsigned iByteCnt )
  175. {
  176. cmMpParser_t* p = _cmMpParserFromHandle(h);
  177. if( p == NULL )
  178. return;
  179. const cmMidiByte_t* ip = iBuf;
  180. const cmMidiByte_t* ep = iBuf + iByteCnt;
  181. for(; ip < ep; ++ip )
  182. {
  183. // if this byte is a status byte
  184. if( cmMidiIsStatus(*ip) )
  185. {
  186. if( p->state != kExpectStatusStId && p->state != kExpectStatusOrDataStId )
  187. ++p->errCnt;
  188. p->status = *ip;
  189. p->dataCnt = cmMidiStatusToByteCount(*ip);
  190. switch( p->status )
  191. {
  192. case kSysExMdId: // if this is the start of a sys-ex msg ...
  193. // ... clear the buffer to prepare from sys-ex data
  194. _cmMpTransmitChMsgs(p);
  195. p->state = kExpectEOXStId;
  196. p->dataCnt = cmInvalidCnt;
  197. p->dataIdx = cmInvalidIdx;
  198. p->buf[ p->bufIdx++ ] = kSysExMdId;
  199. break;
  200. case kSysComEoxMdId: // if this is the end of a sys-ex msg
  201. assert( p->bufIdx < p->bufByteCnt );
  202. p->buf[p->bufIdx++] = *ip;
  203. _cmMpTransmitSysEx(p);
  204. p->state = kExpectStatusStId;
  205. break;
  206. default: // ... otherwise it is a 1,2, or 3 byte msg status
  207. if( p->dataCnt > 0 )
  208. {
  209. p->state = kExpectDataStId;
  210. p->dataIdx = 0;
  211. }
  212. else
  213. {
  214. // this is a status only msg - store it
  215. _cmMpParserStoreChMsg(p,timeStamp,*ip);
  216. p->state = kExpectStatusStId;
  217. p->dataIdx = cmInvalidIdx;
  218. p->dataCnt = cmInvalidCnt;
  219. }
  220. }
  221. continue;
  222. }
  223. // at this point the current byte (*ip) is a data byte
  224. switch(p->state)
  225. {
  226. case kExpectStatusOrDataStId:
  227. assert( p->dataIdx == 0 );
  228. case kExpectDataStId:
  229. switch( p->dataIdx )
  230. {
  231. case 0: // expecting data byte 0 ...
  232. switch( p->dataCnt )
  233. {
  234. case 1: // ... of a 1 byte msg - the msg is complete
  235. _cmMpParserStoreChMsg(p,timeStamp,*ip);
  236. p->state = kExpectStatusOrDataStId;
  237. break;
  238. case 2: // ... of a 2 byte msg - prepare to recv the second data byte
  239. p->state = kExpectDataStId;
  240. p->dataIdx = 1;
  241. p->data0 = *ip;
  242. break;
  243. default:
  244. assert(0);
  245. }
  246. break;
  247. case 1: // expecting data byte 1 of a two byte msg
  248. assert( p->dataCnt == 2 );
  249. assert( p->state == kExpectDataStId );
  250. _cmMpParserStoreChMsg(p,timeStamp,*ip);
  251. p->state = kExpectStatusOrDataStId;
  252. p->dataIdx = 0;
  253. break;
  254. default:
  255. assert(0);
  256. }
  257. break;
  258. case kExpectEOXStId:
  259. assert( p->bufIdx < p->bufByteCnt );
  260. p->buf[p->bufIdx++] = *ip;
  261. // if the buffer is full - then transmit it
  262. if( p->bufIdx == p->bufByteCnt )
  263. _cmMpTransmitSysEx(p);
  264. break;
  265. }
  266. } // ip loop
  267. _cmMpTransmitChMsgs(p);
  268. }
  269. cmMpRC_t cmMpParserMidiTriple( cmMpParserH_t h, const cmTimeSpec_t* timeStamp, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 )
  270. {
  271. cmMpRC_t rc = kOkMpRC;
  272. cmMpParser_t* p = _cmMpParserFromHandle(h);
  273. cmMidiByte_t mb = 0xff; // a midi triple may never have a status of 0xff
  274. if( d0 == 0xff )
  275. p->dataCnt = 0;
  276. else
  277. if( d1 == 0xff )
  278. p->dataCnt = 1;
  279. else
  280. p->dataCnt = 2;
  281. p->status = status;
  282. switch( p->dataCnt )
  283. {
  284. case 0:
  285. mb = status;
  286. break;
  287. case 1:
  288. mb = d0;
  289. break;
  290. case 2:
  291. p->data0 = d0;
  292. mb = d1;
  293. break;
  294. default:
  295. rc = cmErrMsg(&p->err,kInvalidArgMpRC,"An invalid MIDI status byte (0x%x) was encountered by the MIDI data parser.");
  296. goto errLabel;
  297. break;
  298. }
  299. if( mb != 0xff )
  300. _cmMpParserStoreChMsg(p,timeStamp,mb);
  301. p->dataCnt = cmInvalidCnt;
  302. errLabel:
  303. return rc;
  304. }
  305. cmMpRC_t cmMpParserTransmit( cmMpParserH_t h )
  306. {
  307. cmMpParser_t* p = _cmMpParserFromHandle(h);
  308. _cmMpTransmitChMsgs(p);
  309. return kOkMpRC;
  310. }
  311. cmMpRC_t cmMpParserInstallCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
  312. {
  313. cmMpParser_t* p = _cmMpParserFromHandle(h);
  314. cmMpParserCb_t* newCbPtr = cmMemAllocZ( cmMpParserCb_t, 1 );
  315. cmMpParserCb_t* c = p->cbChain;
  316. newCbPtr->cbFunc = cbFunc;
  317. newCbPtr->cbDataPtr = cbDataPtr;
  318. newCbPtr->linkPtr = NULL;
  319. if( p->cbChain == NULL )
  320. p->cbChain = newCbPtr;
  321. else
  322. {
  323. while( c->linkPtr != NULL )
  324. c = c->linkPtr;
  325. c->linkPtr = newCbPtr;
  326. }
  327. return kOkMpRC;
  328. }
  329. cmMpRC_t cmMpParserRemoveCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
  330. {
  331. cmMpParser_t* p = _cmMpParserFromHandle(h);
  332. cmMpParserCb_t* c1 = p->cbChain; // target link
  333. cmMpParserCb_t* c0 = NULL; // link pointing to target
  334. // search for the cbFunc to remove
  335. for(; c1!=NULL; c1=c1->linkPtr)
  336. {
  337. if( c1->cbFunc == cbFunc && c1->cbDataPtr == cbDataPtr)
  338. break;
  339. c0 = c1;
  340. }
  341. // if the cbFunc was not found
  342. if( c1 == NULL )
  343. return cmErrMsg(&p->err,kCbNotFoundMpRC,"Unable to locate the callback function %p for removal.",cbFunc);
  344. // the cbFunc to remove was found
  345. // if it was the first cb in the chain
  346. if( c0 == NULL )
  347. p->cbChain = c1->linkPtr;
  348. else
  349. c0->linkPtr = c1->linkPtr;
  350. cmMemFree(c1);
  351. return kOkMpRC;
  352. }
  353. bool cmMpParserHasCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
  354. {
  355. cmMpParser_t* p = _cmMpParserFromHandle(h);
  356. cmMpParserCb_t* c = p->cbChain; // target link
  357. // search for the cbFunc to remove
  358. for(; c!=NULL; c=c->linkPtr)
  359. if( c->cbFunc == cbFunc && c->cbDataPtr == cbDataPtr )
  360. return true;
  361. return false;
  362. }
  363. //====================================================================================================
  364. //
  365. //
  366. unsigned cmMpDeviceNameToIndex(const cmChar_t* name)
  367. {
  368. assert(name!=NULL);
  369. unsigned i;
  370. unsigned n = cmMpDeviceCount();
  371. for(i=0; i<n; ++i)
  372. if( strcmp(cmMpDeviceName(i),name)==0)
  373. return i;
  374. return cmInvalidIdx;
  375. }
  376. unsigned cmMpDevicePortNameToIndex( unsigned devIdx, unsigned flags, const cmChar_t* name )
  377. {
  378. unsigned i;
  379. unsigned n = cmMpDevicePortCount(devIdx,flags);
  380. for(i=0; i<n; ++i)
  381. if( strcmp(cmMpDevicePortName(devIdx,flags,i),name)==0)
  382. return i;
  383. return cmInvalidIdx;
  384. }
  385. //====================================================================================================
  386. //
  387. //
  388. void cmMpTestPrint( void* userDataPtr, const char* fmt, va_list vl )
  389. {
  390. vprintf(fmt,vl);
  391. }
  392. void cmMpTestCb( const cmMidiPacket_t* pktArray, unsigned pktCnt )
  393. {
  394. unsigned i,j;
  395. for(i=0; i<pktCnt; ++i)
  396. {
  397. const cmMidiPacket_t* p = pktArray + i;
  398. for(j=0; j<p->msgCnt; ++j)
  399. if( p->msgArray != NULL )
  400. printf("%ld %ld 0x%x %i %i\n", p->msgArray[j].timeStamp.tv_sec, p->msgArray[j].timeStamp.tv_nsec, p->msgArray[j].status,p->msgArray[j].d0, p->msgArray[j].d1);
  401. else
  402. printf("0x%x ",p->sysExMsg[j]);
  403. }
  404. }
  405. void cmMpTest( cmCtx_t* ctx )
  406. {
  407. char ch;
  408. unsigned parserBufByteCnt = 1024;
  409. cmMpInitialize(ctx,cmMpTestCb,NULL,parserBufByteCnt,"app");
  410. cmMpReport(&ctx->rpt);
  411. cmRptPrintf(&ctx->rpt,"<return> to continue\n");
  412. while((ch = getchar()) != 'q')
  413. {
  414. cmMpDeviceSend(2,0,0x90,60,60);
  415. }
  416. cmMpFinalize();
  417. }