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

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