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

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