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

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