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.

cmMidiAlsa.c 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  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 "cmLinkedHeap.h"
  9. #include "cmThread.h"
  10. #include "cmMidi.h"
  11. #include "cmMidiPort.h"
  12. #include <alsa/asoundlib.h>
  13. typedef struct
  14. {
  15. bool inputFl; // true if this an input port
  16. char* nameStr; // string label of this device
  17. unsigned alsa_type; // ALSA type flags from snd_seq_port_info_get_type()
  18. unsigned alsa_cap; // ALSA capability flags from snd_seq_port_info_get_capability()
  19. snd_seq_addr_t alsa_addr; // ALSA client/port address for this port
  20. cmMpParserH_t parserH; // interface to the client callback function for this port
  21. } cmMpPort_t;
  22. // MIDI devices
  23. typedef struct
  24. {
  25. char* nameStr; // string label for this device
  26. unsigned iPortCnt; // input ports on this device
  27. cmMpPort_t* iPortArray;
  28. unsigned oPortCnt; // output ports on this device
  29. cmMpPort_t* oPortArray;
  30. unsigned char clientId; // ALSA client id (all ports on this device use use this client id in their address)
  31. } cmMpDev_t;
  32. typedef struct
  33. {
  34. cmErr_t err; // error object
  35. cmLHeapH_t lH; // linked heap used for all internal memory
  36. unsigned devCnt; // MIDI devices attached to this computer
  37. cmMpDev_t* devArray;
  38. cmMpCallback_t cbFunc; // MIDI input application callback
  39. void* cbDataPtr;
  40. snd_seq_t* h; // ALSA system sequencer handle
  41. snd_seq_addr_t alsa_addr; // ALSA client/port address representing the application
  42. int alsa_queue; // ALSA device queue
  43. cmThreadH_t thH; // MIDI input listening thread
  44. int alsa_fdCnt; // MIDI input driver file descriptor array
  45. struct pollfd* alsa_fd;
  46. cmMpDev_t* prvRcvDev; // the last device and port to rcv MIDI
  47. cmMpPort_t* prvRcvPort;
  48. unsigned prvTimeMicroSecs; // time of last recognized event in microseconds
  49. unsigned eventCnt; // count of recognized events
  50. } cmMpRoot_t;
  51. cmMpRoot_t* _cmMpRoot = NULL;
  52. cmMpRC_t _cmMpErrMsgV(cmErr_t* err, cmMpRC_t rc, int alsaRc, const cmChar_t* fmt, va_list vl )
  53. {
  54. if( alsaRc < 0 )
  55. cmErrMsg(err,kSysErrMpRC,"ALSA Error:%i %s",alsaRc,snd_strerror(alsaRc));
  56. return cmErrVMsg(err,rc,fmt,vl);
  57. }
  58. cmMpRC_t _cmMpErrMsg(cmErr_t* err, cmMpRC_t rc, int alsaRc, const cmChar_t* fmt, ... )
  59. {
  60. va_list vl;
  61. va_start(vl,fmt);
  62. rc = _cmMpErrMsgV(err,rc,alsaRc,fmt,vl);
  63. va_end(vl);
  64. return rc;
  65. }
  66. unsigned _cmMpGetPortCnt( snd_seq_t* h, snd_seq_port_info_t* pip, bool inputFl )
  67. {
  68. unsigned i = 0;
  69. snd_seq_port_info_set_port(pip,-1);
  70. while( snd_seq_query_next_port(h,pip) == 0)
  71. if( cmIsFlag(snd_seq_port_info_get_capability(pip),inputFl?SND_SEQ_PORT_CAP_READ:SND_SEQ_PORT_CAP_WRITE) )
  72. ++i;
  73. return i;
  74. }
  75. cmMpDev_t* _cmMpClientIdToDev( int clientId )
  76. {
  77. cmMpRoot_t* p = _cmMpRoot;
  78. unsigned i;
  79. for(i=0; i<p->devCnt; ++i)
  80. if( p->devArray[i].clientId == clientId )
  81. return p->devArray + i;
  82. return NULL;
  83. }
  84. cmMpPort_t* _cmMpInPortIdToPort( cmMpDev_t* dev, int portId )
  85. {
  86. unsigned i;
  87. for(i=0; i<dev->iPortCnt; ++i)
  88. if( dev->iPortArray[i].alsa_addr.port == portId )
  89. return dev->iPortArray + i;
  90. return NULL;
  91. }
  92. void _cmMpSplit14Bits( unsigned v, cmMidiByte_t* d0, cmMidiByte_t* d1 )
  93. {
  94. *d0 = (v & 0x3f80) >> 7;
  95. *d1 = v & 0x7f;
  96. }
  97. cmMpRC_t cmMpPoll()
  98. {
  99. cmMpRC_t rc = kOkMpRC;
  100. cmMpRoot_t* p = _cmMpRoot;
  101. int timeOutMs = 50;
  102. snd_seq_event_t *ev;
  103. if (poll(p->alsa_fd, p->alsa_fdCnt, timeOutMs) > 0)
  104. {
  105. int rc = 1;
  106. do
  107. {
  108. rc = snd_seq_event_input(p->h,&ev);
  109. // if no input
  110. if( rc == -EAGAIN )
  111. break;
  112. // if input buffer overrun
  113. if( rc == -ENOSPC )
  114. break;
  115. // get the device this event arrived from
  116. if( p->prvRcvDev==NULL || p->prvRcvDev->clientId != ev->source.client )
  117. p->prvRcvDev = _cmMpClientIdToDev(ev->source.client);
  118. // get the port this event arrived from
  119. if( p->prvRcvDev != NULL && (p->prvRcvPort==NULL || p->prvRcvPort->alsa_addr.port != ev->source.port) )
  120. p->prvRcvPort = _cmMpInPortIdToPort(p->prvRcvDev,ev->source.port);
  121. if( p->prvRcvDev == NULL || p->prvRcvPort == NULL )
  122. continue;
  123. //printf("%i %x\n",ev->type,ev->type);
  124. //printf("dev:%i port:%i ch:%i %i\n",ev->source.client,ev->source.port,ev->data.note.channel,ev->data.note.note);
  125. unsigned microSecs1 = (ev->time.time.tv_sec * 1000000) + (ev->time.time.tv_nsec/1000);
  126. unsigned deltaMicroSecs = p->prvTimeMicroSecs==0 ? 0 : microSecs1 - p->prvTimeMicroSecs;
  127. cmMidiByte_t d0 = 0xff;
  128. cmMidiByte_t d1 = 0xff;
  129. cmMidiByte_t status = 0;
  130. switch(ev->type)
  131. {
  132. //
  133. // MIDI Channel Messages
  134. //
  135. case SND_SEQ_EVENT_NOTEON:
  136. status = kNoteOnMdId;
  137. d0 = ev->data.note.note;
  138. d1 = ev->data.note.velocity;
  139. //printf("%s (%i : %i) (%i)\n", snd_seq_ev_is_abstime(ev)?"abs":"rel",ev->time.time.tv_sec,ev->time.time.tv_nsec, deltaMicroSecs/1000);
  140. break;
  141. case SND_SEQ_EVENT_NOTEOFF:
  142. status = kNoteOffMdId;
  143. d0 = ev->data.note.note;
  144. d1 = ev->data.note.velocity;
  145. break;
  146. case SND_SEQ_EVENT_KEYPRESS:
  147. status = kPolyPresMdId;
  148. d0 = ev->data.note.note;
  149. d1 = ev->data.note.velocity;
  150. break;
  151. case SND_SEQ_EVENT_PGMCHANGE:
  152. status = kPgmMdId;
  153. d0 = ev->data.control.param;
  154. d1 = 0xff;
  155. break;
  156. case SND_SEQ_EVENT_CHANPRESS:
  157. status = kChPresMdId;
  158. d0 = ev->data.control.param;
  159. d1 = 0xff;
  160. break;
  161. case SND_SEQ_EVENT_CONTROLLER:
  162. status = kCtlMdId;
  163. d0 = ev->data.control.param;
  164. d1 = ev->data.control.value;
  165. break;
  166. case SND_SEQ_EVENT_PITCHBEND:
  167. _cmMpSplit14Bits(ev->data.control.value + 8192, &d0, &d1 );
  168. status = kPbendMdId;
  169. break;
  170. //
  171. // MIDI System Common Messages
  172. //
  173. case SND_SEQ_EVENT_QFRAME:
  174. status = kSysComMtcMdId;
  175. d0 = ev->data.control.value;
  176. break;
  177. case SND_SEQ_EVENT_SONGPOS:
  178. _cmMpSplit14Bits(ev->data.control.value, &d0, &d1 );
  179. status = kSysComSppMdId;
  180. break;
  181. case SND_SEQ_EVENT_SONGSEL:
  182. status = kSysComSelMdId;
  183. d0 = ev->data.control.value;
  184. break;
  185. case SND_SEQ_EVENT_TUNE_REQUEST:
  186. status = kSysComTuneMdId;
  187. break;
  188. //
  189. // MIDI System Real-time Messages
  190. //
  191. case SND_SEQ_EVENT_CLOCK: status = kSysRtClockMdId; break;
  192. case SND_SEQ_EVENT_START: status = kSysRtStartMdId; break;
  193. case SND_SEQ_EVENT_CONTINUE: status = kSysRtContMdId; break;
  194. case SND_SEQ_EVENT_STOP: status = kSysRtStopMdId; break;
  195. case SND_SEQ_EVENT_SENSING: status = kSysRtSenseMdId; break;
  196. case SND_SEQ_EVENT_RESET: status = kSysRtResetMdId; break;
  197. }
  198. if( status != 0 )
  199. {
  200. cmMidiByte_t ch = ev->data.note.channel;
  201. cmMpParserMidiTriple(p->prvRcvPort->parserH, deltaMicroSecs, status | ch, d0, d1 );
  202. p->prvTimeMicroSecs = microSecs1;
  203. p->eventCnt += 1;
  204. }
  205. }while( snd_seq_event_input_pending(p->h,0));
  206. cmMpParserTransmit(p->prvRcvPort->parserH);
  207. }
  208. return rc;
  209. }
  210. bool _cmMpThreadFunc(void* param)
  211. {
  212. cmMpPoll();
  213. return true;
  214. }
  215. cmMpRC_t _cmMpAllocStruct( cmMpRoot_t* p, const cmChar_t* appNameStr, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, cmRpt_t* rpt )
  216. {
  217. cmMpRC_t rc = kOkMpRC;
  218. snd_seq_client_info_t* cip = NULL;
  219. snd_seq_port_info_t* pip = NULL;
  220. snd_seq_port_subscribe_t *subs = NULL;
  221. unsigned i,j,k,arc;
  222. // alloc the subscription recd on the stack
  223. snd_seq_port_subscribe_alloca(&subs);
  224. // alloc the client recd
  225. if((arc = snd_seq_client_info_malloc(&cip)) < 0 )
  226. {
  227. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"ALSA seq client info allocation failed.");
  228. goto errLabel;
  229. }
  230. // alloc the port recd
  231. if((arc = snd_seq_port_info_malloc(&pip)) < 0 )
  232. {
  233. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"ALSA seq port info allocation failed.");
  234. goto errLabel;
  235. }
  236. if((p->alsa_queue = snd_seq_alloc_queue(p->h)) < 0 )
  237. {
  238. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,p->alsa_queue,"ALSA queue allocation failed.");
  239. goto errLabel;
  240. }
  241. // Set arbitrary tempo (mm=100) and resolution (240) (FROM RtMidi.cpp)
  242. /*
  243. snd_seq_queue_tempo_t *qtempo;
  244. snd_seq_queue_tempo_alloca(&qtempo);
  245. snd_seq_queue_tempo_set_tempo(qtempo, 600000);
  246. snd_seq_queue_tempo_set_ppq(qtempo, 240);
  247. snd_seq_set_queue_tempo(p->h, p->alsa_queue, qtempo);
  248. snd_seq_drain_output(p->h);
  249. */
  250. // setup the client port
  251. snd_seq_set_client_name(p->h,appNameStr);
  252. snd_seq_port_info_set_client(pip, p->alsa_addr.client = snd_seq_client_id(p->h) );
  253. snd_seq_port_info_set_name(pip,cmStringNullGuard(appNameStr));
  254. snd_seq_port_info_set_capability(pip,SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_DUPLEX | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_SUBS_WRITE );
  255. snd_seq_port_info_set_type(pip, SND_SEQ_PORT_TYPE_SOFTWARE | SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC );
  256. snd_seq_port_info_set_midi_channels(pip, 16);
  257. // cfg for real-time time stamping
  258. snd_seq_port_info_set_timestamping(pip, 1);
  259. snd_seq_port_info_set_timestamp_real(pip, 1);
  260. snd_seq_port_info_set_timestamp_queue(pip, p->alsa_queue);
  261. // create the client port
  262. if((p->alsa_addr.port = snd_seq_create_port(p->h,pip)) < 0 )
  263. {
  264. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,p->alsa_addr.port,"ALSA client port creation failed.");
  265. goto errLabel;
  266. }
  267. p->devCnt = 0;
  268. // determine the count of devices
  269. snd_seq_client_info_set_client(cip, -1);
  270. while( snd_seq_query_next_client(p->h,cip) == 0)
  271. p->devCnt += 1;
  272. // allocate the device array
  273. p->devArray = cmLhAllocZ(p->lH,cmMpDev_t,p->devCnt);
  274. // fill in each device record
  275. snd_seq_client_info_set_client(cip, -1);
  276. for(i=0; snd_seq_query_next_client(p->h,cip)==0; ++i)
  277. {
  278. assert(i<p->devCnt);
  279. int client = snd_seq_client_info_get_client(cip);
  280. const char* name = snd_seq_client_info_get_name(cip);
  281. // initalize the device record
  282. p->devArray[i].nameStr = cmLhAllocStr(p->lH,cmStringNullGuard(name));
  283. p->devArray[i].iPortCnt = 0;
  284. p->devArray[i].oPortCnt = 0;
  285. p->devArray[i].iPortArray = NULL;
  286. p->devArray[i].oPortArray = NULL;
  287. p->devArray[i].clientId = client;
  288. snd_seq_port_info_set_client(pip,client);
  289. snd_seq_port_info_set_port(pip,-1);
  290. // determine the count of in/out ports on this device
  291. while( snd_seq_query_next_port(p->h,pip) == 0 )
  292. {
  293. unsigned caps = snd_seq_port_info_get_capability(pip);
  294. if( cmIsFlag(caps,SND_SEQ_PORT_CAP_READ) )
  295. p->devArray[i].iPortCnt += 1;
  296. if( cmIsFlag(caps,SND_SEQ_PORT_CAP_WRITE) )
  297. p->devArray[i].oPortCnt += 1;
  298. }
  299. // allocate the device port arrays
  300. if( p->devArray[i].iPortCnt > 0 )
  301. p->devArray[i].iPortArray = cmLhAllocZ(p->lH,cmMpPort_t,p->devArray[i].iPortCnt);
  302. if( p->devArray[i].oPortCnt > 0 )
  303. p->devArray[i].oPortArray = cmLhAllocZ(p->lH,cmMpPort_t,p->devArray[i].oPortCnt);
  304. snd_seq_port_info_set_client(pip,client); // set the ports client id
  305. snd_seq_port_info_set_port(pip,-1);
  306. // fill in the port information
  307. for(j=0,k=0; snd_seq_query_next_port(p->h,pip) == 0; )
  308. {
  309. const char* port = snd_seq_port_info_get_name(pip);
  310. unsigned type = snd_seq_port_info_get_type(pip);
  311. unsigned caps = snd_seq_port_info_get_capability(pip);
  312. snd_seq_addr_t addr = *snd_seq_port_info_get_addr(pip);
  313. if( cmIsFlag(caps,SND_SEQ_PORT_CAP_READ) )
  314. {
  315. assert(j<p->devArray[i].iPortCnt);
  316. p->devArray[i].iPortArray[j].inputFl = true;
  317. p->devArray[i].iPortArray[j].nameStr = cmLhAllocStr(p->lH,cmStringNullGuard(port));
  318. p->devArray[i].iPortArray[j].alsa_type = type;
  319. p->devArray[i].iPortArray[j].alsa_cap = caps;
  320. p->devArray[i].iPortArray[j].alsa_addr = addr;
  321. p->devArray[i].iPortArray[j].parserH = cmMpParserCreate(i, j, cbFunc, cbDataPtr, parserBufByteCnt, rpt );
  322. // port->app
  323. snd_seq_port_subscribe_set_sender(subs, &addr);
  324. snd_seq_port_subscribe_set_dest(subs, &p->alsa_addr);
  325. snd_seq_port_subscribe_set_queue(subs, 1);
  326. snd_seq_port_subscribe_set_time_update(subs, 1);
  327. snd_seq_port_subscribe_set_time_real(subs, 1);
  328. if((arc = snd_seq_subscribe_port(p->h, subs)) < 0)
  329. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"Input port to app. subscription failed on port '%s'.",cmStringNullGuard(port));
  330. ++j;
  331. }
  332. if( cmIsFlag(caps,SND_SEQ_PORT_CAP_WRITE) )
  333. {
  334. assert(k<p->devArray[i].oPortCnt);
  335. p->devArray[i].oPortArray[k].inputFl = false;
  336. p->devArray[i].oPortArray[k].nameStr = cmLhAllocStr(p->lH,cmStringNullGuard(port));
  337. p->devArray[i].oPortArray[k].alsa_type = type;
  338. p->devArray[i].oPortArray[k].alsa_cap = caps;
  339. p->devArray[i].oPortArray[k].alsa_addr = addr;
  340. // app->port connection
  341. snd_seq_port_subscribe_set_sender(subs, &p->alsa_addr);
  342. snd_seq_port_subscribe_set_dest( subs, &addr);
  343. if((arc = snd_seq_subscribe_port(p->h, subs)) < 0 )
  344. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"App to output port subscription failed on port '%s'.",cmStringNullGuard(port));
  345. ++k;
  346. }
  347. }
  348. }
  349. errLabel:
  350. if( pip != NULL)
  351. snd_seq_port_info_free(pip);
  352. if( cip != NULL )
  353. snd_seq_client_info_free(cip);
  354. return rc;
  355. }
  356. cmMpRC_t cmMpInitialize( cmCtx_t* ctx, cmMpCallback_t cbFunc, void* cbArg, unsigned parserBufByteCnt, const char* appNameStr )
  357. {
  358. cmMpRC_t rc = kOkMpRC;
  359. int arc = 0;
  360. cmMpRoot_t* p = NULL;
  361. if((rc = cmMpFinalize()) != kOkMpRC )
  362. return rc;
  363. // allocate the global root object
  364. _cmMpRoot = p = cmMemAllocZ(cmMpRoot_t,1);
  365. p->h = NULL;
  366. p->alsa_queue = -1;
  367. cmErrSetup(&p->err,&ctx->rpt,"MIDI Port");
  368. // setup the local linked heap manager
  369. if(cmLHeapIsValid(p->lH = cmLHeapCreate(2048,ctx)) == false )
  370. {
  371. rc = _cmMpErrMsg(&p->err,kLHeapErrMpRC,0,"Linked heap initialization failed.");
  372. goto errLabel;
  373. }
  374. // create the listening thread
  375. if( cmThreadCreate( &p->thH, _cmMpThreadFunc, NULL, &ctx->rpt) != kOkThRC )
  376. {
  377. rc = _cmMpErrMsg(&p->err,kThreadErrMpRC,0,"Thread initialization failed.");
  378. goto errLabel;
  379. }
  380. // initialize the ALSA sequencer
  381. if((arc = snd_seq_open(&p->h, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK )) < 0 )
  382. {
  383. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"ALSA Sequencer open failed.");
  384. goto errLabel;
  385. }
  386. // setup the device and port structures
  387. if((rc = _cmMpAllocStruct(p,appNameStr,cbFunc,cbArg,parserBufByteCnt,&ctx->rpt)) != kOkMpRC )
  388. goto errLabel;
  389. // allocate the file descriptors used for polling
  390. p->alsa_fdCnt = snd_seq_poll_descriptors_count(p->h, POLLIN);
  391. p->alsa_fd = cmMemAllocZ(struct pollfd,p->alsa_fdCnt);
  392. snd_seq_poll_descriptors(p->h, p->alsa_fd, p->alsa_fdCnt, POLLIN);
  393. p->cbFunc = cbFunc;
  394. p->cbDataPtr = cbArg;
  395. // start the sequencer queue
  396. if((arc = snd_seq_start_queue(p->h, p->alsa_queue, NULL)) < 0 )
  397. {
  398. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"ALSA queue start failed.");
  399. goto errLabel;
  400. }
  401. // send any pending commands to the driver
  402. snd_seq_drain_output(p->h);
  403. if( cmThreadPause(p->thH,0) != kOkThRC )
  404. rc = _cmMpErrMsg(&p->err,kThreadErrMpRC,0,"Thread start failed.");
  405. errLabel:
  406. if( rc != kOkMpRC )
  407. cmMpFinalize();
  408. return rc;
  409. }
  410. cmMpRC_t cmMpFinalize()
  411. {
  412. cmMpRC_t rc = kOkMpRC;
  413. cmMpRoot_t* p = _cmMpRoot;
  414. if( _cmMpRoot != NULL )
  415. {
  416. int arc;
  417. // stop the thread first
  418. if( cmThreadDestroy(&p->thH) != kOkThRC )
  419. {
  420. rc = _cmMpErrMsg(&p->err,kThreadErrMpRC,0,"Thread destroy failed.");
  421. goto errLabel;
  422. }
  423. // stop the queue
  424. if((arc = snd_seq_stop_queue(p->h,p->alsa_queue, NULL)) < 0 )
  425. {
  426. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"ALSA queue stop failed.");
  427. goto errLabel;
  428. }
  429. // release the alsa queue
  430. if( p->alsa_queue != -1 )
  431. {
  432. if((arc = snd_seq_free_queue(p->h,p->alsa_queue)) < 0 )
  433. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"ALSA queue release failed.");
  434. else
  435. p->alsa_queue = -1;
  436. }
  437. // release the alsa system handle
  438. if( p->h != NULL )
  439. {
  440. if( (arc = snd_seq_close(p->h)) < 0 )
  441. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"ALSA sequencer close failed.");
  442. else
  443. p->h = NULL;
  444. }
  445. // release each parser
  446. unsigned i,j;
  447. for(i=0; i<p->devCnt; ++i)
  448. for(j=0; j<p->devArray[i].iPortCnt; ++j)
  449. cmMpParserDestroy(&p->devArray[i].iPortArray[j].parserH);
  450. cmLHeapDestroy(&p->lH);
  451. cmMemFree(p->alsa_fd);
  452. cmMemPtrFree(&_cmMpRoot);
  453. }
  454. errLabel:
  455. return rc;
  456. }
  457. bool cmMpIsInitialized()
  458. { return _cmMpRoot!=NULL; }
  459. unsigned cmMpDeviceCount()
  460. { return _cmMpRoot==NULL ? 0 : _cmMpRoot->devCnt; }
  461. const char* cmMpDeviceName( unsigned devIdx )
  462. {
  463. if( _cmMpRoot==NULL || devIdx>=_cmMpRoot->devCnt)
  464. return NULL;
  465. return _cmMpRoot->devArray[devIdx].nameStr;
  466. }
  467. unsigned cmMpDevicePortCount( unsigned devIdx, unsigned flags )
  468. {
  469. if( _cmMpRoot==NULL || devIdx>=_cmMpRoot->devCnt)
  470. return 0;
  471. if( cmIsFlag(flags,kInMpFl) )
  472. return _cmMpRoot->devArray[devIdx].iPortCnt;
  473. return _cmMpRoot->devArray[devIdx].oPortCnt;
  474. }
  475. const char* cmMpDevicePortName( unsigned devIdx, unsigned flags, unsigned portIdx )
  476. {
  477. if( _cmMpRoot==NULL || devIdx>=_cmMpRoot->devCnt)
  478. return 0;
  479. if( cmIsFlag(flags,kInMpFl) )
  480. {
  481. if( portIdx >= _cmMpRoot->devArray[devIdx].iPortCnt )
  482. return 0;
  483. return _cmMpRoot->devArray[devIdx].iPortArray[portIdx].nameStr;
  484. }
  485. if( portIdx >= _cmMpRoot->devArray[devIdx].oPortCnt )
  486. return 0;
  487. return _cmMpRoot->devArray[devIdx].oPortArray[portIdx].nameStr;
  488. }
  489. cmMpRC_t cmMpDeviceSend( unsigned devIdx, unsigned portIdx, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 )
  490. {
  491. cmMpRC_t rc = kOkMpRC;
  492. snd_seq_event_t ev;
  493. int arc;
  494. cmMpRoot_t* p = _cmMpRoot;
  495. assert( p!=NULL && devIdx < p->devCnt && portIdx < p->devArray[devIdx].oPortCnt );
  496. cmMpPort_t* port = p->devArray[devIdx].oPortArray + portIdx;
  497. snd_seq_ev_clear(&ev);
  498. snd_seq_ev_set_source(&ev, p->alsa_addr.port);
  499. //snd_seq_ev_set_subs(&ev);
  500. snd_seq_ev_set_dest(&ev, port->alsa_addr.client, port->alsa_addr.port);
  501. snd_seq_ev_set_direct(&ev);
  502. snd_seq_ev_set_fixed(&ev);
  503. switch( status & 0xf0 )
  504. {
  505. case kNoteOffMdId:
  506. ev.type = SND_SEQ_EVENT_NOTEOFF;
  507. ev.data.note.note = d0;
  508. ev.data.note.velocity = d1;
  509. break;
  510. case kNoteOnMdId:
  511. ev.type = SND_SEQ_EVENT_NOTEON;
  512. ev.data.note.note = d0;
  513. ev.data.note.velocity = d1;
  514. break;
  515. case kPolyPresMdId:
  516. ev.type = SND_SEQ_EVENT_KEYPRESS ;
  517. ev.data.note.note = d0;
  518. ev.data.note.velocity = d1;
  519. break;
  520. case kCtlMdId:
  521. ev.type = SND_SEQ_EVENT_CONTROLLER;
  522. ev.data.control.param = d0;
  523. ev.data.control.value = d1;
  524. break;
  525. case kPgmMdId:
  526. ev.type = SND_SEQ_EVENT_PGMCHANGE;
  527. ev.data.control.param = d0;
  528. ev.data.control.value = d1;
  529. break;
  530. case kChPresMdId:
  531. ev.type = SND_SEQ_EVENT_CHANPRESS;
  532. ev.data.control.param = d0;
  533. ev.data.control.value = d1;
  534. break;
  535. case kPbendMdId:
  536. {
  537. int val = d0;
  538. val <<= 7;
  539. val += d1;
  540. val -= 8192;
  541. ev.type = SND_SEQ_EVENT_PITCHBEND;
  542. ev.data.control.param = 0;
  543. ev.data.control.value = val;
  544. }
  545. break;
  546. default:
  547. rc = _cmMpErrMsg(&p->err,kInvalidArgMpRC,0,"Cannot send an invalid MIDI status byte:0x%x.",status & 0xf0);
  548. goto errLabel;
  549. }
  550. ev.data.note.channel = status & 0x0f;
  551. if((arc = snd_seq_event_output(p->h, &ev)) < 0 )
  552. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"MIDI event output failed.");
  553. if((arc = snd_seq_drain_output(p->h)) < 0 )
  554. rc = _cmMpErrMsg(&p->err,kSysErrMpRC,arc,"MIDI event output drain failed.");
  555. errLabel:
  556. return rc;
  557. }
  558. cmMpRC_t cmMpDeviceSendData( unsigned devIdx, unsigned portIdx, const cmMidiByte_t* dataPtr, unsigned byteCnt )
  559. {
  560. cmMpRoot_t* p = _cmMpRoot;
  561. return cmErrMsg(&p->err,kNotImplMpRC,"cmMpDeviceSendData() has not yet been implemented for ALSA.");
  562. }
  563. cmMpRC_t cmMpInstallCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr )
  564. {
  565. cmMpRC_t rc = kOkMpRC;
  566. unsigned di;
  567. unsigned dn = cmMpDeviceCount();
  568. cmMpRoot_t* p = _cmMpRoot;
  569. for(di=0; di<dn; ++di)
  570. if( di==devIdx || devIdx == -1 )
  571. {
  572. unsigned pi;
  573. unsigned pn = cmMpDevicePortCount(di,kInMpFl);
  574. for(pi=0; pi<pn; ++pi)
  575. if( pi==portIdx || portIdx == -1 )
  576. if( cmMpParserInstallCallback( p->devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) != kOkMpRC )
  577. goto errLabel;
  578. }
  579. errLabel:
  580. return rc;
  581. }
  582. cmMpRC_t cmMpRemoveCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr )
  583. {
  584. cmMpRC_t rc = kOkMpRC;
  585. unsigned di;
  586. unsigned dn = cmMpDeviceCount();
  587. unsigned remCnt = 0;
  588. cmMpRoot_t* p = _cmMpRoot;
  589. for(di=0; di<dn; ++di)
  590. if( di==devIdx || devIdx == -1 )
  591. {
  592. unsigned pi;
  593. unsigned pn = cmMpDevicePortCount(di,kInMpFl);
  594. for(pi=0; pi<pn; ++pi)
  595. if( pi==portIdx || portIdx == -1 )
  596. if( cmMpParserHasCallback( p->devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) )
  597. {
  598. if( cmMpParserRemoveCallback( p->devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) != kOkMpRC )
  599. goto errLabel;
  600. else
  601. ++remCnt;
  602. }
  603. }
  604. if( remCnt == 0 && dn > 0 )
  605. rc = _cmMpErrMsg(&p->err,kCbNotFoundMpRC,0,"The callback was not found on any of the specified devices or ports.");
  606. errLabel:
  607. return rc;
  608. }
  609. bool cmMpUsesCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr )
  610. {
  611. unsigned di;
  612. unsigned dn = cmMpDeviceCount();
  613. cmMpRoot_t* p = _cmMpRoot;
  614. for(di=0; di<dn; ++di)
  615. if( di==devIdx || devIdx == -1 )
  616. {
  617. unsigned pi;
  618. unsigned pn = cmMpDevicePortCount(di,kInMpFl);
  619. for(pi=0; pi<pn; ++pi)
  620. if( pi==portIdx || portIdx == -1 )
  621. if( cmMpParserHasCallback( p->devArray[di].iPortArray[pi].parserH, cbFunc, cbDataPtr ) )
  622. return true;
  623. }
  624. return false;
  625. }
  626. void _cmMpReportPort( cmRpt_t* rpt, const cmMpPort_t* port )
  627. {
  628. cmRptPrintf(rpt," client:%i port:%i %s caps:(",port->alsa_addr.client,port->alsa_addr.port,port->nameStr);
  629. if( port->alsa_cap & SND_SEQ_PORT_CAP_READ ) cmRptPrintf(rpt,"Read " );
  630. if( port->alsa_cap & SND_SEQ_PORT_CAP_WRITE ) cmRptPrintf(rpt,"Writ " );
  631. if( port->alsa_cap & SND_SEQ_PORT_CAP_SYNC_READ ) cmRptPrintf(rpt,"Syrd " );
  632. if( port->alsa_cap & SND_SEQ_PORT_CAP_SYNC_WRITE ) cmRptPrintf(rpt,"Sywr " );
  633. if( port->alsa_cap & SND_SEQ_PORT_CAP_DUPLEX ) cmRptPrintf(rpt,"Dupl " );
  634. if( port->alsa_cap & SND_SEQ_PORT_CAP_SUBS_READ ) cmRptPrintf(rpt,"Subr " );
  635. if( port->alsa_cap & SND_SEQ_PORT_CAP_SUBS_WRITE ) cmRptPrintf(rpt,"Subw " );
  636. if( port->alsa_cap & SND_SEQ_PORT_CAP_NO_EXPORT ) cmRptPrintf(rpt,"Nexp " );
  637. cmRptPrintf(rpt,") type:(");
  638. if( port->alsa_type & SND_SEQ_PORT_TYPE_SPECIFIC ) cmRptPrintf(rpt,"Spec ");
  639. if( port->alsa_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) cmRptPrintf(rpt,"Gnrc ");
  640. if( port->alsa_type & SND_SEQ_PORT_TYPE_MIDI_GM ) cmRptPrintf(rpt,"GM ");
  641. if( port->alsa_type & SND_SEQ_PORT_TYPE_MIDI_GS ) cmRptPrintf(rpt,"GS ");
  642. if( port->alsa_type & SND_SEQ_PORT_TYPE_MIDI_XG ) cmRptPrintf(rpt,"XG ");
  643. if( port->alsa_type & SND_SEQ_PORT_TYPE_MIDI_MT32 ) cmRptPrintf(rpt,"MT32 ");
  644. if( port->alsa_type & SND_SEQ_PORT_TYPE_MIDI_GM2 ) cmRptPrintf(rpt,"GM2 ");
  645. if( port->alsa_type & SND_SEQ_PORT_TYPE_SYNTH ) cmRptPrintf(rpt,"Syn ");
  646. if( port->alsa_type & SND_SEQ_PORT_TYPE_DIRECT_SAMPLE) cmRptPrintf(rpt,"Dsmp ");
  647. if( port->alsa_type & SND_SEQ_PORT_TYPE_SAMPLE ) cmRptPrintf(rpt,"Samp ");
  648. if( port->alsa_type & SND_SEQ_PORT_TYPE_HARDWARE ) cmRptPrintf(rpt,"Hwar ");
  649. if( port->alsa_type & SND_SEQ_PORT_TYPE_SOFTWARE ) cmRptPrintf(rpt,"Soft ");
  650. if( port->alsa_type & SND_SEQ_PORT_TYPE_SYNTHESIZER ) cmRptPrintf(rpt,"Sizr ");
  651. if( port->alsa_type & SND_SEQ_PORT_TYPE_PORT ) cmRptPrintf(rpt,"Port ");
  652. if( port->alsa_type & SND_SEQ_PORT_TYPE_APPLICATION ) cmRptPrintf(rpt,"Appl ");
  653. cmRptPrintf(rpt,")\n");
  654. }
  655. void cmMpReport( cmRpt_t* rpt )
  656. {
  657. cmMpRoot_t* p = _cmMpRoot;
  658. unsigned i,j;
  659. cmRptPrintf(rpt,"Buffer size bytes in:%i out:%i\n",snd_seq_get_input_buffer_size(p->h),snd_seq_get_output_buffer_size(p->h));
  660. for(i=0; i<p->devCnt; ++i)
  661. {
  662. const cmMpDev_t* d = p->devArray + i;
  663. cmRptPrintf(rpt,"%i : Device: %s \n",i,cmStringNullGuard(d->nameStr));
  664. if(d->iPortCnt > 0 )
  665. cmRptPrintf(rpt," Input:\n");
  666. for(j=0; j<d->iPortCnt; ++j)
  667. _cmMpReportPort(rpt,d->iPortArray+j);
  668. if(d->oPortCnt > 0 )
  669. cmRptPrintf(rpt," Output:\n");
  670. for(j=0; j<d->oPortCnt; ++j)
  671. _cmMpReportPort(rpt,d->oPortArray+j);
  672. }
  673. }