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

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