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.

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. }