//=============================================================================
//  MusE Score
//  Linux Music Score Editor
//  $Id: jackaudio.cpp 5660 2012-05-22 14:17:39Z 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 "jackaudio.h"
#include "musescore.h"
#include "libmscore/mscore.h"
#include "preferences.h"
// #include "msynth/synti.h"
#include "seq.h"
#include "libmscore/score.h"
#include "libmscore/repeatlist.h"
#include "mscore/playpanel.h"
#include <jack/midiport.h>
 
// Prevent killing sequencer with wrong data
#define less128(__less) ((__less >=0 && __less <= 127) ? __less : 0)
 
namespace Ms {
 
//---------------------------------------------------------
//   JackAudio
//---------------------------------------------------------
 
JackAudio::JackAudio(Seq* s)
   : Driver(s)
      {
      client = 0;
      }
 
//---------------------------------------------------------
//   ~JackAudio
//---------------------------------------------------------
 
JackAudio::~JackAudio()
      {
      if (client) {
            stop();
            if (jack_client_close(client)) {
                  qDebug("jack_client_close() failed: %s",
                     strerror(errno));
                  }
            }
      }
 
//---------------------------------------------------------
//   updateOutPortCount
//   Add/remove JACK MIDI Out ports
//---------------------------------------------------------
 
void JackAudio::updateOutPortCount(int maxport)
      {
      if (!preferences.useJackMidi || maxport == midiOutputPorts.size())
            return;
      if (MScore::debugMode)
            qDebug()<<"JACK number of ports:"<<midiOutputPorts.size()<<", change to:"<<maxport;
 
      bool oldremember = preferences.rememberLastConnections;
      preferences.rememberLastConnections = true;
 
      if (maxport > midiOutputPorts.size()) {
            for (int i = midiOutputPorts.size(); i < maxport; ++i)
                  registerPort(QString("mscore-midi-%1").arg(i+1), false, true);
            restoreMidiConnections();
            }
      else if (maxport < midiOutputPorts.size()) {
            rememberMidiConnections();
            for(int i = midiOutputPorts.size() - 1; i >= maxport; --i) {
                  unregisterPort(midiOutputPorts[i]);
                  midiOutputPorts.removeAt(i);
                  }
            }
      preferences.rememberLastConnections = oldremember;
      }
 
//---------------------------------------------------------
//   registerPort
//---------------------------------------------------------
 
void JackAudio::registerPort(const QString& name, bool input, bool midi)
      {
      int portFlag         = input ? JackPortIsInput : JackPortIsOutput;
      const char* portType = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE;
 
      jack_port_t* port = jack_port_register(client, qPrintable(name), portType, portFlag, 0);
      if (port == 0) {
            qDebug("JackAudio:registerPort(%s) failed", qPrintable(name));
            return;
            }
      if (midi) {
            if (input)
                  midiInputPorts.append(port);
            else
                  midiOutputPorts.append(port);
            }
      else
            ports.append(port);
      }
 
//---------------------------------------------------------
//   unregisterPort
//---------------------------------------------------------
 
void JackAudio::unregisterPort(jack_port_t* port)
      {
      if (jack_port_is_mine(client,port)) {
            jack_port_unregister(client, port);
            port = 0;
            }
      else
            qDebug("Trying to unregister port that is not my!");
      }
 
//---------------------------------------------------------
//   inputPorts
//---------------------------------------------------------
 
QList<QString> JackAudio::inputPorts()
      {
      const char** ports = jack_get_ports(client, 0, 0, 0);
      QList<QString> clientList;
      for (const char** p = ports; p && *p; ++p) {
            jack_port_t* port = jack_port_by_name(client, *p);
            int flags = jack_port_flags(port);
            if (!(flags & JackPortIsInput))
                  continue;
            char buffer[128];
            strncpy(buffer, *p, 128);
            if (strncmp(buffer, "Mscore", 6) == 0)
                  continue;
            clientList.append(QString(buffer));
            }
      return clientList;
      }
 
//---------------------------------------------------------
//   connect
//---------------------------------------------------------
 
void JackAudio::connect(void* src, void* dst)
      {
      const char* sn = jack_port_name((jack_port_t*) src);
      const char* dn = jack_port_name((jack_port_t*) dst);
 
      if (sn == 0 || dn == 0) {
            qDebug("JackAudio::connect: unknown jack ports");
            return;
            }
      if (jack_connect(client, sn, dn)) {
            qDebug("jack connect <%s>%p - <%s>%p failed",
               sn, src, dn, dst);
            }
      }
 
//---------------------------------------------------------
//   connect
//---------------------------------------------------------
 
void JackAudio::connect(const char* src, const char* dst)
      {
      if (src == 0 || dst == 0) {
            qDebug("JackAudio::connect: unknown jack ports");
            return;
            }
      qDebug("JackAudio::connect <%s> <%s>", src, dst);
      int rv = jack_connect(client, src, dst);
      if (rv)
            qDebug("jack connect port <%s> - <%s> failed: %d", src, dst, rv);
      }
 
//---------------------------------------------------------
//   disconnect
//---------------------------------------------------------
 
void JackAudio::disconnect(void* src, void* dst)
      {
      const char* sn = jack_port_name((jack_port_t*) src);
      const char* dn = jack_port_name((jack_port_t*) dst);
      if (sn == 0 || dn == 0) {
            qDebug("JackAudio::disconnect: unknown jack ports");
            return;
            }
      if (jack_disconnect(client, sn, dn)) {
            qDebug("jack disconnect <%s> - <%s> failed", sn, dn);
            }
      }
 
//---------------------------------------------------------
//   start
//    return false on error
//---------------------------------------------------------
 
bool JackAudio::start(bool hotPlug)
      {
      bool oldremember = preferences.rememberLastConnections;
      if (hotPlug)
            preferences.rememberLastConnections = true;
 
      if (jack_activate(client)) {
            qDebug("JACK: cannot activate client");
            return false;
            }
      /* connect the ports. Note: you can't do this before
         the client is activated, because we can't allow
         connections to be made to clients that aren't
         running.
       */
      if (preferences.useJackAudio)
            restoreAudioConnections();
      if (preferences.useJackMidi)
            restoreMidiConnections();
 
      if (hotPlug)
            preferences.rememberLastConnections = oldremember;
      return true;
      }
 
//---------------------------------------------------------
//   stop
//    return false on error
//---------------------------------------------------------
 
bool JackAudio::stop()
      {
      if (preferences.useJackMidi)
            rememberMidiConnections();
      if (preferences.useJackAudio)
            rememberAudioConnections();
 
      if (jack_deactivate(client)) {
            qDebug("cannot deactivate client");
            return false;
            }
      return true;
      }
 
//---------------------------------------------------------
//   framePos
//---------------------------------------------------------
 
int JackAudio::framePos() const
      {
      jack_nframes_t n = jack_frame_time(client);
      return (int)n;
      }
 
//---------------------------------------------------------
//   freewheel_callback
//---------------------------------------------------------
 
static void freewheel_callback(int /*starting*/, void*)
      {
      }
 
//---------------------------------------------------------
//   sampleRateCallback
//---------------------------------------------------------
 
int sampleRateCallback(jack_nframes_t sampleRate, void*)
      {
      qDebug("JACK: sample rate changed: %d", sampleRate);
      MScore::sampleRate = sampleRate;
      return 0;
      }
 
//---------------------------------------------------------
//   bufferSizeCallback called if JACK buffer changed
//---------------------------------------------------------
 
int bufferSizeCallback(jack_nframes_t nframes, void *arg)
      {
      JackAudio* audio = (JackAudio*)arg;
      audio->setBufferSize(nframes);
      return 0;
      }
 
static void registration_callback(jack_port_id_t, int, void*)
      {
//      qDebug("JACK: registration changed");
      }
 
static int graph_callback(void*)
      {
//      qDebug("JACK: graph changed");
      return 0;
      }
 
//---------------------------------------------------------
//   timebase
//---------------------------------------------------------
 
void JackAudio::timebase(jack_transport_state_t state, jack_nframes_t /*nframes*/, jack_position_t *pos, int /*new_pos*/, void *arg)
      {
      JackAudio* audio = (JackAudio*)arg;
      if (!audio->seq->score()) {
            if (state==JackTransportLooping || state==JackTransportRolling)
                  audio->stopTransport();
            }
      else if (audio->seq->isRunning()) {
            if (!audio->seq->score()->repeatList() || !audio->seq->score()->sigmap())
                  return;
 
            pos->valid = JackPositionBBT;
            int curTick = audio->seq->score()->repeatList()->utick2tick(audio->seq->getCurTick());
            int bar,beat,tick;
            audio->seq->score()->sigmap()->tickValues(curTick, &bar, &beat, &tick);
            // Providing the final tempo
            pos->beats_per_minute = 60 * audio->seq->curTempo() * audio->seq->score()->tempomap()->relTempo();
            pos->ticks_per_beat   = MScore::division;
            pos->tick             = tick;
            pos->bar              = bar+1;
            pos->beat             = beat+1;
 
            if (audio->timeSigTempoChanged) {
                  Fraction timeSig = audio->seq->score()->sigmap()->timesig(curTick).nominal();
                  pos->beats_per_bar =  timeSig.numerator();
                  pos->beat_type = timeSig.denominator();
                  audio->timeSigTempoChanged = false;
                  qDebug()<<"Time signature changed: "<< pos->beats_per_minute<<", bar: "<< pos->bar<<",beat: "<<pos->beat<<", tick:"<<pos->tick<<", time sig: "<<pos->beats_per_bar<<"/"<<pos->beat_type;
                  }
            }
      // TODO: Handle new_pos
      }
//---------------------------------------------------------
//   processAudio
//    JACK callback
//---------------------------------------------------------
 
int JackAudio::processAudio(jack_nframes_t frames, void* p)
      {
      JackAudio* audio = (JackAudio*)p;
      // Prevent from crash if score not opened yet
      if(!audio->seq->score())
            return 0;
 
      float* l;
      float* r;
      if (preferences.useJackAudio && audio->ports.size() == 2) {
            l = (float*)jack_port_get_buffer(audio->ports[0], frames);
            r = (float*)jack_port_get_buffer(audio->ports[1], frames);
            }
      else {
            l = 0;
            r = 0;
            }
      if (preferences.useJackMidi) {
            foreach(jack_port_t* port, audio->midiOutputPorts) {
                  void* portBuffer = jack_port_get_buffer(port, frames);
                  jack_midi_clear_buffer(portBuffer);
                  }
            foreach(jack_port_t* port, audio->midiInputPorts) {
                  void* portBuffer = jack_port_get_buffer(port, frames);
                  if (portBuffer) {
                        jack_nframes_t n = jack_midi_get_event_count(portBuffer);
                        for (jack_nframes_t i = 0; i < n; ++i) {
                              jack_midi_event_t event;
                              int r = jack_midi_event_get(&event, portBuffer, i);
                              if (r != 0)
                                    continue;
                              int nn = event.size;
                              int type = event.buffer[0];
                              if (nn && (type == ME_CLOCK || type == ME_SENSE))
                                    continue;
                              Event e;
                              e.setChannel(type & 0xf);
                              type &= 0xf0;
                              e.setType(type);
                              if (type == ME_NOTEON || type == ME_NOTEOFF) {
                                    e.setPitch(event.buffer[1]);
                                    e.setVelo(event.buffer[2]);
                                    audio->seq->eventToGui(e);
                                    }
                              else if (type == ME_CONTROLLER) {
                                    e.setController(event.buffer[1]);
                                    e.setValue(event.buffer[2]);
                                    audio->seq->eventToGui(e);
                                    }
                              }
                        }
                  }
            }
      if (l && r) {
            float buffer[frames * 2];
            audio->seq->process((unsigned)frames, buffer);
            float* sp = buffer;
            for (unsigned i = 0; i < frames; ++i) {
                  *l++ = *sp++;
                  *r++ = *sp++;
                  }
            }
      else {
            // JACK MIDI only
            float buffer[frames * 2];
            audio->seq->process((unsigned)frames, buffer);
            }
      return 0;
      }
 
//---------------------------------------------------------
//   jackError
//---------------------------------------------------------
 
static void jackError(const char *s)
      {
      qDebug("JACK ERROR: %s", s);
      }
 
//---------------------------------------------------------
//   noJackError
//---------------------------------------------------------
 
static void noJackError(const char*  s )
      {
      qDebug("noJACK ERROR: %s", s);
      }
 
//---------------------------------------------------------
//   init
//    return false on error
//---------------------------------------------------------
 
bool JackAudio::init(bool hot)
      {
      if (hot) {
            hotPlug();
            return true;
            }
      jack_set_error_function(noJackError);
 
      client = 0;
      timeSigTempoChanged = false;
      fakeState = Transport::STOP;
      strcpy(_jackName, "mscore");
 
      jack_options_t options = (jack_options_t)0;
      jack_status_t status;
      client = jack_client_open(_jackName, options, &status);
 
      if (client == 0) {
            qDebug("JackAudio()::init(): failed, status 0x%0x", status);
            return false;
            }
 
      jack_set_error_function(jackError);
      jack_set_process_callback(client, processAudio, this);
      //jack_on_shutdown(client, processShutdown, this);
      jack_set_sample_rate_callback(client, sampleRateCallback, this);
      jack_set_port_registration_callback(client, registration_callback, this);
      jack_set_graph_order_callback(client, graph_callback, this);
      jack_set_freewheel_callback (client, freewheel_callback, this);
      if (preferences.jackTimebaseMaster)
            setTimebaseCallback();
      if (jack_set_buffer_size_callback (client, bufferSizeCallback, this) != 0)
            qDebug("Can not set bufferSizeCallback");
      _segmentSize  = jack_get_buffer_size(client);
 
      MScore::sampleRate = sampleRate();
      // register mscore left/right output ports
      if (preferences.useJackAudio) {
            registerPort("left", false, false);
            registerPort("right", false, false);
            }
 
      if (preferences.useJackMidi) {
            registerPort(QString("mscore-midi-1"), false, true);
            registerPort(QString("mscore-midiin-1"), true, true);
            }
      return true;
      }
 
//---------------------------------------------------------
//   startTransport
//---------------------------------------------------------
 
void JackAudio::startTransport()
      {
      if (preferences.useJackTransport)
            jack_transport_start(client);
      else
            fakeState = Transport::PLAY;
      }
 
//---------------------------------------------------------
//   stopTrasnport
//---------------------------------------------------------
 
void JackAudio::stopTransport()
      {
      if (preferences.useJackTransport)
            jack_transport_stop(client);
      else
            fakeState = Transport::STOP;
      }
 
//---------------------------------------------------------
//   getState
//---------------------------------------------------------
 
Transport JackAudio::getState()
      {
      if (!preferences.useJackTransport)
            return fakeState;
      int transportState = jack_transport_query(client, NULL);
      switch (transportState) {
            case JackTransportStopped:  return Transport::STOP;
            case JackTransportLooping:
            case JackTransportRolling:  return Transport::PLAY;
            case JackTransportStarting: return seq->isPlaying()?Transport::PLAY:Transport::STOP;// Keep current state
            default:
                  return Transport::STOP;
            }
      }
 
//---------------------------------------------------------
//   putEvent
//---------------------------------------------------------
 
void JackAudio::putEvent(const NPlayEvent& e, unsigned framePos)
      {
      if (!preferences.useJackMidi)
            return;
 
      int portIdx = seq->score()->midiPort(e.channel());
      int chan    = seq->score()->midiChannel(e.channel());
 
// qDebug("JackAudio::putEvent %d:%d  pos %d(%d)", portIdx, chan, framePos, _segmentSize);
 
      if (portIdx < 0 || portIdx >= midiOutputPorts.size()) {
            qDebug("JackAudio::putEvent: invalid port %d", portIdx);
            return;
            }
      jack_port_t* port = midiOutputPorts[portIdx];
      if (midiOutputTrace) {
            const char* portName = jack_port_name(port);
            int a     = e.dataA();
            int b     = e.dataB();
            qDebug("MidiOut<%s>: jackMidi: %02x %02x %02x, chan: %i", portName, e.type(), a, b, chan);
            // e.dump();
            }
      void* pb = jack_port_get_buffer(port, _segmentSize);
 
      if (pb == NULL) {
            qDebug()<<"jack_port_get_buffer failed, cannot send anything";
            }
 
      if (framePos >= _segmentSize) {
            qDebug("JackAudio::putEvent: time out of range %d(seg=%d)", framePos, _segmentSize);
            if (framePos > _segmentSize)
                  framePos = _segmentSize - 1;
            }
 
      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
                        unsigned char* p = jack_midi_event_reserve(pb, framePos, 2);
                        if (p == 0) {
                              qDebug("JackMidi: buffer overflow, event lost");
                              return;
                              }
                        p[0] = ME_PROGRAM | chan;
                        p[1] = less128(e.dataB());
                        break;
                        }
                  //fall through
            case ME_PITCHBEND:
                  {
                  unsigned char* p = jack_midi_event_reserve(pb, framePos, 3);
                  if (p == 0) {
                        qDebug("JackMidi: buffer overflow, event lost");
                        return;
                        }
                  p[0] = e.type() | chan;
                  p[1] = less128(e.dataA());
                  p[2] = less128(e.dataB());
                  }
                  break;
 
            case ME_PROGRAM:
            case ME_AFTERTOUCH:
                  {
                  unsigned char* p = jack_midi_event_reserve(pb, framePos, 2);
                  if (p == 0) {
                        qDebug("JackMidi: buffer overflow, event lost");
                        return;
                        }
                  p[0] = e.type() | chan;
                  p[1] = less128(e.dataA());
                  }
                  break;
          // Do we really need to handle ME_SYSEX?
          /*  case ME_SYSEX:
                  {
                  const unsigned char* data = e.edata();
                  int len = e.len();
                  unsigned char* p = jack_midi_event_reserve(pb, framePos, len+2);
                  if (p == 0) {
                        qDebug("JackMidi: buffer overflow, event lost");
                        return;
                        }
                  p[0]     = 0xf0;
                  p[len+1] = 0xf7;
                  memcpy(p+1, data, len);
                  }
                  break;*/
            case ME_SONGPOS:
            case ME_CLOCK:
            case ME_START:
            case ME_CONTINUE:
            case ME_STOP:
                  qDebug("JackMidi: event type %x not supported", e.type());
                  break;
            }
      }
 
//---------------------------------------------------------
//   midiRead
//---------------------------------------------------------
 
void JackAudio::midiRead()
      {
//      midiDriver->read();
      }
 
//---------------------------------------------------------
//   handleTimeSigTempoChanged
//   Called after tempo or time signature
//   changed while playback
//---------------------------------------------------------
 
void JackAudio::handleTimeSigTempoChanged()
      {
      timeSigTempoChanged = true;
      }
 
//---------------------------------------------------------
//   checkTransportSeek
//   The opposite of Timebase master:
//   check JACK Transport for a new position or tempo.
//---------------------------------------------------------
 
void JackAudio::checkTransportSeek(int cur_frame, int nframes, bool inCountIn)
      {
      if (!seq || !seq->score() || inCountIn)
            return;
 
      // Obtaining the current JACK Transport position
      jack_position_t pos;
      jack_transport_query(client, &pos);
 
      if (preferences.useJackTransport) {
            if (mscore->getPlayPanel() && mscore->getPlayPanel()->isTempoSliderPressed())
                  return;
            int cur_utick = seq->score()->utime2utick((qreal)cur_frame / MScore::sampleRate);
            int utick     = seq->score()->utime2utick((qreal)pos.frame / MScore::sampleRate);
 
            // Conversion is not precise, should check frames and uticks
            if (labs((long int)cur_frame - (long int)pos.frame)>nframes + 1 && abs(utick - cur_utick)> seq->score()->utime2utick((qreal)nframes / MScore::sampleRate) + 1) {
                  if (MScore::debugMode)
                        qDebug()<<"JACK Transport position changed, cur_frame: "<<cur_frame<<",pos.frame: "<<pos.frame<<", frame diff: "<<labs((long int)cur_frame - (long int)pos.frame)<<"cur utick:"<<cur_utick<<",seek to utick: "<<utick<<", tick diff: "<<abs(utick - cur_utick);
                  seq->seekRT(utick);
                  }
            }
 
      // Tempo
      if (!preferences.jackTimebaseMaster  && (pos.valid & JackPositionBBT)) {
            if (!seq->score()->tempomap())
                  return;
 
            if (int(pos.beats_per_minute) != int(60 * seq->curTempo() * seq->score()->tempomap()->relTempo())) {
                  if (MScore::debugMode)
                        qDebug()<<"JACK Transport tempo changed! JACK bpm: "<<(int)pos.beats_per_minute<<", current bpm: "<<int(60 * seq->curTempo() * seq->score()->tempomap()->relTempo());
                  if (60 * seq->curTempo() == 0.0)
                        return;
                  qreal newRelTempo = pos.beats_per_minute / (60* seq->curTempo());
                  seq->setRelTempo(newRelTempo);
                  // Update UI
                  if (mscore->getPlayPanel()) {
                        mscore->getPlayPanel()->setRelTempo(newRelTempo);
                        mscore->getPlayPanel()->setTempo(seq->curTempo() * newRelTempo);
                        }
                  }
            }
      }
 
//---------------------------------------------------------
//   seekTransport
//---------------------------------------------------------
 
void JackAudio::seekTransport(int utick)
      {
      if (MScore::debugMode)
            qDebug()<<"jack locate to utick: "<<utick<<", frame: "<<int(seq->score()->utick2utime(utick) * MScore::sampleRate);
      jack_transport_locate(client, seq->score()->utick2utime(utick) * MScore::sampleRate);
      }
 
//---------------------------------------------------------
//   setTimebaseCallback
//---------------------------------------------------------
 
void JackAudio::setTimebaseCallback()
      {
      int errCode = jack_set_timebase_callback(client, 0, timebase, this); // 0: force set timebase
      if (errCode == 0) {
            if (MScore::debugMode)
                  qDebug("Registered as JACK Timebase Master.");
            }
      else {
            preferences.jackTimebaseMaster = false;
            qDebug("Unable to take over JACK Timebase, error code: %i",errCode);
            }
      }
 
//---------------------------------------------------------
//   releaseTimebaseCallback
//---------------------------------------------------------
 
void JackAudio::releaseTimebaseCallback()
      {
      int errCode = jack_release_timebase(client);
      if (errCode == 0)
            qDebug("Unregistered as JACK Timebase Master");
      else
            qDebug("Unable to unregister as JACK Timebase Master (not a Timebase Master?), error code: %i", errCode);
      }
 
//---------------------------------------------------------
//   rememberAudioConnections
//---------------------------------------------------------
 
void JackAudio::rememberAudioConnections()
      {
      if (!preferences.rememberLastConnections)
            return;
      if (MScore::debugMode)
            qDebug("Saving audio connections...");
      QSettings settings;
      settings.setValue(QString("audio-0-connections"), 0);
      settings.setValue(QString("audio-1-connections"), 0);
      int port = 0;
      foreach(jack_port_t* mp, ports) {
            const char** cc = jack_port_get_connections(mp);
            const char** c = cc;
            int idx = 0;
            while (c) {
                  const char* p = *c++;
                  if (p == 0)
                        break;
                  settings.setValue(QString("audio-%1-%2").arg(port).arg(idx), p);
                  ++idx;
                  }
            settings.setValue(QString("audio-%1-connections").arg(port), idx);
            free((void*)cc);
            ++port;
            }
      }
 
//---------------------------------------------------------
//   restoreAudioConnections
//   Connect to the ports in Preferences->I/O
//---------------------------------------------------------
 
void JackAudio::restoreAudioConnections()
      {
      for (auto p : ports)
            jack_port_disconnect(client, p);
 
      QList<QString> portList = inputPorts();
      QList<QString>::iterator pi = portList.begin();
 
      QSettings settings;
      // Number of saved ports
      int n = settings.value(QString("audio-0-connections"), 0).toInt() + settings.value(QString("audio-1-connections"), 0).toInt();
 
      // Connecting to system ports
      if (!preferences.rememberLastConnections || n == 0) {
            if (MScore::debugMode)
                  qDebug("Connecting to system ports...");
            for (auto p : ports) {
                  const char* src = jack_port_name(p);
                  if (pi != portList.end()) {
                        connect(src, qPrintable(*pi));
                        ++pi;
                        }
                  }
            return;
            }
      if (MScore::debugMode)
            qDebug("Restoring audio connections...");
      // Connecting to saved ports
      int nPorts = ports.size();
      for (int i = 0; i < nPorts; ++i) {
            int n = settings.value(QString("audio-%1-connections").arg(i), 0).toInt();
            const char* src = jack_port_name(ports[i]);
            for (int k = 0; k < n; ++k) {
                  QString dst = settings.value(QString("audio-%1-%2").arg(i).arg(k), "").toString();
                  if (!dst.isEmpty()) {
                        if (jack_port_connected_to(ports[i], qPrintable(dst)))
                              qDebug()<<"Audio port "<<src<<" ("<<i<<") already connected to "<<qPrintable(dst);
                        else
                              connect(src, qPrintable(dst));
                        }
                  }
            }
      }
 
//---------------------------------------------------------
//   rememberMidiConnections
//---------------------------------------------------------
 
void JackAudio::rememberMidiConnections()
      {
      if (!preferences.rememberLastConnections)
            return;
      if (MScore::debugMode)
            qDebug("Saving midi connections...");
      QSettings settings;
      int port = 0;
      foreach(jack_port_t* mp, midiOutputPorts) {
            const char** cc = jack_port_get_connections(mp);
            const char** c = cc;
            int idx = 0;
            while (c) {
                  const char* p = *c++;
                  if (p == 0)
                        break;
                  settings.setValue(QString("midi-%1-%2").arg(port).arg(idx), p);
                  ++idx;
                  }
            settings.setValue(QString("midi-%1-connections").arg(port), idx);
            free((void*)cc);
            ++port;
            }
 
      port = 0;
      foreach(jack_port_t* mp, midiInputPorts) {
            const char** cc = jack_port_get_connections(mp);
            const char** c = cc;
            int idx = 0;
            while (c) {
                  const char* p = *c++;
                  if (p == 0)
                        break;
                  settings.setValue(QString("midiin-%1-%2").arg(idx).arg(port), p);
                  ++idx;
                  }
            settings.setValue(QString("midiin-%1-connections").arg(port), idx);
            free((void*)cc);
            ++port;
            }
      }
 
//---------------------------------------------------------
//   restoreMidiConnections
//   Connects to the ports from previous connection
//---------------------------------------------------------
 
void JackAudio::restoreMidiConnections()
      {
      if (!preferences.rememberLastConnections)
            return;
      if (MScore::debugMode)
            qDebug("Restoring midi connections...");
      QSettings settings;
      int nPorts = midiOutputPorts.size();
      for (int i = 0; i < nPorts; ++i) {
            int n = settings.value(QString("midi-%1-connections").arg(i), 0).toInt();
            const char* src = jack_port_name(midiOutputPorts[i]);
            for (int k = 0; k < n; ++k) {
                  QString dst = settings.value(QString("midi-%1-%2").arg(i).arg(k), "").toString();
                  if (!dst.isEmpty()) {
                        if (jack_port_connected_to(midiOutputPorts[i], qPrintable(dst)))
                              continue;
                        connect(src, qPrintable(dst));
                        }
                  }
            }
      nPorts = midiInputPorts.size();
      for (int i = 0; i < nPorts; ++i) {
            int n = settings.value(QString("midiin-%1-connections").arg(i), 0).toInt();
            const char* dst = jack_port_name(midiInputPorts[i]);
            for (int k = 0; k < n; ++k) {
                  QString src = settings.value(QString("midiin-%1-%2").arg(k).arg(i), "").toString();
                  if (!src.isEmpty()) {
                        if (jack_port_connected_to(midiInputPorts[i], qPrintable(src)))
                              continue;
                        connect(qPrintable(src), dst);
                        }
                  }
            }
      }
 
//---------------------------------------------------------
//   hotPlug
//   Change driver settings without unload
//---------------------------------------------------------
 
void JackAudio::hotPlug()
      {
      bool oldremember = preferences.rememberLastConnections;
      preferences.rememberLastConnections = true;
      // Remember connections before calling jack_deactivate() - it disconnects all ports
      rememberMidiConnections();
      if (ports.size() != 0)
            rememberAudioConnections();
 
      // We must set callbacks only on inactive client
      if (jack_deactivate(client))
            qDebug("cannot deactivate client");
 
      // Audio connections
      if (preferences.useJackAudio) {
            if (ports.size() == 0) {
                  registerPort("left", false, false);
                  registerPort("right", false, false);
                  }
            }
      else if (!preferences.useJackAudio) {
            foreach(jack_port_t* p, ports) {
                  unregisterPort(p);
                  ports.removeOne(p);
                  }
            }
 
      // Midi connections
      if (preferences.useJackMidi) {
            if (midiInputPorts.size() == 0)
                  registerPort(QString("mscore-midiin-1"), true, true);
            }
      else { // No midi
            updateOutPortCount(0);
            if (midiInputPorts.size() != 0) {
                  unregisterPort(midiInputPorts[0]);
                  midiInputPorts.removeOne(midiInputPorts[0]);
                  }
            }
 
      // Timebase Master callback
      if (preferences.jackTimebaseMaster)
            setTimebaseCallback();
      else
            releaseTimebaseCallback();
 
      preferences.rememberLastConnections = oldremember;
      }
}

V547 Expression '!preferences.useJackAudio' is always true.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: _segmentSize, fakeState, _jackName, timeSigTempoChanged.