Browse Source

cmMidiAlsa.c: Implemented cmMidiPort() based on ALSA.

master
kevin 11 years ago
parent
commit
7adaa96d90
1 changed files with 779 additions and 44 deletions
  1. 779
    44
      linux/cmMidiAlsa.c

+ 779
- 44
linux/cmMidiAlsa.c View File

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

Loading…
Cancel
Save