    //=============================================================================
    //  MuseScore
    //  Linux Music Score Editor
    //  $Id: mididriver.cpp 5568 2012-04-22 10:08:43Z wschweer $
    //
    //  Copyright (C) 2008 Werner Schweer and others
    //
    //  AlsaDriver based on code from Fons Adriaensen (clalsadr.cc)
    //  Copyright (C) 2003 Fons Adriaensen
    //  partly based on original work from Paul Davis
    //
    //  This program is free software; you can redistribute it and/or modify
    //  it under the terms of the GNU General Public License version 2.
    //
    //  This program is distributed in the hope that it will be useful,
    //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    //  GNU General Public License for more details.
    //
    //  You should have received a copy of the GNU General Public License
    //  along with this program; if not, write to the Free Software
    //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    //=============================================================================

    #include <time.h>

    #include "config.h"
    #include "mididriver.h"
    #include "preferences.h"
    #include "musescore.h"
    #include "midi/midifile.h"
    #include "globals.h"
    #include "seq.h"
    #include "libmscore/utils.h"
    #include "libmscore/score.h"

    namespace Ms {

    //---------------------------------------------------------
    //   Port
    //---------------------------------------------------------

    Port::Port()
          {
          type = ZERO_TYPE;
          }

    Port::Port(unsigned char client, unsigned char port)
          {
          _alsaPort = port;
          _alsaClient = client;
          type = ALSA_TYPE;
          }

    //---------------------------------------------------------
    //   setZero
    //---------------------------------------------------------

    void Port::setZero()
          {
          type = ZERO_TYPE;
          }

    //---------------------------------------------------------
    //   isZero
    //---------------------------------------------------------

    bool Port::isZero()  const
          {
          return type == ZERO_TYPE;
          }

    //---------------------------------------------------------
    //   operator==
    //---------------------------------------------------------

    bool Port::operator==(const Port& p) const
          {
          if (type == ALSA_TYPE)
                return _alsaPort == p._alsaPort && _alsaClient == p._alsaClient;
          else
                return true;
          }

    //---------------------------------------------------------
    //   operator<
    //---------------------------------------------------------

    bool Port::operator<(const Port& p) const
          {
          if (type == ALSA_TYPE) {
                if (_alsaPort != p._alsaPort)
                      return _alsaPort < p._alsaPort;
                return _alsaClient < p._alsaClient;
                }
          return false;
          }
    }

    #ifdef USE_ALSA
    #include "alsa.h"
    #include "alsamidi.h"

    namespace Ms {
    static const unsigned int inCap  = SND_SEQ_PORT_CAP_SUBS_READ;
    static const unsigned int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE;

    //---------------------------------------------------------
    //   AlsaMidiDriver
    //---------------------------------------------------------

    AlsaMidiDriver::AlsaMidiDriver(Seq* s)
       : MidiDriver(s)
          {
          }

    //---------------------------------------------------------
    //   init
    //    return false on error
    //---------------------------------------------------------

    bool AlsaMidiDriver::init()
          {
          int error = snd_seq_open(&alsaSeq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
          if (error < 0) {
                if (error == ENOENT)
                      qDebug("open ALSA sequencer failed: %s", snd_strerror(error));
                return false;
                }

          snd_seq_set_client_name(alsaSeq, "MuseScore");

          //-----------------------------------------
          //    subscribe to "Announce"
          //    this enables callbacks for any
          //    alsa port changes
          //-----------------------------------------

          snd_seq_addr_t src, dst;
          int rv = snd_seq_create_simple_port(alsaSeq, "MuseScore Port 0",
             inCap | outCap | SND_SEQ_PORT_CAP_READ
             | SND_SEQ_PORT_CAP_WRITE
             | SND_SEQ_PORT_CAP_NO_EXPORT,
             SND_SEQ_PORT_TYPE_APPLICATION);
          if (rv < 0) {
                qDebug("Alsa: create MuseScore port failed: %s", snd_strerror(error));
                return false;
                }
          dst.port   = rv;
          dst.client = snd_seq_client_id(alsaSeq);
          src.port   = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
          src.client = SND_SEQ_CLIENT_SYSTEM;

          snd_seq_port_subscribe_t* subs;
          snd_seq_port_subscribe_alloca(&subs);
          snd_seq_port_subscribe_set_dest(subs, &dst);
          snd_seq_port_subscribe_set_sender(subs, &src);
          error = snd_seq_subscribe_port(alsaSeq, subs);
          if (error < 0) {
                qDebug("Alsa: Subscribe System failed: %s", snd_strerror(error));
                return false;
                }
          midiInPort   = registerOutPort("MuseScore Port-0");
          midiOutPorts.append(registerInPort("MuseScore Port-0"));

          struct pollfd* pfd;
          int npfd;
          getInputPollFd(&pfd, &npfd);
          for (int i = 0; i < npfd; ++i) {
                int fd = pfd[i].fd;
                if (fd != -1) {
                      QSocketNotifier* s = new QSocketNotifier(fd, QSocketNotifier::Read,  mscore);
                      s->connect(s, SIGNAL(activated(int)), seq, SLOT(midiInputReady()));
                      }
                }
    #if 0
          // TODO: autoconnect all output ports
          QList<PortName> ol = outputPorts();
          foreach(PortName pn, ol) {
                if (MScore::debugMode)
                      qDebug("connect to midi output <%s>", qPrintable(pn.name));
                qDebug("Output <%s>", qPrintable(pn.name));
                }
    #endif

          // connect all midi sources to mscore
          QList<PortName> il = inputPorts();
          foreach(PortName pn, il) {
                if (MScore::debugMode)
                      qDebug("connect to midi input <%s>", qPrintable(pn.name));
                connect(pn.port, midiInPort);
                }
          return true;
          }

    //---------------------------------------------------------
    //   outputPorts
    //---------------------------------------------------------

    QList<PortName> AlsaMidiDriver::outputPorts()
          {
          QList<PortName> clientList;
          snd_seq_client_info_t* cinfo;
          snd_seq_client_info_alloca(&cinfo);
          snd_seq_client_info_set_client(cinfo, 0);

          while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
                snd_seq_port_info_t *pinfo;
                snd_seq_port_info_alloca(&pinfo);
                snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
                snd_seq_port_info_set_port(pinfo, -1);
                while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
                      unsigned int capability = snd_seq_port_info_get_capability(pinfo);
                      if (((capability & outCap) == outCap)
                         && !(capability & SND_SEQ_PORT_CAP_NO_EXPORT)) {
                            int client = snd_seq_port_info_get_client(pinfo);
                            if (client != snd_seq_client_id(alsaSeq)) {
                                  PortName pn;
                                  pn.name = QString(snd_seq_port_info_get_name(pinfo));
                                  pn.port = Port(client, snd_seq_port_info_get_port(pinfo));
                                  clientList.append(pn);
                                  }
                            }
                      }
                }
          return clientList;
          }

    //---------------------------------------------------------
    //   inputPorts
    //---------------------------------------------------------

    QList<PortName> AlsaMidiDriver::inputPorts()
          {
          QList<PortName> clientList;

          snd_seq_client_info_t* cinfo;
          snd_seq_client_info_alloca(&cinfo);
          snd_seq_client_info_set_client(cinfo, 0);

          while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
                snd_seq_port_info_t *pinfo;
                snd_seq_port_info_alloca(&pinfo);
                snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
                snd_seq_port_info_set_port(pinfo, -1);
                while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
                      unsigned int capability = snd_seq_port_info_get_capability(pinfo);
                      if (((capability & inCap) == inCap)
                         && !(capability & SND_SEQ_PORT_CAP_NO_EXPORT)) {
                            int client = snd_seq_port_info_get_client(pinfo);
                            if (client != snd_seq_client_id(alsaSeq)) {
                                  PortName pn;
                                  pn.name = QString(snd_seq_port_info_get_name(pinfo));
                                  pn.port = Port(client, snd_seq_port_info_get_port(pinfo));
                                  clientList.append(pn);
                                  }
                            }
                      }
                }
          return clientList;
          }

    //---------------------------------------------------------
    //   connect
    //    return false if connect fails
    //---------------------------------------------------------

    bool AlsaMidiDriver::connect(Port src, Port dst)
          {
          snd_seq_port_subscribe_t* sub;
          snd_seq_port_subscribe_alloca(&sub);

          snd_seq_addr_t s, d;
          s.port   = src.alsaPort();
          s.client = src.alsaClient();
          d.port   = dst.alsaPort();
          d.client = dst.alsaClient();
          snd_seq_port_subscribe_set_sender(sub, &s);
          snd_seq_port_subscribe_set_dest(sub, &d);

          int rv = snd_seq_subscribe_port(alsaSeq, sub);
          if (rv < 0) {
                qDebug("AlsaMidi::connect(%d:%d, %d:%d) failed: %s",
                   src.alsaClient(), src.alsaPort(),
                   dst.alsaClient(), dst.alsaPort(),
                   snd_strerror(rv));
                return false;
                }
          return true;
          }

    //---------------------------------------------------------
    //   registerOutPort
    //---------------------------------------------------------

    Port AlsaMidiDriver::registerOutPort(const QString& name)
          {
          int alsaPort  = snd_seq_create_simple_port(alsaSeq, name.toLatin1().data(),
             outCap | SND_SEQ_PORT_CAP_WRITE, SND_SEQ_PORT_TYPE_APPLICATION);
          if (alsaPort < 0) {
                perror("cannot create alsa out port");
                return Port();
                }
          return Port(snd_seq_client_id(alsaSeq), alsaPort);
          }

    //---------------------------------------------------------
    //   registerInPort
    //---------------------------------------------------------

    Port AlsaMidiDriver::registerInPort(const QString& name)
          {
          int alsaPort  = snd_seq_create_simple_port(alsaSeq, name.toLatin1().data(),
             inCap | SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_APPLICATION);
          if (alsaPort < 0) {
                perror("cannot create alsa in port");
                return Port();
                }
          return Port(snd_seq_client_id(alsaSeq), alsaPort);
          }

    //---------------------------------------------------------
    //   updateInPortCount
    //---------------------------------------------------------

    void AlsaMidiDriver::updateInPortCount(int maxport)
          {
          int ports = midiOutPorts.size();
          if (maxport == ports)
                return;
          if (MScore::debugMode)
                qDebug()<<"ALSA number of ports:"<<ports<<", change to:"<<maxport;
          if (maxport > ports) {
                for (int i = ports; i < maxport; ++i)
                      midiOutPorts.append(registerInPort(QString("MuseScore Port-%1").arg(i)));
                }
          else if (maxport < ports) {
                for(int i = ports - 1; i >= maxport; --i) {
                      if (snd_seq_delete_simple_port(alsaSeq, midiOutPorts[i].alsaPort()) < 0)
                            qDebug("Can not delete ALSA port");
                      else
                            midiOutPorts.removeAt(i);
                      }
                }
          }

    //---------------------------------------------------------
    //   getInputPollFd
    //---------------------------------------------------------

    void AlsaMidiDriver::getInputPollFd(struct pollfd** p, int* n)
          {
          int npfdi = snd_seq_poll_descriptors_count(alsaSeq, POLLIN);
          struct pollfd* pfdi  = new struct pollfd[npfdi];
          snd_seq_poll_descriptors(alsaSeq, pfdi, npfdi, POLLIN);
          *p = pfdi;
          *n = npfdi;
          }

    //---------------------------------------------------------
    //   getOutputPollFd
    //---------------------------------------------------------

    void AlsaMidiDriver::getOutputPollFd(struct pollfd** p, int* n)
          {
          int npfdo = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT);
          struct pollfd* pfdo  = new struct pollfd[npfdo];
          snd_seq_poll_descriptors(alsaSeq, pfdo, npfdo, POLLOUT);
          *p = pfdo;
          *n = npfdo;
          }

    //---------------------------------------------------------
    //   read
    //---------------------------------------------------------

    void AlsaMidiDriver::read()
          {
          snd_seq_event_t* ev;
          for (;;) {
                int rv = snd_seq_event_input(alsaSeq, &ev);
                if (rv < 0)
                      return;

                if (!mscore || !mscore->midiinEnabled()) {
                      snd_seq_free_event(ev);
                      return;
                      }

                if (ev->type == SND_SEQ_EVENT_NOTEON) {
                      int pitch = ev->data.note.note;
                      int velo  = ev->data.note.velocity;
                      mscore->midiNoteReceived(ev->data.note.channel, pitch, velo);
                      }
                else if (ev->type == SND_SEQ_EVENT_NOTEOFF) {    // "Virtual Keyboard" sends this
                      int pitch = ev->data.note.note;
                      mscore->midiNoteReceived(ev->data.note.channel, pitch, 0);
                      }
                else if (ev->type == SND_SEQ_EVENT_CONTROLLER) {
                      mscore->midiCtrlReceived(ev->data.control.param,
                         ev->data.control.value);
                      }

                if (midiInputTrace) {
                      qDebug("MidiIn: ");
                      switch(ev->type) {
                            case SND_SEQ_EVENT_NOTEON:
                                  qDebug("noteOn ch:%2d 0x%02x 0x%02x",
                                     ev->data.note.channel,
                                     ev->data.note.note,
                                     ev->data.note.velocity);
                                  break;
                            case SND_SEQ_EVENT_SYSEX:
                                  qDebug("sysEx len %d", ev->data.ext.len);
                                  break;
                            case SND_SEQ_EVENT_CONTROLLER:
                                  qDebug("ctrl 0x%02x 0x%02x",
                                     ev->data.control.param,
                                     ev->data.control.value);
                                  break;
                            case SND_SEQ_EVENT_PITCHBEND:
                                  qDebug("pitchbend 0x%04x", ev->data.control.value);
                                  break;
                            case SND_SEQ_EVENT_PGMCHANGE:
                                  qDebug("pgmChange 0x%02x", ev->data.control.value);
                                  break;
                            case SND_SEQ_EVENT_CHANPRESS:
                                  qDebug("channelPress 0x%02x", ev->data.control.value);
                                  break;
                            case SND_SEQ_EVENT_START:
                                  qDebug("start");
                                  break;
                            case SND_SEQ_EVENT_CONTINUE:
                                  qDebug("continue");
                                  break;
                            case SND_SEQ_EVENT_STOP:
                                  qDebug("stop");
                                  break;
                            case SND_SEQ_EVENT_SONGPOS:
                                  qDebug("songpos");
                                  break;
                            default:
                                  qDebug("type 0x%02x", ev->type);
                                  break;
                            case SND_SEQ_EVENT_PORT_SUBSCRIBED:
                            case SND_SEQ_EVENT_SENSING:
                                  break;
                            }
                      }
                snd_seq_free_event(ev);
                }
          }

    //---------------------------------------------------------
    //   write
    //---------------------------------------------------------

    void AlsaMidiDriver::write(const Event& e)
          {
          MasterScore* cs = mscore->currentScore()->masterScore();
          int port  = cs->midiPort(e.channel());
          int chn   = cs->midiChannel(e.channel());
          int a     = e.dataA();
          int b     = e.dataB();

          if (midiOutputTrace)
                qDebug("midiOut: %2d %02x %02x %02x", port, e.type(), a, b);

          snd_seq_event_t event;
          memset(&event, 0, sizeof(event));
          snd_seq_ev_set_direct(&event);
          if (port >= midiOutPorts.size())
                port = 0;
          snd_seq_ev_set_source(&event, midiOutPorts[port].alsaPort());
          snd_seq_ev_set_dest(&event, SND_SEQ_ADDRESS_SUBSCRIBERS, 0);

          switch(e.type()) {
                case ME_NOTEON:
                      snd_seq_ev_set_noteon(&event, chn, a, b);
                      break;
                case ME_NOTEOFF:
                      snd_seq_ev_set_noteoff(&event, chn, a, 0);
                      break;
                case ME_PROGRAM:
                      snd_seq_ev_set_pgmchange(&event, chn, a);
                      break;
                case ME_CONTROLLER:
                      snd_seq_ev_set_controller(&event, chn, a, b);
                      break;
                case ME_PITCHBEND:
                      snd_seq_ev_set_pitchbend(&event, chn, a);
                      break;
                case ME_POLYAFTER:
                      // chnEvent2(chn, 0xa0, a, b);
                      break;
                case ME_AFTERTOUCH:
                      snd_seq_ev_set_chanpress(&event, chn, a);
                      break;
                case ME_SYSEX:
                      {
    #if 0
                      SysexEvent* se         = (SysexEvent*) e;
                      const unsigned char* p = se->data();
                      int n                  = se->len();
                      int len                = n + sizeof(event) + 2;
                      char buf[len];
                      event.type             = SND_SEQ_EVENT_SYSEX;
                      event.flags            = SND_SEQ_EVENT_LENGTH_VARIABLE;
                      event.data.ext.len     = n + 2;
                      event.data.ext.ptr  = (void*)(buf + sizeof(event));
                      memcpy(buf, &event, sizeof(event));
                      char* pp = buf + sizeof(event);
                      *pp++ = 0xf0;
                      memcpy(pp, p, n);
                      pp += n;
                      *pp = 0xf7;
                      putEvent(&event);
    #endif
                      return;
                      }
                default:
                      qDebug("MidiAlsaDriver::putEvent(): event type 0x%02x not implemented", e.type());
                      return;
                }
          putEvent(&event);
          }

    //---------------------------------------------------------
    //   putEvent
    //    return false if event is delivered
    //---------------------------------------------------------

    bool AlsaMidiDriver::putEvent(snd_seq_event_t* event)
          {
          int error;

          do {
                error   = snd_seq_event_output_direct(alsaSeq, event);
                int len = snd_seq_event_length(event);
                if (error == len) {
                      return false;
                      }
                if (error < 0) {
                      if (error == -12) {
                            return true;
                            }
                      else {
                            qDebug("MidiAlsaDevice::%p putEvent(): midi write error: %s",
                               this, snd_strerror(error));
                            //exit(-1);
                            }
                      }
                else
                      qDebug("MidiAlsaDevice::putEvent(): midi write returns %d, expected %d: %s",
                         error, len, snd_strerror(error));
                } while (error == -12);
          return true;
          }
    }

    #endif /* USE_ALSA */

↑ V547 Expression 'error == 2' is always false.

↑ V505 The 'alloca' function is used inside the loop. This can quickly overflow stack.

↑ V505 The 'alloca' function is used inside the loop. This can quickly overflow stack.

↑ V684 A value of the variable '(& event)->flags' is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.

↑ V684 A value of the variable '(& event)->flags' is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.

↑ V684 A value of the variable '(& event)->flags' is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.

↑ V684 A value of the variable '(& event)->flags' is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.

↑ V684 A value of the variable '(& event)->flags' is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: _alsaPort, _alsaClient.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: alsaSeq.