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 11KB

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