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 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 = 0xff; // a midi triple may never have a status of 0xff
  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 != 0xff )
  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. }