1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //  $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $
5 //  (C) Copyright 2000-2001 Werner Schweer (ws@seh.de)
6 //  (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge)
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; version 2 of
11 //  the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 //=========================================================
23 
24 #include "alsamidi.h"
25 
26 #ifdef ALSA_SUPPORT
27 
28 #include <stdio.h>
29 
30 #include "globals.h"
31 #include "midi_consts.h"
32 #include "../midiport.h"
33 #include "../midiseq.h"
34 #include "../midictrl.h"
35 #include "../audio.h"
36 #include "minstrument.h"
37 #include "utils.h"
38 #include "helper.h"
39 #include "audiodev.h"
40 #include "xml.h"
41 #include "part.h"
42 #include "gconfig.h"
43 #include "track.h"
44 #include "song.h"
45 #include "muse_atomic.h"
46 #include "lock_free_buffer.h"
47 #include "evdata.h"
48 
49 #include <QApplication>
50 
51 // Enable debugging:
52 //#define ALSA_DEBUG 1
53 
54 // For debugging output: Uncomment the fprintf section.
55 #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args);
56 
57 namespace MusECore {
58 muse_atomic_t atomicAlsaMidiScanPending;
59 
60 static int alsaSeqFdi = -1;
61 static int alsaSeqFdo = -1;
62 
63 snd_seq_t* alsaSeq = 0;
64 static snd_seq_addr_t musePort;
65 static snd_seq_addr_t announce_adr;
66 
67 //---------------------------------------------------------
68 //   createAlsaMidiDevice
69 //   If name parameter is blank, creates a new (locally) unique one.
70 //---------------------------------------------------------
71 
createAlsaMidiDevice(QString name,int rwflags)72 MidiDevice* MidiAlsaDevice::createAlsaMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable 3: Writable + Readable
73 {
74   int ni = 0;
75   if(name.isEmpty())
76   {
77     for( ; ni < 65536; ++ni)
78     {
79       name = QString("alsa-midi-") + QString::number(ni);
80       if(!MusEGlobal::midiDevices.find(name))
81         break;
82     }
83   }
84   if(ni >= 65536)
85   {
86     fprintf(stderr, "MusE: createAlsaMidiDevice failed! Can't find an unused midi device name 'alsa-midi-[0-65535]'.\n");
87     return 0;
88   }
89 
90   snd_seq_addr_t a;
91 
92   // From seq.h: "Special client (port) ids SND_SEQ_ADDRESS_UNKNOWN 253 = unknown source"
93   // Hopefully we can use that as a 'valid' marker here. We can't use zero.
94   a.client = SND_SEQ_ADDRESS_UNKNOWN;
95   a.port = SND_SEQ_ADDRESS_UNKNOWN;
96 
97   MidiAlsaDevice* dev = new MidiAlsaDevice(a, name);
98 
99   dev->setrwFlags(rwflags);
100   MusEGlobal::midiDevices.add(dev);
101   return dev;
102 }
103 
104 
105 //---------------------------------------------------------
106 //   MidiAlsaDevice
107 //---------------------------------------------------------
108 
MidiAlsaDevice(const snd_seq_addr_t & a,const QString & n)109 MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n)
110    : MidiDevice(n)
111       {
112 //       _playEventFifo = new LockFreeBuffer<MidiPlayEvent>(8192);
113       adr = a;
114       }
115 
~MidiAlsaDevice()116 MidiAlsaDevice::~MidiAlsaDevice()
117 {
118 //   if(_playEventFifo)
119 //     delete _playEventFifo;
120 }
121 
122 //---------------------------------------------------------
123 //   selectWfd
124 //---------------------------------------------------------
125 
selectWfd()126 int MidiAlsaDevice::selectWfd()
127       {
128       return alsaSeqFdo;
129       }
130 
131 //---------------------------------------------------------
132 //   open
133 //---------------------------------------------------------
134 
open()135 QString MidiAlsaDevice::open()
136 {
137       _openFlags &= _rwFlags; // restrict to available bits
138 
139       if(!alsaSeq)
140       {
141         _state = QString("Unavailable");
142         return _state;
143       }
144 
145       snd_seq_port_info_t *pinfo = NULL;
146       snd_seq_port_subscribe_t* subs = NULL;
147 
148       DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Getting port info: address: %d:%d\n", adr.client, adr.port);
149       if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN)
150       {
151         snd_seq_port_info_alloca(&pinfo);
152         int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo);
153         if(rv < 0)
154         {
155           fprintf(stderr, "MidiAlsaDevice::open Error getting port info: address: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv));
156           _state = QString(snd_strerror(rv));
157           return _state;
158         }
159         DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open: address: %d:%d\n", adr.client, adr.port);
160         // Allocated on stack, no need to call snd_seq_port_subscribe_free() later.
161         snd_seq_port_subscribe_alloca(&subs);
162       }
163 
164       QString estr;
165       int wer = 0;
166       int rer = 0;
167 
168       if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN)
169       {
170 
171         int cap = snd_seq_port_info_get_capability(pinfo);
172 
173 #ifdef ALSA_DEBUG
174         fprintf(stderr, "MidiAlsaDevice::open cap:%d\n", cap);
175 #endif
176 
177         // subscribe for writing
178         if (_openFlags & 1)
179         {
180               if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE)
181               {
182                 snd_seq_port_subscribe_set_sender(subs, &musePort);
183                 snd_seq_port_subscribe_set_dest(subs, &adr);
184                 DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking write subscription: address: %d:%d\n", adr.client, adr.port);
185                 // Not already subscribed (or error)? Then try subscribing.
186                 if(snd_seq_get_port_subscription(alsaSeq, subs) < 0)
187                 {
188                   //int error = snd_seq_subscribe_port(alsaSeq, subs);
189                   wer = snd_seq_subscribe_port(alsaSeq, subs);
190                   //if (error < 0)
191                   if(wer < 0)
192                         //return QString("Play: ")+QString(snd_strerror(error));
193                         estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" "));
194                 }
195               }
196               if(!wer && (cap & SND_SEQ_PORT_CAP_WRITE))
197                 _writeEnable = true;
198         }
199 
200         // subscribe for reading
201         if (_openFlags & 2)
202         {
203               if(cap & SND_SEQ_PORT_CAP_SUBS_READ)
204               {
205                 snd_seq_port_subscribe_set_dest(subs, &musePort);
206                       snd_seq_port_subscribe_set_sender(subs, &adr);
207                 DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking read subscription: address: %d:%d\n", adr.client, adr.port);
208                 // Not already subscribed (or error)? Then try subscribing.
209                 if(snd_seq_get_port_subscription(alsaSeq, subs) < 0)
210                 {
211                   //int error = snd_seq_subscribe_port(alsaSeq, subs);
212                   rer = snd_seq_subscribe_port(alsaSeq, subs);
213                   //if (error < 0)
214                   if(rer < 0)
215                         //return QString("Rec: ") + QString(snd_strerror(error));
216                         estr += (QString("Rec: ") + QString(snd_strerror(rer)));
217                 }
218               }
219               if(!rer && (cap & SND_SEQ_PORT_CAP_READ))
220                 _readEnable = true;
221         }
222       }
223       else
224       {
225         _state = QString("Unavailable");
226         return _state;
227       }
228 
229       if(wer < 0 || rer < 0)
230       {
231         _state = estr;
232         return _state;
233       }
234 
235       _state = QString("OK");
236       return _state;
237 }
238 
239 //---------------------------------------------------------
240 //   close
241 //---------------------------------------------------------
242 
close()243 void MidiAlsaDevice::close()
244 {
245       if(!alsaSeq)
246       {
247         _state = QString("Unavailable");
248         return;
249       }
250 
251       snd_seq_port_info_t *pinfo;
252       snd_seq_port_subscribe_t* subs;
253       if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN)
254       {
255 
256         snd_seq_port_info_alloca(&pinfo);
257         int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo);
258         if(rv < 0)
259         {
260           fprintf(stderr, "MidiAlsaDevice::close Error getting port info: adr: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv));
261           _state = QString("Error on close");
262           return;
263         }
264         // Allocated on stack, no need to call snd_seq_port_subscribe_free() later.
265         snd_seq_port_subscribe_alloca(&subs);
266       }
267 
268       if(adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN)
269       {
270         _readEnable = false;
271         _writeEnable = false;
272         _state = QString("Unavailable");
273       }
274       else
275 
276       {
277         int wer = 0;
278         int rer = 0;
279 
280         int cap = snd_seq_port_info_get_capability(pinfo);
281 
282 #ifdef ALSA_DEBUG
283         fprintf(stderr, "MidiAlsaDevice::close cap:%d\n", cap);
284 #endif
285 
286         // This function appears to be called only by MidiPort::setMidiDevice(),
287         //  which closes then opens the device.
288         // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags.
289         //
290         // NOTE: Tested: The read unsubscribe works ok but not the write.
291         //               As viewed in say, qjackctl, the connection is clearly lost,
292         //                but strangely the events are still accepted, ie, playback notes
293         //                are still heard etc. Tried an alsa midi device AND external fluidsynth inst.
294         //
295         //               Also, jack running and with jack midi disabled, we get messages like
296         //                MidiAlsaDevice::0x84512c0 putEvent(): midi write error: No such device
297         //                 dst 16:0
298         //                only sometimes (not when playing notes), but with jack midi turned on,
299         //                we don't get the messages. With jack stopped we get the messages
300         //                no matter if jack midi is turned on or not.   Tim.
301 
302         //if (_openFlags & 1) {
303         //if (!(_openFlags & 1))
304         {
305               if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE)
306               {
307                 snd_seq_port_subscribe_set_sender(subs, &musePort);
308                 snd_seq_port_subscribe_set_dest(subs, &adr);
309 
310                 // Already subscribed? Then unsubscribe.
311                 if(!snd_seq_get_port_subscription(alsaSeq, subs))
312                 {
313                   wer = snd_seq_unsubscribe_port(alsaSeq, subs);
314                   //if(!wer)
315                   //  _writeEnable = false;
316                   //else
317                   if(wer < 0)
318                     fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for writing: %s\n", adr.client, adr.port, snd_strerror(wer));
319                 }
320                 //else
321                   //_writeEnable = false;
322               }
323               _writeEnable = false;
324         }
325 
326         //if (_openFlags & 2) {
327         //if (!(_openFlags & 2))
328         {
329               if(cap & SND_SEQ_PORT_CAP_SUBS_READ)
330               {
331                 snd_seq_port_subscribe_set_dest(subs, &musePort);
332                 snd_seq_port_subscribe_set_sender(subs, &adr);
333 
334                 // Already subscribed? Then unsubscribe.
335                 if(!snd_seq_get_port_subscription(alsaSeq, subs))
336                 {
337                   rer = snd_seq_unsubscribe_port(alsaSeq, subs);
338                   //if(!rer)
339                   //  _readEnable = false;
340                   //else
341                   if(rer < 0)
342                     fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for reading: %s\n", adr.client, adr.port, snd_strerror(rer));
343                 }
344                 //else
345                 //  _readEnable = false;
346               }
347               _readEnable = false;
348         }
349         _state = QString("Closed");
350       }
351 }
352 
353 //---------------------------------------------------------
354 //   writeRouting
355 //---------------------------------------------------------
356 
writeRouting(int level,Xml & xml) const357 void MidiAlsaDevice::writeRouting(int level, Xml& xml) const
358 {
359       // If this device is not actually in use by the song, do not write any routes.
360       // This prevents bogus routes from being saved and propagated in the med file.  Tim.
361       if(midiPort() == -1)
362         return;
363 
364       QString s;
365       for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r)
366       {
367         if((r->type == Route::TRACK_ROUTE && r->track) || (r->type != Route::TRACK_ROUTE && !r->name().isEmpty()))
368         {
369           s = "Route";
370           if(r->channel != -1)
371             s += QString(" channel=\"%1\"").arg(r->channel);
372           xml.tag(level++, s.toLatin1().constData());
373           xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData());
374           s = "dest";
375           if(r->type == Route::MIDI_DEVICE_ROUTE)
376             s += QString(" devtype=\"%1\" name=\"%2\"/").arg(r->device->deviceType()).arg(Xml::xmlString(r->name()));
377           else if(r->type == Route::TRACK_ROUTE)
378             s += QString(" track=\"%1\"").arg(MusEGlobal::song->tracks()->index(r->track));
379           else
380             s += QString(" type=\"%1\" name=\"%2\"/").arg(r->type).arg(Xml::xmlString(r->name()));
381           xml.tag(level, s.toLatin1().constData());
382           xml.etag(level--, "Route");
383         }
384       }
385 }
386 
387 //---------------------------------------------------------
388 //   putAlsaEvent
389 //    return false if event is delivered
390 //---------------------------------------------------------
391 
putAlsaEvent(snd_seq_event_t * event)392 bool MidiAlsaDevice::putAlsaEvent(snd_seq_event_t* event)
393       {
394       if (MusEGlobal::midiOutputTrace) {
395             fprintf(stderr, "ALSA MidiOut driver: <%s>: ", name().toLatin1().constData());
396             dump(event);
397             }
398 
399       if(!_writeEnable || !alsaSeq || adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN)
400         return true;
401 
402       int error;
403 
404 #ifdef ALSA_DEBUG
405       fprintf(stderr, "MidiAlsaDevice::putAlsaEvent\n");
406 #endif
407 
408       do {
409             error   = snd_seq_event_output_direct(alsaSeq, event);
410             int len = snd_seq_event_length(event);
411             if (error == len) {
412 //                  printf(".");fflush(stdout);
413                   return false;
414                   }
415             if (error < 0) {
416                   if (error == -12) {
417                         return true;
418                         }
419                   else {
420                         fprintf(stderr, "MidiAlsaDevice::%p putAlsaEvent(): midi write error: %s\n",
421                            this, snd_strerror(error));
422                         fprintf(stderr, "  dst %d:%d\n", adr.client, adr.port);
423                         //exit(-1);
424                         }
425                   }
426             else
427                   fprintf(stderr, "MidiAlsaDevice::putAlsaEvent(): midi write returns %d, expected %d: %s\n",
428                      error, len, snd_strerror(error));
429             } while (error == -12);
430       return true;
431       }
432 
433 //---------------------------------------------------------
434 //    processEvent
435 //    return false if event is delivered
436 //---------------------------------------------------------
437 
processEvent(const MidiPlayEvent & ev)438 bool MidiAlsaDevice::processEvent(const MidiPlayEvent& ev)
439 {
440       if (MusEGlobal::midiOutputTrace) {
441             fprintf(stderr, "ALSA MidiOut pre-driver: <%s>: ", name().toLatin1().constData());
442             dumpMPEvent(&ev);
443             }
444 
445       int chn = ev.channel();
446       int a   = ev.dataA();
447       int b   = ev.dataB();
448 
449       snd_seq_event_t event;
450       snd_seq_ev_clear(&event);
451 
452       event.queue   = SND_SEQ_QUEUE_DIRECT;
453       event.source  = musePort;
454       event.dest    = adr;
455 
456       MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; // Default to NoteOffAll in case of no port.
457       const int mport = midiPort();
458       if(mport != -1)
459       {
460         if(MidiInstrument* mi = MusEGlobal::midiPorts[mport].instrument())
461           nom = mi->noteOffMode();
462       }
463 
464       switch(ev.type())
465       {
466         case ME_NOTEON:
467 
468               if(b == 0)
469               {
470                 // Handle zero-velocity note ons. Technically this is an error because internal midi paths
471                 //  are now all 'note-off' without zero-vel note ons - they're converted to note offs.
472                 // Nothing should be setting a Note type Event's on velocity to zero.
473                 // But just in case... If we get this warning, it means there is still code to change.
474                 fprintf(stderr, "MidiAlsaDevice::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n",
475                         ev.time(), ev.type(), chn, a, b);
476                 switch(nom)
477                 {
478                   // Instrument uses note offs. Convert to zero-vel note off.
479                   case MidiInstrument::NoteOffAll:
480                     if(MusEGlobal::midiOutputTrace)
481                       fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note off:\n");
482                     snd_seq_ev_set_noteoff(&event, chn, a, 0);
483                   break;
484 
485                   // Instrument uses no note offs at all. Send as-is.
486                   case MidiInstrument::NoteOffNone:
487                   // Instrument converts all note offs to zero-vel note ons. Send as-is.
488                   case MidiInstrument::NoteOffConvertToZVNoteOn:
489                     snd_seq_ev_set_noteon(&event, chn, a, b);
490                   break;
491                 }
492               }
493               else
494 
495                 snd_seq_ev_set_noteon(&event, chn, a, b);
496               break;
497         case ME_NOTEOFF:
498 
499               switch(nom)
500               {
501                 // Instrument uses note offs. Send as-is.
502                 case MidiInstrument::NoteOffAll:
503                   snd_seq_ev_set_noteoff(&event, chn, a, b);
504                 break;
505 
506                 // Instrument uses no note offs at all. Send nothing. Eat up the event - return false.
507                 case MidiInstrument::NoteOffNone:
508                   return false;
509 
510                 // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on.
511                 case MidiInstrument::NoteOffConvertToZVNoteOn:
512                   if(MusEGlobal::midiOutputTrace)
513                     fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note on:\n");
514                   snd_seq_ev_set_noteon(&event, chn, a, 0);
515                 break;
516               }
517               break;
518         case ME_PROGRAM:
519               {
520                 _curOutParamNums[chn].resetParamNums();  // Probably best to reset.
521                 _curOutParamNums[chn].setPROG(a);
522                 snd_seq_ev_set_pgmchange(&event, chn, a);
523               }
524               break;
525         case ME_PITCHBEND:
526               snd_seq_ev_set_pitchbend(&event, chn, a);
527               break;
528         case ME_POLYAFTER:
529               snd_seq_ev_set_keypress(&event, chn, a, b);
530               break;
531         case ME_AFTERTOUCH:
532               snd_seq_ev_set_chanpress(&event, chn, a);
533               break;
534         case ME_SYSEX:
535               {
536                 // Probably best to reset all.
537                 resetCurOutParamNums();
538 
539                 // Stage the event data - is it OK to proceed?
540                 const size_t len = sysExOutProcessor()->stageEvData(ev.eventData(), ev.time());
541                 if(len > 0)
542                 {
543                   unsigned char buf[len];
544                   if(sysExOutProcessor()->getCurChunk(buf, MusEGlobal::sampleRate))
545                   {
546                     snd_seq_ev_set_sysex(&event, len, buf);
547                     // NOTE: Don't move this out, 'buf' would go out of scope.
548                     return putAlsaEvent(&event);
549                   }
550                 }
551                 return true;
552               }
553               break;
554         case ME_SONGPOS:
555               event.data.control.value = a;
556               event.type = SND_SEQ_EVENT_SONGPOS;
557               break;
558         case ME_CLOCK:
559               event.type = SND_SEQ_EVENT_CLOCK;
560               break;
561         case ME_START:
562               event.type = SND_SEQ_EVENT_START;
563               break;
564         case ME_CONTINUE:
565               event.type = SND_SEQ_EVENT_CONTINUE;
566               break;
567         case ME_STOP:
568               event.type = SND_SEQ_EVENT_STOP;
569               break;
570         case ME_CONTROLLER:
571         {
572             int a = ev.dataA();
573             int b = ev.dataB();
574             int chn = ev.channel();
575 
576             if(a == CTRL_PITCH)
577               snd_seq_ev_set_pitchbend(&event, chn, b);
578             else if((a | 0xff) == CTRL_POLYAFTER)
579               snd_seq_ev_set_keypress(&event, chn, a & 0x7f, b & 0x7f);
580             else if(a == CTRL_AFTERTOUCH)
581               snd_seq_ev_set_chanpress(&event, chn, b);
582             else if(a == CTRL_PROGRAM) {
583                         _curOutParamNums[chn].resetParamNums();  // Probably best to reset.
584                         int hb = (b >> 16) & 0xff;
585                         int lb = (b >> 8) & 0xff;
586                         int pr = b & 0xff;
587                         _curOutParamNums[chn].setCurrentProg(pr, lb, hb);
588                         if(hb != 0xff)
589                         {
590                           snd_seq_ev_set_controller(&event, chn, CTRL_HBANK, hb);
591                           if(putAlsaEvent(&event))
592                             return true;
593                         }
594                         if(lb != 0xff)
595                         {
596                           snd_seq_ev_set_controller(&event, chn, CTRL_LBANK, lb);
597                           if(putAlsaEvent(&event))
598                             return true;
599                         }
600                         if(pr != 0xff)
601                         {
602                           snd_seq_ev_set_pgmchange(&event, chn, pr);
603                           if(putAlsaEvent(&event))
604                             return true;
605                         }
606                         return false;
607                   }
608 
609 // Set this to 1 if ALSA cannot handle RPN NRPN etc.
610 // NOTE: Although ideally it should be 0, there are problems with
611 //        letting ALSA do the 'composition' of the messages in putMidiEvent() -
612 //        chiefly that ALSA does not handle 7-bit (N)RPN controllers.
613 //       This define is kept because it is important to understand, try, and see
614 //        the difference between the two techniques, and possibly make it work...
615 //       Also see the corresponding define in MidiAlsaDevice::putMidiEvent().
616 #if 0
617                   snd_seq_ev_set_controller(&event, chn, a, b);
618 #else
619 
620             else if (a < CTRL_14_OFFSET) {          // 7 Bit Controller
621                   if(a == CTRL_HRPN)
622                     _curOutParamNums[chn].setRPNH(b);
623                   else if(a == CTRL_LRPN)
624                     _curOutParamNums[chn].setRPNL(b);
625                   else if(a == CTRL_HNRPN)
626                     _curOutParamNums[chn].setNRPNH(b);
627                   else if(a == CTRL_LNRPN)
628                     _curOutParamNums[chn].setNRPNL(b);
629                   else if(a == CTRL_HBANK)
630                   {
631                     _curOutParamNums[chn].setBANKH(b);
632                     _curOutParamNums[chn].resetParamNums();  // Probably best to reset.
633                   }
634                   else if(a == CTRL_LBANK)
635                   {
636                     _curOutParamNums[chn].setBANKL(b);
637                     _curOutParamNums[chn].resetParamNums();  // Probably best to reset.
638                   }
639                   else if(a == CTRL_RESET_ALL_CTRL)
640                     _curOutParamNums[chn].resetParamNums();  // Probably best to reset.
641 
642                   snd_seq_ev_set_controller(&event, chn, a, b);
643                   }
644             else if (a < CTRL_RPN_OFFSET) {     // 14 bit high resolution controller
645                   int ctrlH = (a >> 8) & 0x7f;
646                   int ctrlL = a & 0x7f;
647 #if 0
648                   int dataH = (b >> 7) & 0x7f;
649                   int dataL = b & 0x7f;
650                   snd_seq_ev_set_controller(&event, chn, ctrlH, dataH);
651                   if(putAlsaEvent(&event))
652                     return true;
653                   snd_seq_ev_set_controller(&event, chn, ctrlL, dataL);
654                   return putAlsaEvent(&event);
655 #else
656                   snd_seq_event_t ev;
657                   snd_seq_ev_clear(&ev);
658                   ev.queue   = SND_SEQ_QUEUE_DIRECT;
659                   ev.source  = musePort;
660                   ev.dest    = adr;
661                   int n = (ctrlH << 7) + ctrlL;
662                   snd_seq_ev_set_controller(&ev, chn, n, b);
663                   ev.type = SND_SEQ_EVENT_CONTROL14;
664                   return putAlsaEvent(&ev);
665 #endif
666 
667                   }
668             else if (a < CTRL_NRPN_OFFSET) {     // RPN 7-Bit Controller
669                   int ctrlH = (a >> 8) & 0x7f;
670                   int ctrlL = a & 0x7f;
671                   int data = b & 0x7f;
672                   if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers)
673                   {
674                     _curOutParamNums[chn].setRPNL(ctrlL);
675                     snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL);
676                     if(putAlsaEvent(&event))
677                       return true;
678                   }
679                   if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers)
680                   {
681                     _curOutParamNums[chn].setRPNH(ctrlH);
682                     snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH);
683                     if(putAlsaEvent(&event))
684                       return true;
685                   }
686                   if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
687                   {
688                     _curOutParamNums[chn].setDATAH(data);
689                     snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data);
690                     if(putAlsaEvent(&event))
691                       return true;
692                   }
693 
694                   // Select null parameters so that subsequent data controller
695                   //  events do not upset the last *RPN controller.  Tim.
696                   if(MusEGlobal::config.midiSendNullParameters)
697                   {
698                     _curOutParamNums[chn].setRPNH(0x7f);
699                     snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f);
700                     if(putAlsaEvent(&event))
701                       return true;
702 
703                     _curOutParamNums[chn].setRPNL(0x7f);
704                     snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f);
705                     if(putAlsaEvent(&event))
706                       return true;
707                   }
708                   return false;
709                 }
710             else if (a < CTRL_INTERNAL_OFFSET) {     // NRPN 7-Bit Controller
711                   int ctrlH = (a >> 8) & 0x7f;
712                   int ctrlL = a & 0x7f;
713                   int data = b & 0x7f;
714                   if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers)
715                   {
716                     _curOutParamNums[chn].setNRPNL(ctrlL);
717                     snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL);
718                     if(putAlsaEvent(&event))
719                       return true;
720                   }
721                   if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers)
722                   {
723                     _curOutParamNums[chn].setNRPNH(ctrlH);
724                     snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH);
725                     if(putAlsaEvent(&event))
726                       return true;
727                   }
728                   if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
729                   {
730                     _curOutParamNums[chn].setDATAH(data);
731                     snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data);
732                     if(putAlsaEvent(&event))
733                       return true;
734                   }
735 
736                   if(MusEGlobal::config.midiSendNullParameters)
737                   {
738                     _curOutParamNums[chn].setNRPNH(0x7f);
739                     snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f);
740                     if(putAlsaEvent(&event))
741                       return true;
742 
743                     _curOutParamNums[chn].setNRPNL(0x7f);
744                     snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f);
745                     if(putAlsaEvent(&event))
746                       return true;
747                   }
748                   return false;
749                   }
750             else if (a < CTRL_RPN14_OFFSET)      // Unaccounted for internal controller
751                   return true;
752             else if (a < CTRL_NRPN14_OFFSET) {     // RPN14 Controller
753                   int ctrlH = (a >> 8) & 0x7f;
754                   int ctrlL = a & 0x7f;
755                   int dataH = (b >> 7) & 0x7f;
756                   int dataL = b & 0x7f;
757                   if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers)
758                   {
759                     _curOutParamNums[chn].setRPNL(ctrlL);
760                     snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL);
761                     if(putAlsaEvent(&event))
762                       return true;
763                   }
764                   if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers)
765                   {
766                     _curOutParamNums[chn].setRPNH(ctrlH);
767                     snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH);
768                     if(putAlsaEvent(&event))
769                       return true;
770                   }
771                   if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
772                   {
773                     _curOutParamNums[chn].setDATAH(dataH);
774                     snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH);
775                     if(putAlsaEvent(&event))
776                         return true;
777                   }
778                   if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers)
779                   {
780                     _curOutParamNums[chn].setDATAL(dataL);
781                     snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL);
782                     if(putAlsaEvent(&event))
783                         return true;
784                   }
785 
786                   if(MusEGlobal::config.midiSendNullParameters)
787                   {
788                     _curOutParamNums[chn].setRPNH(0x7f);
789                     snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f);
790                     if(putAlsaEvent(&event))
791                       return true;
792 
793                     _curOutParamNums[chn].setRPNL(0x7f);
794                     snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f);
795                     if(putAlsaEvent(&event))
796                       return true;
797                   }
798                   return false;
799                   }
800             else if (a < CTRL_NONE_OFFSET) {     // NRPN14 Controller
801                   int ctrlH = (a >> 8) & 0x7f;
802                   int ctrlL = a & 0x7f;
803 #if 0
804                   int dataH = (b >> 7) & 0x7f;
805                   int dataL = b & 0x7f;
806                   if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers)
807                   {
808                     _curOutParamNums[chn].setNRPNL(ctrlL);
809                     snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL);
810                     if(putAlsaEvent(&event))
811                       return true;
812                   }
813                   if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers)
814                   {
815                     _curOutParamNums[chn].setNRPNH(ctrlH);
816                     snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH);
817                     if(putAlsaEvent(&event))
818                       return true;
819                   }
820                   if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
821                   {
822                     _curOutParamNums[chn].setDATAH(dataH);
823                     snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH);
824                     if(putAlsaEvent(&event))
825                       return true;
826                   }
827                   if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers)
828                   {
829                     _curOutParamNums[chn].setDATAL(dataL);
830                     snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL);
831                     if(putAlsaEvent(&event))
832                       return true;
833                   }
834 
835 #else
836                   int n = (ctrlH << 7) + ctrlL;
837                   snd_seq_ev_set_controller(&event, chn, n, b);
838                   event.type = SND_SEQ_EVENT_NONREGPARAM;
839                   if(putAlsaEvent(&event))
840                     return true;
841 #endif
842 
843                   if(MusEGlobal::config.midiSendNullParameters)
844                   {
845                     _curOutParamNums[chn].setNRPNH(0x7f);
846                     snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f);
847                     if(putAlsaEvent(&event))
848                       return true;
849 
850                     _curOutParamNums[chn].setNRPNL(0x7f);
851                     snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f);
852                     if(putAlsaEvent(&event))
853                       return true;
854                   }
855                   return false;
856                   }
857             else {
858                   fprintf(stderr, "MidiAlsaDevice::processEvent: unknown controller type 0x%x\n", a);
859                   return true;
860                   }
861 #endif
862         }
863         break;  // ME_CONTROLLER
864 
865             default:
866                   if(MusEGlobal::debugMsg)
867                     fprintf(stderr, "MidiAlsaDevice::processEvent(): event type %d not implemented\n", ev.type());
868                   return true;
869             }
870 
871       return putAlsaEvent(&event);
872 }
873 
874 //---------------------------------------------------------
875 //   processMidi
876 //   Called from ALSA midi sequencer thread only.
877 //---------------------------------------------------------
878 
processMidi(unsigned int curFrame)879 void MidiAlsaDevice::processMidi(unsigned int curFrame)
880 {
881   // Get the state of the stop flag.
882   const bool do_stop = stopFlag();
883 
884   //--------------------------------------------------------------------------------
885   // For now we stop ALL ring buffer processing until any sysex transmission is finished.
886   // TODO FIXME Some realtime events ARE allowed while sending a sysex. Let that happen below...
887   //--------------------------------------------------------------------------------
888 
889   SysExOutputProcessor* sop = sysExOutProcessor();
890   switch(sop->state())
891   {
892     case SysExOutputProcessor::Clear:
893       // Proceed with normal ring buffer processing, below...
894     break;
895 
896     case SysExOutputProcessor::Sending:
897     {
898       // Current chunk is meant for a future cycle?
899       if(sop->curChunkFrame() > curFrame)
900         break;
901 
902       const size_t len = sop->curChunkSize();
903       if(len > 0)
904       {
905         unsigned char buf[len];
906         if(sop->getCurChunk(buf, MusEGlobal::sampleRate))
907         {
908           snd_seq_event_t event;
909           snd_seq_ev_clear(&event);
910           event.queue   = SND_SEQ_QUEUE_DIRECT;
911           event.source  = musePort;
912           event.dest    = adr;
913           snd_seq_ev_set_sysex(&event, len, buf);
914           putAlsaEvent(&event);
915         }
916       }
917       else
918       {
919         fprintf(stderr, "Error: MidiAlsaDevice::processMidi(): curChunkSize is zero while state is Sending\n");
920         // Should not happen. Protection against accidental zero chunk size while sending.
921         // Let's just clear the thing.
922         sop->clear();
923       }
924     }
925     break;
926 
927     case SysExOutputProcessor::Finished:
928     {
929       // Wait for the last chunk to transmit.
930       if(sop->curChunkFrame() > curFrame)
931         break;
932       // Now we are truly done. Clear or reset the processor, which
933       //  sets the state to Clear. Prefer reset for speed but clear is OK,
934       //  the EvData reference will already have been released.
935       //sop->reset();
936       sop->clear();
937     }
938     break;
939   }
940 
941   MidiPlayEvent buf_ev;
942 
943   // Transfer the user lock-free buffer events to the user sorted multi-set.
944   // False = don't use the size snapshot, but update it.
945   const unsigned int usr_buf_sz = eventBuffers(UserBuffer)->getSize(false);
946   for(unsigned int i = 0; i < usr_buf_sz; ++i)
947   {
948     if(eventBuffers(UserBuffer)->get(buf_ev))
949       _outUserEvents.insert(buf_ev);
950   }
951 
952   // Transfer the playback lock-free buffer events to the playback sorted multi-set.
953   const unsigned int pb_buf_sz = eventBuffers(PlaybackBuffer)->getSize(false);
954   for(unsigned int i = 0; i < pb_buf_sz; ++i)
955   {
956     // Are we stopping? Just remove the item.
957     if(do_stop)
958       eventBuffers(PlaybackBuffer)->remove();
959     // Otherwise get the item.
960     else if(eventBuffers(PlaybackBuffer)->get(buf_ev))
961       _outPlaybackEvents.insert(buf_ev);
962   }
963 
964   // Are we stopping?
965   if(do_stop)
966   {
967     // Transport has stopped, purge ALL further scheduled playback events now.
968     _outPlaybackEvents.clear();
969     // Reset the flag.
970     setStopFlag(false);
971   }
972 
973   iMPEvent impe_pb = _outPlaybackEvents.begin();
974   iMPEvent impe_us = _outUserEvents.begin();
975   bool using_pb;
976 
977   while(1)
978   {
979     if(impe_pb != _outPlaybackEvents.end() && impe_us != _outUserEvents.end())
980       using_pb = *impe_pb < *impe_us;
981     else if(impe_pb != _outPlaybackEvents.end())
982       using_pb = true;
983     else if(impe_us != _outUserEvents.end())
984       using_pb = false;
985     else break;
986 
987     const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us;
988 
989     #ifdef ALSA_DEBUG
990     fprintf(stderr, "INFO: MidiAlsaDevice::processMidi() evTime:%u curFrame:%u\n", e.time(), curFrame);
991     #endif
992 
993     // Event is meant for next cycle?
994     if(e.time() > curFrame)
995     {
996 #ifdef ALSA_DEBUG
997       fprintf(stderr, " alsa play event is for future:%lu, breaking loop now\n", e.time());
998 #endif
999       break;
1000     }
1001 
1002     // Is the sysex processor not in a Clear state?
1003     // Only realtime events are allowed to be sent while a sysex is in progress.
1004     // The delayed events list is pre-allocated with reserve().
1005     // According to http://en.cppreference.com/w/cpp/container/vector/clear,
1006     //  clear() shall not change the capacity. That statement references here:
1007     // https://stackoverflow.com/questions/18467624/what-does-the-standard-say-
1008     //  about-how-calling-clear-on-a-vector-changes-the-capac/18467916#18467916
1009     // But http://www.cplusplus.com/reference/vector/vector/clear
1010     //  has not been updated to clarify this situation.
1011     if(sop->state() != SysExOutputProcessor::Clear)
1012     {
1013       // Is it a realtime message?
1014       if(e.type() >= 0xf8 && e.type() <= 0xff)
1015         // Process it now.
1016         processEvent(e);
1017       else
1018         // Store it for later.
1019         _sysExOutDelayedEvents->push_back(e);
1020     }
1021     else
1022     {
1023       // Process any delayed events.
1024       const unsigned int sz = _sysExOutDelayedEvents->size();
1025       for(unsigned int i = 0; i < sz; ++i)
1026         processEvent(_sysExOutDelayedEvents->at(i));
1027 
1028       // Let's check that capacity out of curiosity...
1029       const unsigned int cap = _sysExOutDelayedEvents->capacity();
1030       // Done with the delayed event list. Clear it.
1031       _sysExOutDelayedEvents->clear();
1032 
1033       // Throw up a developer warning if things are fishy.
1034       if(_sysExOutDelayedEvents->capacity() != cap)
1035         fprintf(stderr, "WARNING: MidiAlsaDevice::processMidi() delayed events vector "
1036                 "capacity:%u is not the same as before clear:%u\n",
1037                 (unsigned int)_sysExOutDelayedEvents->capacity(), cap);
1038 
1039       // If processEvent fails, although we would like to not miss events by keeping them
1040       //  until next cycle and trying again, that can lead to a large backup of events
1041       //  over a long time. So we'll just... miss them.
1042       processEvent(e);
1043     }
1044 
1045     // Successfully processed event. Remove it from FIFO.
1046     // C++11.
1047     if(using_pb)
1048       impe_pb = _outPlaybackEvents.erase(impe_pb);
1049     else
1050       impe_us = _outUserEvents.erase(impe_us);
1051   }
1052 }
1053 
1054 //---------------------------------------------------------
1055 //   initMidiAlsa
1056 //    return true on error
1057 //---------------------------------------------------------
1058 
initMidiAlsa()1059 bool initMidiAlsa()
1060       {
1061       // The sequencer is required for ALSA midi. Initialize it if not already done.
1062       // But do not start the sequencer thread yet... the caller may want to do that a bit later.
1063       MusECore::initMidiSequencer();
1064 
1065       if(alsaSeq)
1066       {
1067         DEBUG_PRST_ROUTES(stderr, "initMidiAlsa: alsaSeq already initialized, ignoring\n");
1068         return false;
1069       }
1070 
1071       muse_atomic_init(&atomicAlsaMidiScanPending);
1072       muse_atomic_set(&atomicAlsaMidiScanPending, 0);
1073 
1074       if (MusEGlobal::debugMsg)
1075             fprintf(stderr, "initMidiAlsa\n");
1076       int error = snd_seq_open(&alsaSeq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
1077       if (error < 0) {
1078             fprintf(stderr, "Could not open ALSA sequencer: %s\n",
1079                snd_strerror(error));
1080             return true;
1081             }
1082 
1083       const int inCap  = SND_SEQ_PORT_CAP_SUBS_READ;
1084       const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE;
1085 
1086       snd_seq_client_info_t *cinfo;
1087       snd_seq_client_info_alloca(&cinfo);
1088       snd_seq_client_info_set_client(cinfo, -1);
1089 
1090       while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
1091             const char* cname = snd_seq_client_info_get_name(cinfo);
1092             //fprintf(stderr, "ALSA client name: %s\n", cname);
1093 
1094             // Put Midi Through and user clients after others. Insert other unwanted clients here:          // p4.0.41
1095             if(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || strcmp("Midi Through", cname) == 0)
1096               continue;
1097 
1098             snd_seq_port_info_t *pinfo;
1099             snd_seq_port_info_alloca(&pinfo);
1100             snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1101             snd_seq_port_info_set_port(pinfo, -1);
1102 
1103             while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
1104                   unsigned int capability = snd_seq_port_info_get_capability(pinfo);
1105                   if (capability & SND_SEQ_PORT_CAP_NO_EXPORT)  // Ignore ports like "qjackctl" or "port".    p4.0.41
1106                     continue;
1107                   if ((capability & outCap) == 0) {
1108                           const char *name = snd_seq_port_info_get_name(pinfo);
1109                           if (strcmp("Timer", name) == 0 ||
1110                               strcmp("Announce", name) == 0 ||
1111                               strcmp("Receiver", name) == 0)
1112                                 continue;
1113                           }
1114                   snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo);
1115 
1116                   const QString dev_name(snd_seq_port_info_get_name(pinfo));
1117                   MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI);
1118                   const bool dev_found = dev;
1119                   if(dev_found)
1120                   {
1121                     DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo));
1122                     // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device?
1123                     dev->setAddressClient(adr.client);
1124                     dev->setAddressPort(adr.port);
1125                     // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below).
1126                     //if(dev->midiPort() == -1)
1127                       dev->setState("Closed");
1128                   }
1129                   else
1130                     dev = new MidiAlsaDevice(adr, QString(dev_name));
1131                   //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo)));
1132                   int flags = 0;
1133                   if (capability & outCap)
1134                         flags |= 1;
1135                   if (capability & inCap)
1136                         flags |= 2;
1137                   dev->setrwFlags(flags);
1138                   if (MusEGlobal::debugMsg)
1139                         fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1140                            snd_seq_port_info_get_name(pinfo),
1141                            adr.client, adr.port,
1142                            flags, capability);
1143                   DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1144                            snd_seq_port_info_get_name(pinfo),
1145                            adr.client, adr.port,
1146                            flags, capability);
1147                   if(dev_found)
1148                   {
1149 //                     // The device should be closed right now. Open it if necessary,
1150 //                     //  which will also change the state to 'Open'.
1151 //                     if(dev->midiPort() != -1)
1152 //                       dev->open();
1153                   }
1154                   else
1155                     MusEGlobal::midiDevices.add(dev);
1156                   }
1157             }
1158 
1159       snd_seq_client_info_set_client(cinfo, -1);   // Reset
1160       while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
1161             const char* cname = snd_seq_client_info_get_name(cinfo);
1162             //fprintf(stderr, "ALSA client name: %s\n", cname);
1163 
1164             bool is_thru = (strcmp("Midi Through", cname) == 0);
1165             // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41
1166             if( !(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || is_thru) )
1167               continue;
1168 
1169             snd_seq_port_info_t *pinfo;
1170             snd_seq_port_info_alloca(&pinfo);
1171             snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1172             snd_seq_port_info_set_port(pinfo, -1);
1173 
1174             while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
1175                   unsigned int capability = snd_seq_port_info_get_capability(pinfo);
1176                   if (capability & SND_SEQ_PORT_CAP_NO_EXPORT)  // Ignore ports like "qjackctl" or "port".    p4.0.41
1177                     continue;
1178                   if ((capability & outCap) == 0) {
1179                           const char *name = snd_seq_port_info_get_name(pinfo);
1180                           if (strcmp("Timer", name) == 0 ||
1181                               strcmp("Announce", name) == 0 ||
1182                               strcmp("Receiver", name) == 0)
1183                                 continue;
1184                           }
1185                   snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo);
1186                   const QString dev_name(snd_seq_port_info_get_name(pinfo));
1187                   MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI);
1188                   const bool dev_found = dev;
1189                   if(dev_found)
1190                   {
1191                     DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo));
1192                     // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device?
1193                     dev->setAddressClient(adr.client);
1194                     dev->setAddressPort(adr.port);
1195                     // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below).
1196                     //if(dev->midiPort() == -1)
1197                       dev->setState("Closed");
1198                   }
1199                   else
1200                     dev = new MidiAlsaDevice(adr, dev_name);
1201                   //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo)));
1202                   int flags = 0;
1203                   if (capability & outCap)
1204                         flags |= 1;
1205                   if (capability & inCap)
1206                         flags |= 2;
1207                   dev->setrwFlags(flags);
1208                   if(is_thru)             // Don't auto-open Midi Through.
1209                     dev->setOpenFlags(0);
1210                   if (MusEGlobal::debugMsg)
1211                         fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1212                            snd_seq_port_info_get_name(pinfo),
1213                            adr.client, adr.port,
1214                            flags, capability);
1215                   DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1216                            snd_seq_port_info_get_name(pinfo),
1217                            adr.client, adr.port,
1218                            flags, capability);
1219                   if(dev_found)
1220                   {
1221 //                     // The device should be closed right now. Open it if necessary,
1222 //                     //  which will also change the state to 'Open'.
1223 //                     if(dev->midiPort() != -1)
1224 //                       dev->open();
1225                   }
1226                   else
1227                     MusEGlobal::midiDevices.add(dev);
1228                   }
1229             }
1230 
1231 
1232       //snd_seq_set_client_name(alsaSeq, "MusE Sequencer");
1233       error = snd_seq_set_client_name(alsaSeq, MusEGlobal::audioDevice->clientName());
1234       if (error < 0) {
1235             fprintf(stderr, "Alsa: Set client name failed: %s", snd_strerror(error));
1236             return true;
1237             }
1238 
1239       int ci = snd_seq_poll_descriptors_count(alsaSeq, POLLIN);
1240       int co = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT);
1241 
1242       if (ci > 1 || co > 1) {
1243             fprintf(stderr, "ALSA midi: cannot handle more than one poll fd\n");
1244             abort();
1245             }
1246 
1247       struct pollfd pfdi[ci];
1248       struct pollfd pfdo[co];
1249       snd_seq_poll_descriptors(alsaSeq, pfdi, ci, POLLIN);
1250       snd_seq_poll_descriptors(alsaSeq, pfdo, co, POLLOUT);
1251       alsaSeqFdo = pfdo[0].fd;
1252       alsaSeqFdi = pfdi[0].fd;
1253 
1254       int port  = snd_seq_create_simple_port(alsaSeq, "MusE Port 0",
1255          inCap | outCap | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE,
1256          SND_SEQ_PORT_TYPE_APPLICATION);
1257       if (port < 0) {
1258             perror("create port");
1259             exit(1);
1260             }
1261       musePort.port   = port;
1262       musePort.client = snd_seq_client_id(alsaSeq);
1263 
1264       //-----------------------------------------
1265       //    subscribe to "Announce"
1266       //    this enables callbacks for any
1267       //    alsa port changes
1268       //-----------------------------------------
1269 
1270       //snd_seq_addr_t aadr;
1271       announce_adr.client = SND_SEQ_CLIENT_SYSTEM;
1272       announce_adr.port   = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
1273 
1274       snd_seq_port_subscribe_t* subs;
1275       snd_seq_port_subscribe_alloca(&subs);
1276       snd_seq_port_subscribe_set_dest(subs, &musePort);
1277       snd_seq_port_subscribe_set_sender(subs, &announce_adr);
1278       error = snd_seq_subscribe_port(alsaSeq, subs);
1279       if (error < 0) {
1280             fprintf(stderr, "Alsa: Subscribe System failed: %s", snd_strerror(error));
1281             return true;
1282             }
1283 
1284 
1285       // The ALSA devices should be closed right now. Open them if necessary,
1286       //  which will also change their states to 'Open'.
1287       for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i)
1288       {
1289         MidiDevice* d = *i;
1290         switch(d->deviceType())
1291         {
1292           case MidiDevice::ALSA_MIDI:
1293             if(d->midiPort() != -1)
1294               d->open();
1295           break;
1296 
1297           case MidiDevice::JACK_MIDI:
1298           case MidiDevice::SYNTH_MIDI:
1299           break;
1300         }
1301       }
1302 
1303       return false;
1304       }
1305 
1306 
1307 //---------------------------------------------------------
1308 //   exitMidiAlsa
1309 //---------------------------------------------------------
1310 
exitMidiAlsa()1311 void exitMidiAlsa()
1312 {
1313   if(alsaSeq)
1314   {
1315     int error = 0;
1316     snd_seq_port_subscribe_t* subs;
1317     // Allocated on stack, no need to call snd_seq_port_subscribe_free() later.
1318     snd_seq_port_subscribe_alloca(&subs);
1319 
1320     //snd_seq_port_info_t *pinfo;
1321     //snd_seq_port_info_alloca(&pinfo);
1322     //snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1323     //snd_seq_port_info_set_addr(pinfo, &announce_adr);
1324 
1325     snd_seq_port_subscribe_set_dest(subs, &musePort);
1326     snd_seq_port_subscribe_set_sender(subs, &announce_adr);
1327 
1328     // Already subscribed? Then unsubscribe.
1329     if(!snd_seq_get_port_subscription(alsaSeq, subs))
1330     {
1331       error = snd_seq_unsubscribe_port(alsaSeq, subs);
1332       if(error < 0)
1333         fprintf(stderr, "MusE: exitMidiAlsa: Error unsubscribing alsa midi Announce port %d:%d for reading: %s\n", announce_adr.client, announce_adr.port, snd_strerror(error));
1334     }
1335 
1336     error = snd_seq_delete_simple_port(alsaSeq, musePort.port);
1337     if(error < 0)
1338       fprintf(stderr, "MusE: Could not delete ALSA simple port: %s\n", snd_strerror(error));
1339 
1340     error = snd_seq_close(alsaSeq);
1341     if(error < 0)
1342       fprintf(stderr, "MusE: Could not close ALSA sequencer: %s\n", snd_strerror(error));
1343 
1344     muse_atomic_destroy(&atomicAlsaMidiScanPending);
1345   }
1346   else
1347     fprintf(stderr, "initMidiAlsa: alsaSeq already exited, ignoring\n");
1348 
1349   alsaSeq = 0;
1350   // Be sure to call MusEGlobal::midiSeq->msgUpdatePollFd() or midiSeq->updatePollFd()
1351   //  for this to take effect.
1352   alsaSeqFdi = -1;
1353   alsaSeqFdo = -1;
1354 }
1355 
1356 
1357 //---------------------------------------------------------
1358 //   setAlsaClientName
1359 //---------------------------------------------------------
1360 
setAlsaClientName(const char * name)1361 void setAlsaClientName(const char* name)
1362 {
1363 #ifdef ALSA_DEBUG
1364   fprintf(stderr, "setAlsaClientName: %s  seq:%p\n", name, alsaSeq);
1365 #endif
1366 
1367   if(!alsaSeq)
1368     return;
1369 
1370   int error = snd_seq_set_client_name(alsaSeq, name);
1371   if (error < 0)
1372     fprintf(stderr, "setAlsaClientName: failed: %s", snd_strerror(error));
1373 }
1374 
1375 struct AlsaPort {
1376       snd_seq_addr_t adr;
1377       char* name;
1378       int flags;
AlsaPortMusECore::AlsaPort1379       AlsaPort(snd_seq_addr_t a, const char* s, int f) {
1380             adr = a;
1381             name = strdup(s);
1382             flags = f;
1383             }
1384       //~AlsaPort() { if(name) free(name); }
1385       };
1386 
1387 static std::list<AlsaPort> portList;
1388 
1389 //---------------------------------------------------------
1390 //   alsaScanMidiPorts
1391 //---------------------------------------------------------
1392 
alsaScanMidiPorts()1393 void alsaScanMidiPorts()
1394       {
1395 #ifdef ALSA_DEBUG
1396       fprintf(stderr, "alsa scan midi ports\n");
1397 #endif
1398       DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts\n");
1399 
1400       bool idling = false;
1401       portList.clear();
1402 
1403       if(!alsaSeq)
1404       {
1405         // Reset this now.
1406         muse_atomic_set(&atomicAlsaMidiScanPending, 0);
1407 
1408         // Check for devices to disable
1409         for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i)
1410         {
1411           MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i);
1412           if(d == 0)
1413                 continue;
1414 
1415           DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts stopped: disabling device:%p %s\n",
1416                   d, d->name().toLatin1().constData());
1417           if(!idling)
1418           {
1419             // Not much choice but to idle both the audio and midi threads since
1420             //  midi does not idle while audio messages are being processed.
1421             MusEGlobal::audio->msgIdle(true);
1422             idling = true;
1423           }
1424 
1425           //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress));
1426           d->adr.client = SND_SEQ_ADDRESS_UNKNOWN;
1427           d->adr.port = SND_SEQ_ADDRESS_UNKNOWN;
1428           // Close to reset some device members.
1429           d->close();
1430           // Update the port's state
1431           d->setState("Unavailable");
1432           if(d->midiPort() != -1)
1433             MusEGlobal::midiPorts[d->midiPort()].setState(d->state());
1434         }
1435 
1436         if(idling)
1437         {
1438           MusEGlobal::audio->msgIdle(false);
1439           // Update the GUI.
1440           MusEGlobal::song->update(SC_CONFIG);
1441         }
1442         return;
1443       }
1444 
1445       QString state;
1446       const int inCap  = SND_SEQ_PORT_CAP_SUBS_READ;
1447       const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE;
1448 
1449       snd_seq_client_info_t* cinfo;
1450       snd_seq_client_info_alloca(&cinfo);
1451       snd_seq_client_info_set_client(cinfo, 0);
1452 
1453       while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
1454             snd_seq_port_info_t *pinfo;
1455             snd_seq_port_info_alloca(&pinfo);
1456             snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1457             snd_seq_port_info_set_port(pinfo, -1);
1458             while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
1459                   unsigned int capability = snd_seq_port_info_get_capability(pinfo);
1460                   if (capability & SND_SEQ_PORT_CAP_NO_EXPORT)  // Ignore ports like "qjackctl" or "port".    p4.0.41
1461                     continue;
1462                   if (((capability & outCap) == 0)
1463                      && ((capability & inCap) == 0))
1464                         continue;
1465                   snd_seq_addr_t adr;
1466                   const char* name;
1467                   adr  = *snd_seq_port_info_get_addr(pinfo);
1468                   name = snd_seq_port_info_get_name(pinfo);
1469                   if (adr.client == musePort.client && adr.port == musePort.port)
1470                         continue;
1471                   int flags = 0;
1472                   if (capability & outCap)
1473                         flags |= 1;
1474                   if (capability & inCap)
1475                         flags |= 2;
1476 // fprintf(stderr, "ALSA port add: <%s>, flags %d\n", name, flags);
1477                   portList.push_back(AlsaPort(adr, name, flags));
1478                   }
1479             }
1480 
1481       // Reset this now.
1482       muse_atomic_set(&atomicAlsaMidiScanPending, 0);
1483 
1484       //
1485       //  check for devices to delete
1486       //
1487       for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) {
1488             MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i);
1489             if (d == 0)
1490                   continue;
1491 
1492             std::list<AlsaPort>::iterator k = portList.begin();
1493             for (; k != portList.end(); ++k) {
1494                   if (k->adr.client == d->adr.client
1495                      && k->adr.port == d->adr.port) {
1496                         break;
1497                         }
1498                   // Search by name if either of the client or port are 0.
1499                   if(strcmp(k->name, d->name().toLatin1().constData()) == 0 &&
1500                      ((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) ||
1501                       (d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == k->adr.port) ||
1502                       (d->adr.port == SND_SEQ_ADDRESS_UNKNOWN && d->adr.client == k->adr.client)))
1503                     break;
1504                   }
1505             if (k == portList.end()) {
1506                   DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts nulling adr op:ModifyMidiDeviceAddress device:%p %s\n",
1507                           d, d->name().toLatin1().constData());
1508                   if(!idling)
1509                   {
1510                     // Not much choice but to idle both the audio and midi threads since
1511                     //  midi does not idle while audio messages are being processed.
1512                     MusEGlobal::audio->msgIdle(true);
1513                     idling = true;
1514                   }
1515 
1516                   //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress));
1517                   d->adr.client = SND_SEQ_ADDRESS_UNKNOWN;
1518                   d->adr.port = SND_SEQ_ADDRESS_UNKNOWN;
1519                   // Close to reset some device members.
1520                   d->close();
1521                   // Update the port's state
1522                   d->setState("Unavailable");
1523                   if(d->midiPort() != -1)
1524                     MusEGlobal::midiPorts[d->midiPort()].setState(d->state());
1525                   }
1526             }
1527 
1528 
1529       //
1530       //  check for devices to add
1531       //
1532       // TODO: Possibly auto-add them to available midi ports.
1533       //
1534       for (std::list<AlsaPort>::iterator k = portList.begin(); k != portList.end(); ++k) {
1535             iMidiDevice i = MusEGlobal::midiDevices.begin();
1536             for (;i != MusEGlobal::midiDevices.end(); ++i) {
1537                   MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i);
1538                   if (d == 0)
1539                         continue;
1540                   DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts add: checking port:%s client:%d port:%d device:%p %s client:%d port:%d\n",
1541                           k->name, k->adr.client, k->adr.port, d, d->name().toLatin1().constData(), d->adr.client, d->adr.port);
1542                   if (k->adr.client == d->adr.client && k->adr.port == d->adr.port)
1543                         break;
1544 
1545                   if((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN || d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) && strcmp(k->name, d->name().toLatin1().constData()) == 0)
1546                   {
1547                     if(d->adr.client != SND_SEQ_ADDRESS_UNKNOWN && d->adr.client != k->adr.client)
1548                     {
1549                       DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.client:%u != k->adr.client:%u", k->name, d->adr.client, k->adr.client);
1550                       //continue;
1551                     }
1552                     if(d->adr.port != SND_SEQ_ADDRESS_UNKNOWN && d->adr.port != k->adr.port)
1553                     {
1554                       DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.port:%u != k->adr.port:%u", k->name, d->adr.port, k->adr.port);
1555                       //continue;
1556                     }
1557                     //if(d->adr.client == SND_SEQ_ADDRESS_UNKNOWN)
1558 //                       d->adr.client = k->adr.client;
1559                     //if(d->adr.port == SND_SEQ_ADDRESS_UNKNOWN)
1560 //                       d->adr.port = k->adr.port;
1561                     DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts modifying adr op:ModifyMidiDeviceAddress device:%p %s client:%d port:%d\n",
1562                             d, d->name().toLatin1().constData(), k->adr.client, k->adr.port);
1563 
1564                     if(!idling)
1565                     {
1566                       MusEGlobal::audio->msgIdle(true);
1567                       idling = true;
1568                     }
1569 
1570                     //operations.add(PendingOperationItem(d, k->adr.client, k->adr.port, PendingOperationItem::ModifyMidiDeviceAddress));
1571                     // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events
1572                     //operations.add(PendingOperationItem(d, k->flags, k->flags, PendingOperationItem::ModifyMidiDeviceFlags));
1573                     d->setAddressClient(k->adr.client);
1574                     d->setAddressPort(k->adr.port);
1575                     d->setrwFlags(k->flags);
1576                     // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events
1577                     d->setOpenFlags(k->flags);
1578                     if(d->midiPort() < 0)
1579                       // Keep the device closed and update the state.
1580                       d->setState("Closed");
1581                     else
1582                       // Re-subscribe, open and update the port's state.
1583                       MusEGlobal::midiPorts[d->midiPort()].setState(d->open());
1584                     break;
1585                   }
1586 
1587                   }
1588             if (i == MusEGlobal::midiDevices.end()) {
1589 
1590                   if(!idling)
1591                   {
1592                     MusEGlobal::audio->msgIdle(true);
1593                     idling = true;
1594                   }
1595 
1596                   // add device
1597 
1598                   const QString dev_name(k->name);
1599                   MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI);
1600                   const bool dev_found = dev;
1601                   if(dev_found)
1602                   {
1603                     // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device?
1604                     dev->setAddressClient(k->adr.client);
1605                     dev->setAddressPort(k->adr.port);
1606                   }
1607                   else
1608                     dev = new MidiAlsaDevice(k->adr, dev_name);
1609                   //MidiAlsaDevice* dev = new MidiAlsaDevice(k->adr, QString(k->name));
1610                   dev->setrwFlags(k->flags);
1611                   DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts op:AddMidiDevice adding:%p %s adr.client:%d adr.port:%d\n",
1612                           dev, dev->name().toLatin1().constData(), k->adr.client, k->adr.port);
1613 
1614                   //operations.add(PendingOperationItem(MusEGlobal::midiDevices, dev, PendingOperationItem::AddMidiDevice));
1615                   //MusEGlobal::midiDevices.addOperation(dev, operations);
1616                   if(!dev_found)
1617                     MusEGlobal::midiDevices.add(dev);
1618                   // Subscribe
1619                   //dev->open();
1620                   }
1621             }
1622 
1623             //if(!operations.empty())
1624             //{
1625             //  if(!idling)
1626             //    MusEGlobal::audio->msgIdle(true);
1627             //  //MusEGlobal::audio->msgExecutePendingOperations(operations, true);
1628             //  // Execute directly both stages
1629             //  operations.executeNonRTStage();
1630             //  operations.executeRTStage();
1631               if(idling)
1632               {
1633                 MusEGlobal::audio->msgIdle(false);
1634                 // Update the GUI.
1635                 MusEGlobal::song->update(SC_CONFIG);
1636               }
1637             //}
1638       }
1639 
1640 //---------------------------------------------------------
1641 //   alsaSelectRfd
1642 //---------------------------------------------------------
1643 
alsaSelectRfd()1644 int alsaSelectRfd()
1645       {
1646       return alsaSeqFdi;
1647       }
1648 
1649 //---------------------------------------------------------
1650 //   alsaSelectWfd
1651 //---------------------------------------------------------
1652 
alsaSelectWfd()1653 int alsaSelectWfd()
1654       {
1655       return alsaSeqFdo;
1656       }
1657 
1658 //---------------------------------------------------------
1659 //   processInput
1660 //---------------------------------------------------------
1661 
alsaProcessMidiInput()1662 void alsaProcessMidiInput()
1663 {
1664       unsigned frame_ts = MusEGlobal::audio->curFrame();
1665 
1666       DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput()\n");
1667 
1668       if(!alsaSeq)
1669         return;
1670 
1671       MidiRecordEvent event;
1672       snd_seq_event_t* ev;
1673 
1674       for (;;)
1675       {
1676             int rv = snd_seq_event_input(alsaSeq, &ev);
1677 // fprintf(stderr, "AlsaInput %d\n", rv);
1678             if (rv < 0) {
1679 //                  fprintf(stderr, "AlsaMidi: read error %s\n", snd_strerror(rv));
1680                   return;
1681                   }
1682 
1683             if (MusEGlobal::midiInputTrace) {
1684                   switch(ev->type)
1685                   {
1686                     // Ignore some flooding events like clock.
1687                     case SND_SEQ_EVENT_CLOCK:
1688                     break;
1689 
1690                     default:
1691                       fprintf(stderr, "ALSA MidiIn driver: ");
1692                       MidiAlsaDevice::dump(ev);
1693                     break;
1694                   }
1695                   }
1696 
1697             switch(ev->type) {
1698                   case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1699                         DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
1700                                 ev->data.connect.sender.client, ev->data.connect.sender.port,
1701                                 ev->data.connect.dest.client, ev->data.connect.dest.port);
1702 //                         MusEGlobal::audio->midiPortsChanged();  // signal gui
1703 //                         snd_seq_free_event(ev);
1704 //                         return;
1705                         if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1706                         {
1707                           muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1708                           MusEGlobal::audio->sendMsgToGui('P');
1709                         }
1710                         snd_seq_free_event(ev);
1711                         if(rv == 0)
1712                           return;
1713                         continue;
1714 
1715                   case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
1716                         DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
1717                                 ev->data.connect.sender.client, ev->data.connect.sender.port,
1718                                 ev->data.connect.dest.client, ev->data.connect.dest.port);
1719 //                         MusEGlobal::audio->midiPortsChanged();  // signal gui
1720 //                         snd_seq_free_event(ev);
1721 //                         return;
1722                         if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1723                         {
1724                           muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1725                           MusEGlobal::audio->sendMsgToGui('P');
1726                         }
1727                         snd_seq_free_event(ev);
1728                         if(rv == 0)
1729                           return;
1730                         continue;
1731 
1732                   case SND_SEQ_EVENT_CLIENT_START:
1733                         DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n",
1734                                 ev->data.addr.client, ev->data.addr.port);
1735 
1736                         //alsaScanMidiPorts();
1737 //                         MusEGlobal::audio->midiPortsChanged();  // signal gui
1738 //                         snd_seq_free_event(ev);
1739 //                         return;
1740                         if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1741                         {
1742                           muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1743                           MusEGlobal::audio->sendMsgToGui('P');
1744                         }
1745                         snd_seq_free_event(ev);
1746                         if(rv == 0)
1747                           return;
1748                         continue;
1749 
1750                   case SND_SEQ_EVENT_CLIENT_EXIT:
1751                         DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n",
1752                                 ev->data.addr.client, ev->data.addr.port);
1753                         //snd_seq_free_event(ev);
1754                         // return;
1755                         // on first start of a software synthesizer we only
1756                         // get CLIENT_START event and no PORT_START, why?
1757 
1758                         //alsaScanMidiPorts();
1759 //                         MusEGlobal::audio->midiPortsChanged();  // signal gui
1760 //                         snd_seq_free_event(ev);
1761 //                         return;
1762                         if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1763                         {
1764                           muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1765                           MusEGlobal::audio->sendMsgToGui('P');
1766                         }
1767                         snd_seq_free_event(ev);
1768                         if(rv == 0)
1769                           return;
1770                         continue;
1771 
1772                   case SND_SEQ_EVENT_PORT_START:
1773                         DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_START adr: %d:%d\n",
1774                                 ev->data.addr.client, ev->data.addr.port);
1775 
1776                         //alsaScanMidiPorts();
1777 //                         MusEGlobal::audio->midiPortsChanged();  // signal gui
1778 //                         snd_seq_free_event(ev);
1779 //                         return;
1780                         if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1781                         {
1782                           muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1783                           MusEGlobal::audio->sendMsgToGui('P');
1784                         }
1785                         snd_seq_free_event(ev);
1786                         if(rv == 0)
1787                           return;
1788                         continue;
1789 
1790                   case SND_SEQ_EVENT_PORT_EXIT:
1791                         DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n",
1792                                 ev->data.addr.client, ev->data.addr.port);
1793                         //alsaScanMidiPorts();
1794 //                         MusEGlobal::audio->midiPortsChanged();  // signal gui
1795 //                         snd_seq_free_event(ev);
1796 //                         return;
1797                         if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1798                         {
1799                           muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1800                           MusEGlobal::audio->sendMsgToGui('P');
1801                         }
1802                         snd_seq_free_event(ev);
1803                         if(rv == 0)
1804                           return;
1805                         continue;
1806 
1807 //                   case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1808 //                   case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
1809 //                   case SND_SEQ_EVENT_CLIENT_START:
1810 //                   case SND_SEQ_EVENT_CLIENT_EXIT:
1811 //                   case SND_SEQ_EVENT_PORT_START:
1812 //                   case SND_SEQ_EVENT_PORT_EXIT:
1813 //                         if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1814 //                         {
1815 //                           muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1816 //                           MusEGlobal::audio->sendMsgToGui('P');
1817 //                         }
1818 //                         snd_seq_free_event(ev);
1819 //                         if(rv == 0)
1820 //                           return;
1821 //                         continue;
1822                   }
1823 
1824             int curPort = -1;
1825             MidiAlsaDevice* mdev = 0;
1826             //
1827             // find real source device
1828             //
1829             for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) {
1830                   if((*i)->deviceType() != MidiDevice::ALSA_MIDI)
1831                     continue;
1832                   MidiAlsaDevice* d = static_cast<MidiAlsaDevice*>(*i);
1833                   if(d->adr.client == ev->source.client
1834                     && d->adr.port == ev->source.port) {
1835                         curPort = d->midiPort();
1836                         mdev = d;
1837                         }
1838                   }
1839 
1840             if (mdev == 0 || curPort == -1) {
1841                   if (MusEGlobal::debugMsg) {
1842                         fprintf(stderr, "no port %d:%d found for received alsa event\n",
1843                            ev->source.client, ev->source.port);
1844                         }
1845                   snd_seq_free_event(ev);
1846                   //return;
1847                   if(rv == 0)
1848                     return;
1849                   continue;
1850                   }
1851 
1852             event.setType(0);      // mark as unused
1853             event.setPort(curPort);
1854             event.setB(0);
1855 
1856             //MidiInstrument* instr = MusEGlobal::midiPorts[curPort].inputInstrument();
1857 
1858             switch(ev->type)
1859             {
1860                   case SND_SEQ_EVENT_NOTEON:
1861                         if(ev->data.note.velocity == 0)
1862                         {
1863                           // Convert zero-velocity note ons to note offs as per midi spec.
1864                           event.setChannel(ev->data.note.channel);
1865                           event.setType(ME_NOTEOFF);
1866                           event.setA(ev->data.note.note);
1867                           event.setB(ev->data.note.velocity);
1868                         }
1869                         else
1870                         {
1871                           event.setChannel(ev->data.note.channel);
1872                           event.setType(ME_NOTEON);
1873                           event.setA(ev->data.note.note);
1874                           event.setB(ev->data.note.velocity);
1875                         }
1876                         break;
1877 
1878                   case SND_SEQ_EVENT_NOTEOFF:
1879                         event.setChannel(ev->data.note.channel);
1880                         event.setType(ME_NOTEOFF);
1881                         event.setA(ev->data.note.note);
1882                         event.setB(ev->data.note.velocity);
1883                         break;
1884 
1885                   case SND_SEQ_EVENT_KEYPRESS:
1886                         event.setChannel(ev->data.note.channel);
1887                         event.setType(ME_POLYAFTER);
1888                         event.setA(ev->data.note.note);
1889                         event.setB(ev->data.note.velocity);
1890                         break;
1891 
1892                   case SND_SEQ_EVENT_CHANPRESS:
1893                         event.setChannel(ev->data.control.channel);
1894                         event.setType(ME_AFTERTOUCH);
1895                         event.setA(ev->data.control.value);
1896                         break;
1897 
1898                   case SND_SEQ_EVENT_PGMCHANGE:
1899                         event.setChannel(ev->data.control.channel);
1900                         event.setType(ME_PROGRAM);
1901                         event.setA(ev->data.control.value);
1902                         break;
1903 
1904                   case SND_SEQ_EVENT_PITCHBEND:
1905                         event.setChannel(ev->data.control.channel);
1906                         event.setType(ME_PITCHBEND);
1907                         event.setA(ev->data.control.value);
1908                         break;
1909 
1910                   case SND_SEQ_EVENT_CONTROLLER:
1911                         event.setChannel(ev->data.control.channel);
1912                         event.setType(ME_CONTROLLER);
1913                         event.setA(ev->data.control.param);
1914                         event.setB(ev->data.control.value);
1915                         break;
1916 
1917                   case SND_SEQ_EVENT_CLOCK:
1918                         if(MusEGlobal::audio && MusEGlobal::audio->isRunning())
1919                           mdev->midiClockInput(frame_ts);
1920                         break;
1921 
1922                   case SND_SEQ_EVENT_START:
1923                       #ifdef ALSA_DEBUG
1924                         if(MusEGlobal::midiInputTrace)
1925                           fprintf(stderr, "alsaProcessMidiInput: start port:%d curFrame:%u\n", curPort, frame_ts);
1926                       #endif
1927                         MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_START);
1928                         break;
1929 
1930                   case SND_SEQ_EVENT_CONTINUE:
1931                         MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_CONTINUE);
1932                         break;
1933 
1934                   case SND_SEQ_EVENT_STOP:
1935                         MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_STOP);
1936                         break;
1937 
1938                   case SND_SEQ_EVENT_TICK:
1939                         MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_TICK);
1940                         break;
1941 
1942                   case SND_SEQ_EVENT_SYSEX:
1943                         {
1944                           EvData ed;
1945                           const unsigned char* p = (unsigned char*)ev->data.ext.ptr;
1946 
1947                           // Process the input. Create the event data only if finished.
1948                           if(mdev->sysExInProcessor()->processInput(
1949                              &ed, p, ev->data.ext.len, frame_ts) != SysExInputProcessor::Finished)
1950                             break;
1951 
1952                         #ifdef ALSA_DEBUG
1953                           fprintf(stderr, "alsaProcessMidiInput: SysEx: frame_ts:%u startFrame:%u\n",
1954                                   frame_ts, (unsigned int)mdev->sysExInProcessor()->startFrame());
1955                         #endif
1956 
1957                           // Finished composing the sysex data.
1958                           // Mark the frame timestamp as the frame at which the sysex started.
1959                           frame_ts = mdev->sysExInProcessor()->startFrame();
1960                           event.setType(ME_SYSEX);
1961                           event.setData(ed);
1962                         }
1963                         break;
1964                   case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1965                   case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:  // write port is released
1966                         break;
1967                   case SND_SEQ_EVENT_SONGPOS:
1968                         MusEGlobal::midiSyncContainer.setSongPosition(curPort, ev->data.control.value);
1969                         break;
1970                   case SND_SEQ_EVENT_SENSING:
1971                         break;
1972                   case SND_SEQ_EVENT_QFRAME:
1973                         MusEGlobal::midiSyncContainer.mtcInputQuarter(curPort, ev->data.control.value);
1974                         break;
1975                   // case SND_SEQ_EVENT_CLIENT_START:
1976                   // case SND_SEQ_EVENT_CLIENT_EXIT:
1977                   // case SND_SEQ_EVENT_CLIENT_CHANGE:
1978                   // case SND_SEQ_EVENT_PORT_CHANGE:
1979                   // case SND_SEQ_EVENT_SONGSEL:
1980                   // case SND_SEQ_EVENT_TIMESIGN:
1981                   // case SND_SEQ_EVENT_KEYSIGN:
1982                   // case SND_SEQ_EVENT_SETPOS_TICK:
1983                   // case SND_SEQ_EVENT_SETPOS_TIME:
1984                   // case SND_SEQ_EVENT_TEMPO:
1985                   // case SND_SEQ_EVENT_TUNE_REQUEST:
1986                   // case SND_SEQ_EVENT_RESET:
1987 
1988                   // case SND_SEQ_EVENT_NOTE:
1989                   // case SND_SEQ_EVENT_CONTROL14:
1990 //                   case SND_SEQ_EVENT_NONREGPARAM:
1991 //                         fprintf(stderr, "ALSA Midi input: NONREGPARAM ch:%u param:%u value:%d\n",
1992 //                                         ev->data.control.channel,
1993 //                                         ev->data.control.param,
1994 //                                         ev->data.control.value);
1995 //                         event.setChannel(ev->data.control.channel);
1996 //                         event.setType(ME_CONTROLLER);
1997 //                         event.setA(ev->data.control.param);
1998 //                         event.setB(ev->data.control.value);
1999 //                     break;
2000                   // case SND_SEQ_EVENT_REGPARAM:
2001                   default:
2002                         fprintf(stderr, "ALSA Midi input: type %d not handled\n", ev->type);
2003                         break;
2004             }
2005             if(event.type())
2006             {
2007               // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice.
2008               event.setTime(frame_ts);
2009               event.setTick(MusEGlobal::lastExtMidiSyncTick);
2010 
2011               mdev->recordEvent(event);
2012             }
2013 
2014             snd_seq_free_event(ev);
2015             if (rv == 0)
2016                   break;
2017       }
2018 }
2019 
2020 //---------------------------------------------------------
2021 //   dump
2022 //   static
2023 //---------------------------------------------------------
2024 
dump(const snd_seq_event_t * ev)2025 void MidiAlsaDevice::dump(const snd_seq_event_t* ev)
2026 {
2027   switch(ev->type)
2028   {
2029     case SND_SEQ_EVENT_PORT_SUBSCRIBED:
2030       fprintf(stderr, "SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
2031               ev->data.connect.sender.client, ev->data.connect.sender.port,
2032               ev->data.connect.dest.client, ev->data.connect.dest.port);
2033     break;
2034 
2035     case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
2036       fprintf(stderr, "SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
2037               ev->data.connect.sender.client, ev->data.connect.sender.port,
2038               ev->data.connect.dest.client, ev->data.connect.dest.port);
2039     break;
2040 
2041     case SND_SEQ_EVENT_CLIENT_START:
2042       fprintf(stderr, "SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n",
2043               ev->data.addr.client, ev->data.addr.port);
2044     break;
2045 
2046     case SND_SEQ_EVENT_CLIENT_EXIT:
2047       fprintf(stderr, "SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n",
2048               ev->data.addr.client, ev->data.addr.port);
2049     break;
2050 
2051     case SND_SEQ_EVENT_PORT_START:
2052       fprintf(stderr, "SND_SEQ_EVENT_PORT_START adr: %d:%d\n",
2053               ev->data.addr.client, ev->data.addr.port);
2054     break;
2055 
2056     case SND_SEQ_EVENT_PORT_EXIT:
2057       fprintf(stderr, "SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n",
2058               ev->data.addr.client, ev->data.addr.port);
2059     break;
2060 
2061     case SND_SEQ_EVENT_CONTROLLER:
2062       fprintf(stderr, "SND_SEQ_EVENT_CONTROLLER chan:%u param:%u value:%d\n",
2063               ev->data.control.channel, ev->data.control.param, ev->data.control.value);
2064     break;
2065 
2066     case SND_SEQ_EVENT_NOTE:
2067       fprintf(stderr, "SND_SEQ_EVENT_NOTE chan:%u note:%u velocity:%u off_velocity:%u duration:%u\n",
2068               ev->data.note.channel, ev->data.note.note, ev->data.note.velocity, ev->data.note.off_velocity, ev->data.note.duration);
2069     break;
2070 
2071     case SND_SEQ_EVENT_NOTEON:
2072       fprintf(stderr, "SND_SEQ_EVENT_NOTEON chan:%u note:%u velocity:%u\n",
2073               ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
2074     break;
2075 
2076     case SND_SEQ_EVENT_NOTEOFF:
2077       fprintf(stderr, "SND_SEQ_EVENT_NOTEOFF chan:%u note:%u velocity:%u\n",
2078               ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
2079     break;
2080 
2081     case SND_SEQ_EVENT_KEYPRESS:
2082       fprintf(stderr, "SND_SEQ_EVENT_KEYPRESS chan:%u note:%u velocity:%u\n",
2083               ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
2084     break;
2085 
2086     case SND_SEQ_EVENT_CHANPRESS:
2087       fprintf(stderr, "SND_SEQ_EVENT_CHANPRESS chan:%u value:%d\n",
2088               ev->data.control.channel, ev->data.control.value);
2089     break;
2090 
2091     case SND_SEQ_EVENT_PGMCHANGE:
2092       fprintf(stderr, "SND_SEQ_EVENT_PGMCHANGE chan:%u value:%d\n",
2093               ev->data.control.channel, ev->data.control.value);
2094     break;
2095 
2096     case SND_SEQ_EVENT_PITCHBEND:
2097       fprintf(stderr, "SND_SEQ_EVENT_PITCHBEND chan:%u value:%d\n",
2098               ev->data.control.channel, ev->data.control.value);
2099     break;
2100 
2101     case SND_SEQ_EVENT_CLOCK:
2102       fprintf(stderr, "SND_SEQ_EVENT_CLOCK\n");
2103     break;
2104 
2105     case SND_SEQ_EVENT_START:
2106       fprintf(stderr, "SND_SEQ_EVENT_START\n");
2107     break;
2108 
2109     case SND_SEQ_EVENT_CONTINUE:
2110       fprintf(stderr, "SND_SEQ_EVENT_CONTINUE\n");
2111     break;
2112 
2113     case SND_SEQ_EVENT_STOP:
2114       fprintf(stderr, "SND_SEQ_EVENT_STOP\n");
2115     break;
2116 
2117     case SND_SEQ_EVENT_TICK:
2118       fprintf(stderr, "SND_SEQ_EVENT_TICK\n");
2119     break;
2120 
2121     case SND_SEQ_EVENT_SYSEX:
2122       fprintf(stderr, "SND_SEQ_EVENT_SYSEX len:%u data: ", ev->data.ext.len);
2123       for(unsigned int i = 0; i < ev->data.ext.len && i < 16; ++i)
2124         fprintf(stderr, "%0x ", ((unsigned char*)ev->data.ext.ptr)[i]);
2125       if(ev->data.ext.len >= 16)
2126         fprintf(stderr, "...");
2127       fprintf(stderr, "\n");
2128     break;
2129 
2130     case SND_SEQ_EVENT_SONGPOS:
2131       fprintf(stderr, "SND_SEQ_EVENT_SONGPOS value:%d\n",
2132               ev->data.control.value);
2133     break;
2134 
2135     case SND_SEQ_EVENT_SENSING:
2136       fprintf(stderr, "SND_SEQ_EVENT_SENSING\n");
2137     break;
2138 
2139     case SND_SEQ_EVENT_QFRAME:
2140       fprintf(stderr, "SND_SEQ_EVENT_QFRAME value:%d\n",
2141               ev->data.control.value);
2142     break;
2143     case SND_SEQ_EVENT_CONTROL14:
2144       fprintf(stderr, "SND_SEQ_EVENT_CONTROL14 ch:%u param:%u value:%d\n",
2145               ev->data.control.channel,
2146               ev->data.control.param,
2147               ev->data.control.value);
2148     break;
2149 
2150     case SND_SEQ_EVENT_NONREGPARAM:
2151       fprintf(stderr, "SND_SEQ_EVENT_NONREGPARAM ch:%u param:%u value:%d\n",
2152               ev->data.control.channel,
2153               ev->data.control.param,
2154               ev->data.control.value);
2155     break;
2156 
2157     case SND_SEQ_EVENT_REGPARAM:
2158       fprintf(stderr, "SND_SEQ_EVENT_REGPARAM ch:%u param:%u value:%d\n",
2159               ev->data.control.channel,
2160               ev->data.control.param,
2161               ev->data.control.value);
2162     break;
2163 
2164     // case SND_SEQ_EVENT_CLIENT_CHANGE:
2165     // case SND_SEQ_EVENT_PORT_CHANGE:
2166     // case SND_SEQ_EVENT_SONGSEL:
2167     // case SND_SEQ_EVENT_TIMESIGN:
2168     // case SND_SEQ_EVENT_KEYSIGN:
2169     // case SND_SEQ_EVENT_SETPOS_TICK:
2170     // case SND_SEQ_EVENT_SETPOS_TIME:
2171     // case SND_SEQ_EVENT_TEMPO:
2172     // case SND_SEQ_EVENT_TUNE_REQUEST:
2173     // case SND_SEQ_EVENT_RESET:
2174 
2175     default:
2176       fprintf(stderr, "ALSA dump event: unknown type:%u\n", ev->type);
2177     break;
2178   }
2179 }
2180 
2181 } // namespace MusECore
2182 
2183 #else // ALSA_SUPPORT
2184 
2185 namespace MusECore {
2186 
initMidiAlsa()2187 bool initMidiAlsa() { return false; }
exitMidiAlsa()2188 void exitMidiAlsa() {}
alsaSelectRfd()2189 int alsaSelectRfd() { return -1; }
alsaSelectWfd()2190 int alsaSelectWfd() { return -1; }
alsaProcessMidiInput()2191 void alsaProcessMidiInput() { }
alsaScanMidiPorts()2192 void alsaScanMidiPorts() { }
setAlsaClientName(const char *)2193 void setAlsaClientName(const char*) { }
2194 }
2195 
2196 #endif // ALSA_SUPPORT
2197