    //=============================================================================
    //  MusE Score
    //  Linux Music Score Editor
    //  $Id: pa.cpp 5662 2012-05-23 07:35:47Z wschweer $
    //
    //  Copyright (C) 2002-2010 Werner Schweer and others
    //
    //  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 "preferences.h"
    #include "libmscore/score.h"
    #include "musescore.h"
    #include "seq.h"
    #include "pa.h"

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

    #include <portaudio.h>
    #include "mididriver.h"

    #ifdef USE_PORTMIDI
    #include "pm.h"
    #endif

    namespace Ms {

    static PaStream* stream;

    //---------------------------------------------------------
    //   paCallback
    //---------------------------------------------------------

    int paCallback(const void*, void* out, long unsigned frames,
       const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void *)
          {
          seq->setInitialMillisecondTimestampWithLatency();
          seq->process((unsigned)frames, (float*)out);
          return 0;
          }

    //---------------------------------------------------------
    //   Portaudio
    //---------------------------------------------------------

    Portaudio::Portaudio(Seq* s)
       : Driver(s)
          {
          _sampleRate = 48000;    // will be replaced by device default sample rate
          initialized = false;
          state       = Transport::STOP;
          seekflag    = false;
          midiDriver  = 0;
          }

    //---------------------------------------------------------
    //   ~Portaudio
    //---------------------------------------------------------

    Portaudio::~Portaudio()
          {
          if (initialized) {
                PaError err = Pa_CloseStream(stream);
                if (err != paNoError)
                      qDebug("Portaudio close stream failed: %s", Pa_GetErrorText(err));
                Pa_Terminate();
                }
          }

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

    bool Portaudio::init(bool)
          {
          PaError err = Pa_Initialize();
          if (err != paNoError) {
                qDebug("Portaudio initialize failed: %s", Pa_GetErrorText(err));
                return false;
                }
          initialized = true;
          if (MScore::debugMode)
                qDebug("using PortAudio Version: %s", Pa_GetVersionText());

          PaDeviceIndex idx = preferences.portaudioDevice;
          if (idx < 0) {
                idx = Pa_GetDefaultOutputDevice();
                qDebug("No device selected.  PortAudio detected %d devices.  Will use the default device (index %d).", Pa_GetDeviceCount(), idx);
                }

          const PaDeviceInfo* di = Pa_GetDeviceInfo(idx);

          if (di == nullptr)
                di = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice());

          if (!di)
                return false;    // Portaudio is not properly initialized; disable audio
          _sampleRate = int(di->defaultSampleRate);

          /* Open an audio I/O stream. */
          struct PaStreamParameters out;
          memset(&out, 0, sizeof(out));

          out.device           = idx;
          out.channelCount     = 2;
          out.sampleFormat     = paFloat32;
    #ifdef Q_OS_MAC
          out.suggestedLatency = 0.020;
    #else // on windows, this small latency causes some problem
          out.suggestedLatency = 0.100;
    #endif
          out.hostApiSpecificStreamInfo = 0;

          err = Pa_OpenStream(&stream, 0, &out, double(_sampleRate), 0, 0, paCallback, (void*)this);
          if (err != paNoError) {
                // fall back to default device:
                out.device = Pa_GetDefaultOutputDevice();
                err = Pa_OpenStream(&stream, 0, &out, double(_sampleRate), 0, 0, paCallback, (void*)this);
                if (err != paNoError) {
                      qDebug("Portaudio open stream %d failed: %s", idx, Pa_GetErrorText(err));
                      return false;
                      }
                }
          const PaStreamInfo* si = Pa_GetStreamInfo(stream);
          if (si)
                _sampleRate = int(si->sampleRate);
    #ifdef USE_ALSA
          midiDriver = new AlsaMidiDriver(seq);
    #endif
    #ifdef USE_PORTMIDI
          midiDriver = new PortMidiDriver(seq);
    #endif
          if (midiDriver && !midiDriver->init()) {
                qDebug("Init midi driver failed");
                delete midiDriver;
                midiDriver = 0;
    #ifdef USE_PORTMIDI
                return true;                  // return OK for audio driver; midi is only input
    #else
                return false;
    #endif
                }
          return true;
          }

    //---------------------------------------------------------
    //   apiList
    //---------------------------------------------------------

    QStringList Portaudio::apiList() const
          {
          QStringList al;

          PaHostApiIndex apis = Pa_GetHostApiCount();
          for (PaHostApiIndex i = 0; i < apis; ++i) {
                const PaHostApiInfo* info = Pa_GetHostApiInfo(i);
                if (info)
                      al.append(QString::fromLocal8Bit(info->name));
                }
          return al;
          }

    //---------------------------------------------------------
    //   deviceList
    //---------------------------------------------------------

    QStringList Portaudio::deviceList(int apiIdx)
          {
          QStringList dl;
          const PaHostApiInfo* info = Pa_GetHostApiInfo(apiIdx);
          if (info) {
                for (int i = 0; i < info->deviceCount; ++i) {
                      PaDeviceIndex idx = Pa_HostApiDeviceIndexToDeviceIndex(apiIdx, i);
                      const PaDeviceInfo* di = Pa_GetDeviceInfo(idx);
                      if (di)
                            dl.append(QString::fromLocal8Bit(di->name));
                      }
                }
          return dl;
          }

    //---------------------------------------------------------
    //   deviceIndex
    //---------------------------------------------------------

    int Portaudio::deviceIndex(int apiIdx, int apiDevIdx)
          {
          return Pa_HostApiDeviceIndexToDeviceIndex(apiIdx, apiDevIdx);
          }

    //---------------------------------------------------------
    //   start
    //---------------------------------------------------------

    bool Portaudio::start(bool)
          {
          PaError err = Pa_StartStream(stream);
          if (err != paNoError) {
                qDebug("Portaudio: start stream failed: %s", Pa_GetErrorText(err));
                return false;
                }
          return true;
          }

    //---------------------------------------------------------
    //   stop
    //---------------------------------------------------------

    bool Portaudio::stop()
          {
          PaError err = Pa_StopStream(stream);      // sometimes the program hangs here on exit
          if (err != paNoError) {
                qDebug("Portaudio: stop failed: %s", Pa_GetErrorText(err));
                return false;
                }
          return true;
          }

    //---------------------------------------------------------
    //   framePos
    //---------------------------------------------------------

    int Portaudio::framePos() const
          {
          return 0;
          }

    //---------------------------------------------------------
    //   startTransport
    //---------------------------------------------------------

    void Portaudio::startTransport()
          {
          state = Transport::PLAY;
          }

    //---------------------------------------------------------
    //   stopTransport
    //---------------------------------------------------------

    void Portaudio::stopTransport()
          {
          state = Transport::STOP;
          }

    //---------------------------------------------------------
    //   getState
    //---------------------------------------------------------

    Transport Portaudio::getState()
          {
          return state;
          }

    //---------------------------------------------------------
    //   midiRead
    //---------------------------------------------------------

    void Portaudio::midiRead()
          {
          if (midiDriver)
                midiDriver->read();
          }

    //---------------------------------------------------------
    //   putEvent
    //---------------------------------------------------------

    // Prevent killing sequencer with wrong data
    #define less128(__less) ((__less >=0 && __less <= 127) ? __less : 0)

    // TODO: this was copied from Jack version...I'd like to eventually unify these two, so that they handle midi event types in the same manner
    void Portaudio::putEvent(const NPlayEvent& e, unsigned framePos)
          {
          PortMidiDriver* portMidiDriver = static_cast<PortMidiDriver*>(midiDriver);
          if (!portMidiDriver || !portMidiDriver->getOutputStream() || !portMidiDriver->canOutput())
                return;

          int portIdx = seq->score()->midiPort(e.channel());
          int chan    = seq->score()->midiChannel(e.channel());

          if (portIdx < 0 ) {
                qDebug("Portaudio::putEvent: invalid port %d", portIdx);
                return;
                }

          if (midiOutputTrace) {
                int a     = e.dataA();
                int b     = e.dataB();
                qDebug("MidiOut<%d>: Portaudio: %02x %02x %02x, chan: %i", portIdx, e.type(), a, b, chan);
                }

          switch(e.type()) {
                case ME_NOTEON:
                case ME_NOTEOFF:
                case ME_POLYAFTER:
                case ME_CONTROLLER:
                      // Catch CTRL_PROGRAM and let other ME_CONTROLLER events to go
                      if (e.dataA() == CTRL_PROGRAM) {
                            // Convert CTRL_PROGRAM event to ME_PROGRAM
                            long msg = Pm_Message(ME_PROGRAM | chan, less128(e.dataB()), 0);
                            PmError error = Pm_WriteShort(portMidiDriver->getOutputStream(), seq->getCurrentMillisecondTimestampWithLatency(framePos), msg);
                            if (error != pmNoError) {
                                  qDebug("Portaudio: error %d", error);
                                  return;
                                  }
                            break;
                            }
                      // fall through
                case ME_PITCHBEND:
                      {
                      long msg = Pm_Message(e.type() | chan, less128(e.dataA()), less128(e.dataB()));
                      PmError error = Pm_WriteShort(portMidiDriver->getOutputStream(), seq->getCurrentMillisecondTimestampWithLatency(framePos), msg);
                      if (error != pmNoError) {
                            qDebug("Portaudio: error %d", error);
                            return;
                            }
                      }
                      break;

                case ME_PROGRAM:
                case ME_AFTERTOUCH:
                      {
                      long msg = Pm_Message(e.type() | chan, less128(e.dataA()), 0);
                      PmError error = Pm_WriteShort(portMidiDriver->getOutputStream(), seq->getCurrentMillisecondTimestampWithLatency(framePos), msg);
                      if (error != pmNoError) {
                            qDebug("Portaudio: error %d", error);
                            return;
                            }
                      }
                      break;
                case ME_SONGPOS:
                case ME_CLOCK:
                case ME_START:
                case ME_CONTINUE:
                case ME_STOP:
                      qDebug("Portaudio: event type %x not supported", e.type());
                      break;
                }
          }

    //---------------------------------------------------------
    //   currentApi
    //---------------------------------------------------------

    int Portaudio::currentApi() const
          {
          PaDeviceIndex idx = preferences.portaudioDevice;
          if (idx < 0)
                idx = Pa_GetDefaultOutputDevice();

          for (int api = 0; api < Pa_GetHostApiCount(); ++api) {
                const PaHostApiInfo* info = Pa_GetHostApiInfo(api);
                if (info) {
                      for (int k = 0; k < info->deviceCount; ++k) {
                            PaDeviceIndex i = Pa_HostApiDeviceIndexToDeviceIndex(api, k);
                            if (i == idx)
                                  return api;
                            }
                      }
                }
          qDebug("Portaudio: no current api found for device %d", idx);
          return -1;
          }

    //---------------------------------------------------------
    //   currentDevice
    //---------------------------------------------------------

    int Portaudio::currentDevice() const
          {
          PaDeviceIndex idx = preferences.portaudioDevice;
          if (idx < 0)
                idx = Pa_GetDefaultOutputDevice();

          for (int api = 0; api < Pa_GetHostApiCount(); ++api) {
                const PaHostApiInfo* info = Pa_GetHostApiInfo(api);
                if (info) {
                      for (int k = 0; k < info->deviceCount; ++k) {
                            PaDeviceIndex i = Pa_HostApiDeviceIndexToDeviceIndex(api, k);
                            if (i == idx)
                                  return k;
                            }
                      }
                }
          qDebug("Portaudio: no current ApiDevice found for device %d", idx);
          return -1;
          }
    }

↑ V668 There is no sense in testing the 'midiDriver' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

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