libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmMidiAlsa.c 26KB

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