//=============================================================================
//  MuseScore
//  Linux Music Score Editor
//  $Id: mixer.cpp 5651 2012-05-19 15:57:26Z lasconic $
//
//  Copyright (C) 2002-2016 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 "musescore.h"
#include "libmscore/score.h"
#include "libmscore/part.h"
#include "mixer.h"
#include "seq.h"
#include "libmscore/undo.h"
#include "synthcontrol.h"
#include "synthesizer/msynthesizer.h"
#include "preferences.h"
#include <qmessagebox.h>
#include <accessibletoolbutton.h>
 
namespace Ms {
 
#define _setValue(__x, __y) \
      __x->blockSignals(true); \
      __x->setValue(__y); \
      __x->blockSignals(false);
 
#define _setChecked(__x, __y) \
      __x->blockSignals(true); \
      __x->setChecked(__y); \
      __x->blockSignals(false);
 
//---------------------------------------------------------
//   PartEdit
//---------------------------------------------------------
 
PartEdit::PartEdit(QWidget* parent)
   : QWidget(parent, Qt::Dialog)
      {
      setupUi(this);
      connect(patch,          SIGNAL(activated(int)),           SLOT(patchChanged(int)));
      connect(volume,         SIGNAL(valueChanged(double,int)), SLOT(volChanged(double)));
      connect(pan,            SIGNAL(valueChanged(double,int)), SLOT(panChanged(double)));
      connect(chorus,         SIGNAL(valueChanged(double,int)), SLOT(chorusChanged(double)));
      connect(reverb,         SIGNAL(valueChanged(double,int)), SLOT(reverbChanged(double)));
      connect(mute,           SIGNAL(toggled(bool)),            SLOT(muteChanged(bool)));
      connect(solo,           SIGNAL(toggled(bool)),            SLOT(soloToggled(bool)));
      connect(drumset,        SIGNAL(toggled(bool)),            SLOT(drumsetToggled(bool)));
      connect(portSpinBox,    SIGNAL(valueChanged(int)),        SLOT(midiChannelChanged(int)));
      connect(channelSpinBox, SIGNAL(valueChanged(int)),        SLOT(midiChannelChanged(int)));
      connect(expand,         SIGNAL(toggled(bool)),            SLOT(expandToggled(bool)));
 
      expandToggled(false);
      }
 
//---------------------------------------------------------
//   expandToggled
//---------------------------------------------------------
 
void PartEdit::expandToggled(bool val)
      {
      details->setVisible(val);
      }
 
//---------------------------------------------------------
//   setPart
//---------------------------------------------------------
 
void PartEdit::setPart(Part* p, Channel* a)
      {
      Channel dummy;
      channel = a;
      part    = p;
      QString s = part->partName();
      if (!a->name.isEmpty()) {
            if (a->name != "normal") {
                  s += "-";
                  s += qApp->translate("InstrumentsXML", a->name.toUtf8().data());
                  }
            }
      partName->setText(s);
      _setValue(volume, a->volume);
      volume->setDclickValue1(dummy.volume);
      volume->setDclickValue2(dummy.volume);
      _setValue(reverb, a->reverb);
      reverb->setDclickValue1(dummy.reverb);
      reverb->setDclickValue2(dummy.reverb);
      _setValue(chorus, a->chorus);
 
      chorus->setDclickValue1(dummy.chorus);
      chorus->setDclickValue2(dummy.chorus);
      _setValue(pan, a->pan);
 
      pan->setDclickValue1(0);
      pan->setDclickValue2(0);
      for (int i = 0; i < patch->count(); ++i) {
            MidiPatch* p = (MidiPatch*)patch->itemData(i, Qt::UserRole).value<void*>();
            if (a->synti == p->synti && a->program == p->prog && a->bank == p->bank) {
                  patch->setCurrentIndex(i);
                  break;
                  }
            }
      _setChecked(drumset, p->instrument()->useDrumset());
      _setValue(portSpinBox,    part->masterScore()->midiMapping(a->channel)->port + 1);
      _setValue(channelSpinBox, part->masterScore()->midiMapping(a->channel)->channel + 1);
 
      QHBoxLayout* hb = voiceButtonBox;
      int idx = 0;
      for (Staff* staff : *part->staves()) {
            for (int voice = 0; voice < VOICES; ++voice) {
                  if (!voiceButtons.value(idx)) {
                        QToolButton* tb = new QToolButton;
                        tb->setText(QString("%1").arg(voice+1));
                        tb->setCheckable(true);
                        tb->setChecked(staff->playbackVoice(voice));
                        tb->setFocusPolicy(Qt::ClickFocus);
                        tb->setToolButtonStyle(Qt::ToolButtonTextOnly);
                        QPalette p(tb->palette());
                        p.setColor(QPalette::Base, MScore::selectColor[voice]);
                        tb->setPalette(p);
                        hb->addWidget(tb);
                        voiceButtons.append(tb);
                        connect(tb, SIGNAL(clicked()), SLOT(playbackVoiceChanged()));
                        }
                  ++idx;
                  }
            hb->addStretch(5);
            }
      while (voiceButtons.value(idx)) {
            QToolButton* tb = voiceButtons.value(idx);
            if (!tb)
                  break;
            voiceButtons.takeAt(idx);
            delete tb;
            }
      }
 
//---------------------------------------------------------
//   playbackVoiceChanged
//---------------------------------------------------------
 
void PartEdit::playbackVoiceChanged()
      {
      int idx = 0;
      Score* score = part->score();
      score->startCmd();
      for (Staff* staff : *part->staves()) {
            for (int voice = 0; voice < VOICES; ++voice) {
                  QToolButton* tb = voiceButtons[idx];
                  bool val = tb->isChecked();
                  if (val != staff->playbackVoice(voice)) {
                        switch (voice) {
                              case 0:
                              printf("undo\n");
                                    score->undoChangeProperty(staff, P_ID::PLAYBACK_VOICE1, val);
                                    break;
                              case 1:
                                    score->undoChangeProperty(staff, P_ID::PLAYBACK_VOICE2, val);
                                    break;
                              case 2:
                                    score->undoChangeProperty(staff, P_ID::PLAYBACK_VOICE3, val);
                                    break;
                              case 3:
                                    score->undoChangeProperty(staff, P_ID::PLAYBACK_VOICE4, val);
                                    break;
                              }
                        }
                  ++idx;
                  }
            }
      score->endCmd();
      }
 
//---------------------------------------------------------
//   Mixer
//---------------------------------------------------------
 
Mixer::Mixer(QWidget* parent)
   : QScrollArea(parent)
      {
      setObjectName("Mixer");
      setWidgetResizable(true);
      setWindowFlags(Qt::Tool);
      setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
 
      QWidget* area = new QWidget(this);
      vb = new QVBoxLayout;
      vb->setMargin(0);
      vb->setSpacing(0);
      area->setLayout(vb);
      setWidget(area);
 
      enablePlay = new EnablePlayForWidget(this);
      readSettings();
      retranslate(true);
      }
 
//---------------------------------------------------------
//   retranslate
//---------------------------------------------------------
 
void Mixer::retranslate(bool firstTime)
      {
      setWindowTitle(tr("Mixer"));
      if (!firstTime) {
            for (int i = 0; i < vb->count(); i++) {
                  PartEdit* p = partEdit(i);
                  if (p) p->retranslateUi(p);
                  }
            }
      }
 
//---------------------------------------------------------
//   closeEvent
//---------------------------------------------------------
 
void Mixer::closeEvent(QCloseEvent* ev)
      {
      emit closed(false);
      QWidget::closeEvent(ev);
      }
 
//---------------------------------------------------------
//   showEvent
//---------------------------------------------------------
 
void Mixer::showEvent(QShowEvent* e)
      {
      enablePlay->showEvent(e);
      QScrollArea::showEvent(e);
      activateWindow();
      setFocus();
      }
 
//---------------------------------------------------------
//   eventFilter
//---------------------------------------------------------
 
bool Mixer::eventFilter(QObject* obj, QEvent* e)
      {
      if (enablePlay->eventFilter(obj, e))
            return true;
      return QScrollArea::eventFilter(obj, e);
      }
 
void Mixer::keyPressEvent(QKeyEvent* ev) {
      if (ev->key() == Qt::Key_Escape && ev->modifiers() == Qt::NoModifier) {
            close();
            return;
            }
      QWidget::keyPressEvent(ev);
      }
 
//---------------------------------------------------------
//   changeEvent
//---------------------------------------------------------
 
void Mixer::changeEvent(QEvent *event)
      {
      QScrollArea::changeEvent(event);
      if (event->type() == QEvent::LanguageChange)
            retranslate();
      }
 
//---------------------------------------------------------
//   updateAll
//---------------------------------------------------------
 
void Mixer::updateAll(MasterScore* score)
      {
      cs = score;
      int n = -vb->count();
      if (n < 0) {
            QLayoutItem* wi = vb->itemAt(vb->count()-1);
            vb->removeItem(wi);
            delete wi;
            n += 1;
            }
      if (cs) {
            QList<MidiMapping>* mm = cs->midiMapping();
            n += mm->size();
            }
      while (n < 0) {
            QLayoutItem* wi = vb->itemAt(0);
            vb->removeItem(wi);
            delete wi->widget();
            delete wi;
            ++n;
            }
      while (n > 0) {
            PartEdit* pe = new PartEdit;
            connect(pe, SIGNAL(soloChanged(bool)), SLOT(updateSolo(bool)));
            vb->addWidget(pe);
            --n;
            }
      vb->addStretch(5);
      patchListChanged();
      }
 
//---------------------------------------------------------
//   partEdit
//---------------------------------------------------------
 
PartEdit* Mixer::partEdit(int index)
      {
      if (index < vb->count()) {
            QWidgetItem* wi = (QWidgetItem*)(vb->itemAt(index));
            return (PartEdit*) wi->widget();
            }
      return 0;
      }
 
//---------------------------------------------------------
//   patchListChanged
//---------------------------------------------------------
 
void Mixer::patchListChanged()
      {
      if (!cs)
            return;
 
      int idx = 0;
      QList<MidiMapping>* mm = cs->midiMapping();
      const QList<MidiPatch*> pl = synti->getPatchInfo();
      for (const MidiMapping& m : *mm) {
            QLayoutItem* wi  = (QWidgetItem*)(vb->itemAt(idx));
            if (!wi->widget())
                  continue;
            PartEdit* pe     = (PartEdit*)(wi->widget());
            bool drum        = m.part->instrument()->useDrumset();
            pe->patch->clear();
            for (const MidiPatch* p : pl) {
                  if (p->drum == drum)
                        pe->patch->addItem(p->name, QVariant::fromValue<void*>((void*)p));
                  }
            pe->setPart(m.part, m.articulation);
            idx++;
            }
      // Update solo & mute only after creating all controls (we need to sync all controls)
      idx = 0;
      for (const MidiMapping& m : *mm) {
            QLayoutItem* wi = (QWidgetItem*)(vb->itemAt(idx));
            if (!wi->widget())
                  continue;
            PartEdit* pe    = (PartEdit*)(wi->widget());
            pe->mute->setChecked(m.articulation->mute);
            pe->solo->setChecked(m.articulation->solo);
            idx++;
            }
      }
 
//---------------------------------------------------------
//   midiPrefsChanged
//---------------------------------------------------------
 
void Mixer::midiPrefsChanged(bool showMidiControls)
      {
      if (!cs)
            return;
      for (int i = 0; i < vb->count(); i++ ){
            QWidgetItem* wi = (QWidgetItem*)(vb->itemAt(i));
            PartEdit* pe    = (PartEdit*)(wi->widget());
            pe->channelLabel  ->setVisible(showMidiControls);
            pe->portLabel     ->setVisible(showMidiControls);
            pe->channelSpinBox->setVisible(showMidiControls);
            pe->portSpinBox   ->setVisible(showMidiControls);
 
            if (!showMidiControls)
                  pe->hboxLayout->setSpacing(20);
            else
                  pe->hboxLayout->setSpacing(5);
            }
      }
 
//---------------------------------------------------------
//   showMixer
//---------------------------------------------------------
 
void MuseScore::showMixer(bool val)
      {
      QAction* a = getAction("toggle-mixer");
      if (mixer == 0) {
            mixer = new Mixer(this);
            mscore->stackUnder(mixer);
            if (synthControl)
                  connect(synthControl, SIGNAL(soundFontChanged()), mixer, SLOT(patchListChanged()));
            connect(synti, SIGNAL(soundFontChanged()), mixer, SLOT(patchListChanged()));
            connect(mixer, SIGNAL(closed(bool)), a, SLOT(setChecked(bool)));
            }
      mixer->updateAll(cs->masterScore());
      mixer->setVisible(val);
      }
 
//---------------------------------------------------------
//   patchChanged
//---------------------------------------------------------
 
void PartEdit::patchChanged(int n, bool syncControls)
      {
      if (part == 0)
            return;
 
      const MidiPatch* p = (MidiPatch*)patch->itemData(n, Qt::UserRole).value<void*>();
      if (p == 0) {
            qDebug("PartEdit::patchChanged: no patch");
            return;
            }
      Score* score = part->score();
      if (score) {
            score->startCmd();
            score->undo(new ChangePatch(score, channel, p));
            score->setLayoutAll();
            score->endCmd();
            }
      channel->updateInitList();
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   volChanged
//---------------------------------------------------------
 
void PartEdit::volChanged(double val, bool syncControls)
      {
      int iv = lrint(val);
      seq->setController(channel->channel, CTRL_VOLUME, iv);
      channel->volume = iv;
      channel->updateInitList();
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   panChanged
//---------------------------------------------------------
 
void PartEdit::panChanged(double val, bool syncControls)
      {
      int iv = lrint(val);
      seq->setController(channel->channel, CTRL_PANPOT, iv);
      channel->pan = iv;
      channel->updateInitList();
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   reverbChanged
//---------------------------------------------------------
 
void PartEdit::reverbChanged(double val, bool syncControls)
      {
      int iv = lrint(val);
      seq->setController(channel->channel, CTRL_REVERB_SEND, iv);
      channel->reverb = iv;
      channel->updateInitList();
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   chorusChanged
//---------------------------------------------------------
 
void PartEdit::chorusChanged(double val, bool syncControls)
      {
      int iv = lrint(val);
      seq->setController(channel->channel, CTRL_CHORUS_SEND, iv);
      channel->chorus = iv;
      channel->updateInitList();
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   muteChanged
//---------------------------------------------------------
 
void PartEdit::muteChanged(bool val, bool syncControls)
      {
      if (val)
            seq->stopNotes(channel->channel);
      channel->mute = val;
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   soloToggled
//---------------------------------------------------------
 
void PartEdit::soloToggled(bool val, bool syncControls)
      {
      channel->solo     = val;
      channel->soloMute = !val;
      if (val) {
            mute->setChecked(false);
            for (Part* p : part->score()->parts()) {
                  const InstrumentList* il = p->instruments();
                  for (auto i = il->begin(); i != il->end(); ++i) {
                        const Instrument* instr = i->second;
                        for (Channel* a : instr->channel()) {
                              a->soloMute = (channel != a && !a->solo);
                              a->solo     = (channel == a || a->solo);
                              if (a->soloMute)
                                    seq->stopNotes(a->channel);
                              }
                        }
                  }
            emit soloChanged(true);
            }
      else { //do nothing except if it's the last solo to be switched off
            bool found = false;
            for (Part* p : part->score()->parts()) {
                  const InstrumentList* il = p->instruments();
                  for (auto i = il->begin(); i != il->end(); ++i) {
                        const Instrument* instr = i->second;
                        for (Channel* a : instr->channel()) {
                              if (a->solo){
                                    found = true;
                                    break;
                                    }
                              }
                        }
                  }
            if (!found){
                  foreach(Part* p, part->score()->parts()) {
                        const InstrumentList* il = p->instruments();
                        for (auto i = il->begin(); i != il->end(); ++i) {
                              const Instrument* instr = i->second;
                              for (Channel* a : instr->channel()) {
                                    a->soloMute = false;
                                    a->solo     = false;
                                    }
                              }
                        }
                  emit soloChanged(false);
                  }
            }
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   drumsetToggled
//---------------------------------------------------------
 
void PartEdit::drumsetToggled(bool val, bool syncControls)
      {
      if (part == 0)
            return;
 
      Score* score = part->score();
      score->startCmd();
 
      part->undoChangeProperty(P_ID::USE_DRUMSET, val);
      patch->clear();
      const QList<MidiPatch*> pl = synti->getPatchInfo();
      for (const MidiPatch* p : pl) {
            if (p->drum == val)
                  patch->addItem(p->name, QVariant::fromValue<void*>((void*)p));
            }
 
      // switch to first instrument
      const MidiPatch* p = (MidiPatch*)patch->itemData(0, Qt::UserRole).value<void*>();
      if (p == 0) {
            qDebug("PartEdit::patchChanged: no patch");
            return;
            }
      score->undo(new ChangePatch(score, channel, p));
      score->setLayoutAll();
      score->endCmd();
      sync(syncControls);
      }
 
//---------------------------------------------------------
//   updateSolo
//---------------------------------------------------------
 
void Mixer::updateSolo(bool val)
      {
      for (int i = 0; i < vb->count(); i++ ){
            QWidgetItem* wi = (QWidgetItem*)(vb->itemAt(i));
            PartEdit* pe    = (PartEdit*)(wi->widget());
            if (pe)
                  pe->mute->setEnabled(!val);
            }
      }
 
//---------------------------------------------------------
//   writeSettings
//---------------------------------------------------------
 
void Mixer::writeSettings()
      {
      MuseScore::saveGeometry(this);
      }
 
//---------------------------------------------------------
//   readSettings
//---------------------------------------------------------
 
void Mixer::readSettings()
      {
      resize(QSize(480, 600)); //ensure default size if no geometry in settings
      MuseScore::restoreGeometry(this);
      }
 
//---------------------------------------------------------
//   sync
//   synchronizes controls with same MIDI port and channel
//---------------------------------------------------------
 
void PartEdit::sync(bool syncControls)
      {
      if (!syncControls)
            return;
      int count = this->parentWidget()->layout()->count();
      for(int i = 0; i < count; i++) {
            QWidgetItem* wi = (QWidgetItem*)(this->parentWidget()->layout()->itemAt(i));
            PartEdit* pe    = (PartEdit*)(wi->widget());
            if (pe != 0 && pe != this
                && this->channelSpinBox->value() == pe->channelSpinBox->value()
                && this->portSpinBox->value() == pe->portSpinBox->value()) {
 
                  if (volume->value() != pe->volume->value()) {
                        _setValue(pe->volume, this->volume->value());
                        emit pe->volChanged(this->volume->value(), false);
                        }
                  if (pan->value() != pe->pan->value()) {
                        _setValue(pe->pan, this->pan->value());
                        emit pe->panChanged(this->pan->value(), false);
                        }
                  if (reverb->value() != pe->reverb->value()) {
                        _setValue(pe->reverb, this->reverb->value());
                        emit pe->reverbChanged(this->reverb->value(), false);
                        }
                  if (chorus->value() != pe->chorus->value()) {
                        _setValue(pe->chorus, this->chorus->value());
                       emit pe->chorusChanged(this->chorus->value(), false);
                        }
                  if (mute->isChecked() != pe->mute->isChecked()) {
                        _setChecked(pe->mute, channel->mute);
                        emit pe->muteChanged(channel->mute, false);
                        }
                  if (solo->isChecked() != pe->solo->isChecked()) {
                        _setChecked(pe->solo, channel->solo);
                        emit pe->soloToggled(channel->solo, false);
                        }
 
                  if (drumset->isChecked() != pe->drumset->isChecked()) {
                        _setChecked(pe->drumset, drumset->isChecked());
                        emit pe->drumsetToggled(drumset->isChecked(), false);
                        }
                  if (patch->currentIndex() != pe->patch->currentIndex()) {
                        pe->patch->blockSignals(true);
                        pe->patch->setCurrentIndex(this->patch->currentIndex());
                        pe->patch->blockSignals(false);
                        }
                  }
            }
      }
 
//---------------------------------------------------------
//   midiChannelChanged
//   handles MIDI port & channel change
//---------------------------------------------------------
 
void PartEdit::midiChannelChanged(int)
      {
      if (part == 0)
            return;
      seq->stopNotes(channel->channel);
      int p =    portSpinBox->value() - 1;
      int c = channelSpinBox->value() - 1;
 
      // 1 is for going up, -1 for going down
      int direction = copysign(1, c - part->masterScore()->midiMapping(channel->channel)->channel);
 
      // Channel 9 is special for drums
      if (part->instrument()->useDrumset() && c != 9) {
            _setValue(channelSpinBox, 10);
            return;
            }
      else if (!part->instrument()->useDrumset() && c == 9) {
            c = 9 + direction;
            }
 
      if (c == 16) {
            c = 0;
            p++;
            }
 
      int newChannel = p*16+c;
 
      // If there is an instrument with the same MIDI port and channel, sync this instrument to a found one
      bool needSync = true;
      int elementsInMixer = parentWidget()->layout()->count();
      for (int i = 0; i < elementsInMixer; i++) {
            QWidgetItem* wi = (QWidgetItem*)(this->parentWidget()->layout()->itemAt(i));
            PartEdit* pe    = (PartEdit*)(wi->widget());
            if (pe != 0 && pe != this
                        && pe->channelSpinBox->value() == this->channelSpinBox->value()
                        && pe->portSpinBox->value() == this->portSpinBox->value()) {
                  // Show datails if parameters are different
                  QString detailedText;
                  if (patch->currentIndex() != pe->patch->currentIndex())
                        detailedText += QString(tr("Sound: '%1'' vs '%2'\n")).arg(patch->itemText(patch->currentIndex()), pe->patch->itemText(pe->patch->currentIndex()));
                  if (this->volume->value() != pe->volume->value())
                        detailedText += QString(tr("Volume: %1 vs %2\n")).arg(QString::number(volume->value()),  QString::number(pe->volume->value()));
                  if (this->pan->value() != pe->pan->value())
                        detailedText += QString(tr("Pan: %1 vs %2\n")).arg(QString::number(pan->value()),  QString::number(pe->pan->value()));
                  if (this->reverb->value() != pe->reverb->value())
                        detailedText += QString(tr("Reverb: %1 vs %2\n")).arg(QString::number(reverb->value()),  QString::number(pe->reverb->value()));
                  if (this->chorus->value() != pe->chorus->value())
                        detailedText += QString(tr("Chorus: %1 vs %2\n")).arg(QString::number(chorus->value()),  QString::number(pe->chorus->value()));
                  if (this->solo->isChecked() != pe->solo->isChecked())
                        detailedText += QString(tr("Solo: %1 vs %2\n")).arg(solo->isChecked()?"Yes":"No", pe->solo->isChecked()?"Yes":"No");
                  if (this->mute->isChecked() != pe->mute->isChecked())
                        detailedText += QString(tr("Mute: %1 vs %2\n")).arg(mute->isChecked()?"Yes":"No", pe->mute->isChecked()?"Yes":"No");
 
                  if (!detailedText.isEmpty())
                        detailedText = QString(tr("Instrument '%1'    Instrument '%2'\n")).arg(this->partName->text(), pe->partName->text())+ detailedText;
                  QMessageBox msgBox;
                  msgBox.setIcon(QMessageBox::Warning);
                  QString text = QString(tr("There is already an instrument '%1' with MIDI port = %2 and channel = %3.")).arg(pe->partName->text(), QString::number(pe->portSpinBox->value()), QString::number(pe->channelSpinBox->value()));
                  msgBox.setText(text);
                  msgBox.setInformativeText(tr("Do you want to synchronize current instrument with an existing one?"));
                  msgBox.setDetailedText(detailedText);
                  msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
                  QPushButton *assignFreeChannel = msgBox.addButton(tr("Assign next free MIDI channel"), QMessageBox::HelpRole);
                  msgBox.setDefaultButton(QMessageBox::Ok);
                  if (msgBox.exec() == QMessageBox::Cancel) {
                        _setValue(channelSpinBox, part->masterScore()->midiMapping(channel->channel)->channel + 1);
                        _setValue(portSpinBox,    part->masterScore()->midiMapping(channel->channel)->port + 1);
                        needSync = false;
                        break;
                        }
 
                  if (msgBox.clickedButton() == assignFreeChannel) {
                        newChannel = part->masterScore()->getNextFreeMidiMapping();
                        break;
                        }
                  // Sync
                  _setValue(channelSpinBox, newChannel % 16 + 1);
                  _setValue(portSpinBox,    newChannel / 16 + 1);
                  part->masterScore()->midiMapping(channel->channel)->channel = newChannel % 16;
                  part->masterScore()->midiMapping(channel->channel)->port    = newChannel / 16;
                  channel->volume = lrint(pe->volume->value());
                  channel->pan    = lrint(pe->pan->value());
                  channel->reverb = lrint(pe->reverb->value());
                  channel->chorus = lrint(pe->chorus->value());
                  channel->mute = pe->mute->isChecked();
                  channel->solo = pe->solo->isChecked();
 
                  MidiPatch* newPatch = (MidiPatch*)pe->patch->itemData(pe->patch->currentIndex(), Qt::UserRole).value<void*>();
                  if (newPatch == 0)
                        return;
                  patch->setCurrentIndex(pe->patch->currentIndex());
                  channel->program = newPatch->prog;
                  channel->bank    = newPatch->bank;
                  channel->synti   = newPatch->synti;
 
                  part->masterScore()->setSoloMute();
                  part->score()->setInstrumentsChanged(true);
                  part->score()->setLayoutAll();
                  break;
                  }
            }
      channel->updateInitList();
      if (needSync) {
            _setValue(channelSpinBox, newChannel % 16 + 1);
            _setValue(portSpinBox,    newChannel / 16 + 1);
            part->masterScore()->midiMapping(channel->channel)->channel = newChannel % 16;
            part->masterScore()->midiMapping(channel->channel)->port    = newChannel / 16;
            part->score()->setInstrumentsChanged(true);
            part->score()->setLayoutAll();
            seq->initInstruments();
            }
      else {
            // Initializing an instrument with new channel
            foreach(const MidiCoreEvent& e, channel->init) {
                  if (e.type() == ME_INVALID)
                        continue;
                  NPlayEvent event(e.type(), channel->channel, e.dataA(), e.dataB());
                  seq->sendEvent(event);
                  }
            }
 
      // Update MIDI Out ports
      int maxPort = max(p, part->score()->masterScore()->midiPortCount());
      part->score()->masterScore()->setMidiPortCount(maxPort);
      if (seq->driver() && (preferences.useJackMidi || preferences.useAlsaAudio))
            seq->driver()->updateOutPortCount(maxPort + 1);
      }
}
 

V678 An object is used as an argument to its own method. Consider checking the first actual argument of the 'retranslateUi' function.