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

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