574 lines
20 KiB
C++
574 lines
20 KiB
C++
//=============================================================================
|
|
// MuseScore
|
|
// Linux Music Score Editor
|
|
//
|
|
// 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 "mixerdetails.h"
|
|
|
|
#include "musescore.h"
|
|
|
|
#include "libmscore/score.h"
|
|
#include "libmscore/part.h"
|
|
#include "mixer.h"
|
|
#include "mixertrack.h"
|
|
#include "mixertrackitem.h"
|
|
#include "seq.h"
|
|
#include "libmscore/undo.h"
|
|
#include "synthcontrol.h"
|
|
#include "synthesizer/msynthesizer.h"
|
|
#include "preferences.h"
|
|
|
|
namespace Ms {
|
|
|
|
//---------------------------------------------------------
|
|
// MixerDetails
|
|
//---------------------------------------------------------
|
|
|
|
MixerDetails::MixerDetails(QWidget *parent) :
|
|
QWidget(parent),
|
|
_mti(nullptr),
|
|
mutePerVoiceHolder(nullptr)
|
|
{
|
|
setupUi(this);
|
|
|
|
connect(partNameLineEdit, SIGNAL(editingFinished()), SLOT(partNameChanged()));
|
|
connect(trackColorLabel, SIGNAL(colorChanged(QColor)), SLOT(trackColorChanged(QColor)));
|
|
connect(patchCombo, SIGNAL(activated(int)), SLOT(patchChanged(int)));
|
|
connect(volumeSlider, &QSlider::valueChanged, this, &MixerDetails::volumeChanged);
|
|
connect(volumeSpinBox, SIGNAL(valueChanged(double)), SLOT(volumeChanged(double)));
|
|
connect(panSlider, &QSlider::valueChanged, this, &MixerDetails::panChanged);
|
|
connect(panSpinBox, SIGNAL(valueChanged(double)), SLOT(panChanged(double)));
|
|
connect(chorusSlider, &QSlider::valueChanged, this, &MixerDetails::chorusChanged);
|
|
connect(chorusSpinBox, SIGNAL(valueChanged(double)), SLOT(chorusChanged(double)));
|
|
connect(reverbSlider, &QSlider::valueChanged, this, &MixerDetails::reverbChanged);
|
|
connect(reverbSpinBox, SIGNAL(valueChanged(double)), SLOT(reverbChanged(double)));
|
|
connect(portSpinBox, SIGNAL(valueChanged(int)), SLOT(midiChannelChanged(int)));
|
|
connect(channelSpinBox, SIGNAL(valueChanged(int)), SLOT(midiChannelChanged(int)));
|
|
connect(drumkitCheck, SIGNAL(toggled(bool)), SLOT(drumkitToggled(bool)));
|
|
|
|
updateFromTrack();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setTrack
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::setTrack(MixerTrackItemPtr track)
|
|
{
|
|
_mti = track;
|
|
setNotifier(_mti ? _mti->focusedChan() : nullptr);
|
|
updateFromTrack();
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
// updateFromTrack
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::updateFromTrack()
|
|
{
|
|
if (mutePerVoiceHolder) {
|
|
mutePerVoiceHolder->deleteLater();
|
|
mutePerVoiceHolder = nullptr;
|
|
}
|
|
|
|
if (!_mti) {
|
|
drumkitCheck->setChecked(false);
|
|
patchCombo->clear();
|
|
partNameLineEdit->setText("");
|
|
channelLabel->setText("");
|
|
volumeSlider->setValue(0);
|
|
volumeSpinBox->setValue(0);
|
|
panSlider->setValue(0);
|
|
panSpinBox->setValue(0);
|
|
reverbSlider->setValue(0);
|
|
reverbSpinBox->setValue(0);
|
|
chorusSlider->setValue(0);
|
|
chorusSpinBox->setValue(0);
|
|
portSpinBox->setValue(0);
|
|
channelSpinBox->setValue(0);
|
|
trackColorLabel->setColor(QColor());
|
|
|
|
drumkitCheck->setEnabled(false);
|
|
patchCombo->setEnabled(false);
|
|
partNameLineEdit->setEnabled(false);
|
|
volumeSlider->setEnabled(false);
|
|
volumeSpinBox->setEnabled(false);
|
|
panSlider->setEnabled(false);
|
|
panSpinBox->setEnabled(false);
|
|
reverbSlider->setEnabled(false);
|
|
reverbSpinBox->setEnabled(false);
|
|
chorusSlider->setEnabled(false);
|
|
chorusSpinBox->setEnabled(false);
|
|
portSpinBox->setEnabled(false);
|
|
channelSpinBox->setEnabled(false);
|
|
trackColorLabel->setEnabled(false);
|
|
|
|
labelName->setEnabled(false);
|
|
labelChannel->setEnabled(false);
|
|
labelChannel_2->setEnabled(false);
|
|
labelChorus->setEnabled(false);
|
|
labelPan->setEnabled(false);
|
|
labelPatch->setEnabled(false);
|
|
labelPort->setEnabled(false);
|
|
labelReverb->setEnabled(false);
|
|
labelVolume->setEnabled(false);
|
|
return;
|
|
}
|
|
|
|
drumkitCheck->setEnabled(true);
|
|
patchCombo->setEnabled(true);
|
|
partNameLineEdit->setEnabled(true);
|
|
volumeSlider->setEnabled(true);
|
|
volumeSpinBox->setEnabled(true);
|
|
panSlider->setEnabled(true);
|
|
panSpinBox->setEnabled(true);
|
|
reverbSlider->setEnabled(true);
|
|
reverbSpinBox->setEnabled(true);
|
|
chorusSlider->setEnabled(true);
|
|
chorusSpinBox->setEnabled(true);
|
|
portSpinBox->setEnabled(true);
|
|
channelSpinBox->setEnabled(true);
|
|
trackColorLabel->setEnabled(true);
|
|
|
|
labelName->setEnabled(true);
|
|
labelChannel->setEnabled(true);
|
|
labelChannel_2->setEnabled(true);
|
|
labelChorus->setEnabled(true);
|
|
labelPan->setEnabled(true);
|
|
labelPatch->setEnabled(true);
|
|
labelPort->setEnabled(true);
|
|
labelReverb->setEnabled(true);
|
|
labelVolume->setEnabled(true);
|
|
|
|
|
|
MidiMapping* midiMap = _mti->midiMap();
|
|
Part* part = _mti->part();
|
|
Channel* chan = _mti->focusedChan();
|
|
|
|
//Check if drumkit
|
|
const bool drum = midiMap->part()->instrument()->useDrumset();
|
|
drumkitCheck->blockSignals(true);
|
|
drumkitCheck->setChecked(drum);
|
|
drumkitCheck->blockSignals(false);
|
|
|
|
//Populate patch combo
|
|
patchCombo->blockSignals(true);
|
|
patchCombo->clear();
|
|
const auto& pl = synti->getPatchInfo();
|
|
int patchIndex = 0;
|
|
|
|
// Order by program number instead of bank, so similar instruments
|
|
// appear next to each other, but ordered primarily by soundfont
|
|
std::map<int, std::map<int, std::vector<const MidiPatch*>>> orderedPl;
|
|
|
|
for (const MidiPatch* p : pl)
|
|
orderedPl[p->sfid][p->prog].push_back(p);
|
|
|
|
std::vector<QString> usedNames;
|
|
for (auto const& sf : orderedPl) {
|
|
for (auto const& pn : sf.second) {
|
|
for (const MidiPatch* p : pn.second) {
|
|
if (p->drum == drum || p->synti != "Fluid") {
|
|
QString pName = p->name;
|
|
if (std::find(usedNames.begin(), usedNames.end(), p->name) != usedNames.end()) {
|
|
QString addNum = QString(" (%1)").arg(p->sfid);
|
|
pName.append(addNum);
|
|
}
|
|
else
|
|
usedNames.push_back(p->name);
|
|
|
|
patchCombo->addItem(pName, QVariant::fromValue<void*>((void*)p));
|
|
if (p->synti == chan->synti() &&
|
|
p->bank == chan->bank() &&
|
|
p->prog == chan->program())
|
|
patchIndex = patchCombo->count() - 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
patchCombo->setCurrentIndex(patchIndex);
|
|
|
|
patchCombo->blockSignals(false);
|
|
|
|
QString partName = part->partName();
|
|
if (!chan->name().isEmpty())
|
|
channelLabel->setText(qApp->translate("InstrumentsXML", chan->name().toUtf8().data()));
|
|
else
|
|
channelLabel->setText("");
|
|
partNameLineEdit->setText(partName);
|
|
partNameLineEdit->setToolTip(partName);
|
|
|
|
|
|
trackColorLabel->blockSignals(true);
|
|
volumeSlider->blockSignals(true);
|
|
volumeSpinBox->blockSignals(true);
|
|
panSlider->blockSignals(true);
|
|
panSpinBox->blockSignals(true);
|
|
reverbSlider->blockSignals(true);
|
|
reverbSpinBox->blockSignals(true);
|
|
chorusSlider->blockSignals(true);
|
|
chorusSpinBox->blockSignals(true);
|
|
|
|
portSpinBox->blockSignals(true);
|
|
channelSpinBox->blockSignals(true);
|
|
|
|
trackColorLabel->setColor(QColor(_mti->color() | 0xff000000));
|
|
|
|
volumeSlider->setValue((int)chan->volume());
|
|
volumeSpinBox->setValue(chan->volume());
|
|
panSlider->setValue((int)chan->pan());
|
|
panSpinBox->setValue(chan->pan());
|
|
reverbSlider->setValue((int)chan->reverb());
|
|
reverbSpinBox->setValue(chan->reverb());
|
|
chorusSlider->setValue((int)chan->chorus());
|
|
chorusSpinBox->setValue(chan->chorus());
|
|
|
|
portSpinBox->setValue(part->masterScore()->midiMapping(chan->channel())->port() + 1);
|
|
channelSpinBox->setValue(part->masterScore()->midiMapping(chan->channel())->channel() + 1);
|
|
|
|
trackColorLabel->blockSignals(false);
|
|
volumeSlider->blockSignals(false);
|
|
volumeSpinBox->blockSignals(false);
|
|
panSlider->blockSignals(false);
|
|
panSpinBox->blockSignals(false);
|
|
reverbSlider->blockSignals(false);
|
|
reverbSpinBox->blockSignals(false);
|
|
chorusSlider->blockSignals(false);
|
|
chorusSpinBox->blockSignals(false);
|
|
|
|
portSpinBox->blockSignals(false);
|
|
channelSpinBox->blockSignals(false);
|
|
|
|
//Set up mute per voice buttons
|
|
mutePerVoiceHolder = new QWidget();
|
|
mutePerVoiceArea->addWidget(mutePerVoiceHolder);
|
|
|
|
mutePerVoiceGrid = new QGridLayout();
|
|
mutePerVoiceHolder->setLayout(mutePerVoiceGrid);
|
|
mutePerVoiceGrid->setContentsMargins(0, 0, 0, 0);
|
|
mutePerVoiceGrid->setSpacing(7);
|
|
|
|
for (int staffIdx = 0; staffIdx < (*part->staves()).length(); ++staffIdx) {
|
|
Staff* staff = (*part->staves())[staffIdx];
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
QPushButton* tb = new QPushButton;
|
|
tb->setStyleSheet(
|
|
QString("QPushButton{padding: 4px 8px 4px 8px;}QPushButton:checked{background-color:%1}")
|
|
.arg(MScore::selectColor[voice].name()));
|
|
tb->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
|
tb->setText(QString("%1").arg(voice + 1));
|
|
tb->setCheckable(true);
|
|
tb->setChecked(!staff->playbackVoice(voice));
|
|
tb->setToolTip(QString(tr("Staff #%1")).arg(staffIdx + 1));
|
|
|
|
mutePerVoiceGrid->addWidget(tb, staffIdx, voice);
|
|
MixerDetailsVoiceButtonHandler* handler =
|
|
new MixerDetailsVoiceButtonHandler(this, staffIdx, voice, tb);
|
|
connect(tb, SIGNAL(toggled(bool)), handler, SLOT(setVoiceMute(bool)));
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setVoiceMute
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::setVoiceMute(int staffIdx, int voice, bool shouldMute)
|
|
{
|
|
Part* part = _mti->part();
|
|
Staff* staff = part->staff(staffIdx);
|
|
switch (voice) {
|
|
case 0:
|
|
staff->undoChangeProperty(Pid::PLAYBACK_VOICE1, !shouldMute);
|
|
break;
|
|
case 1:
|
|
staff->undoChangeProperty(Pid::PLAYBACK_VOICE2, !shouldMute);
|
|
break;
|
|
case 2:
|
|
staff->undoChangeProperty(Pid::PLAYBACK_VOICE3, !shouldMute);
|
|
break;
|
|
case 3:
|
|
staff->undoChangeProperty(Pid::PLAYBACK_VOICE4, !shouldMute);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
// partNameChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::partNameChanged()
|
|
{
|
|
if (!_mti)
|
|
return;
|
|
|
|
QString text = partNameLineEdit->text();
|
|
Part* part = _mti->part();
|
|
if (part->partName() == text) {
|
|
return;
|
|
}
|
|
|
|
Score* score = part->score();
|
|
if (score) {
|
|
score->startCmd();
|
|
score->undo(new ChangePart(part, part->instrument(), text));
|
|
score->endCmd();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// trackColorChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::trackColorChanged(QColor col)
|
|
{
|
|
if (trackColorLabel->color() != col) {
|
|
trackColorLabel->blockSignals(true);
|
|
trackColorLabel->setColor(col);
|
|
trackColorLabel->blockSignals(false);
|
|
}
|
|
|
|
_mti->setColor(col.rgb());
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// propertyChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::propertyChanged(Channel::Prop property)
|
|
{
|
|
if (!_mti)
|
|
return;
|
|
|
|
MidiMapping* _midiMap = _mti->midiMap();
|
|
Channel* chan = _midiMap->articulation();
|
|
|
|
switch (property) {
|
|
case Channel::Prop::VOLUME: {
|
|
volumeSlider->blockSignals(true);
|
|
volumeSpinBox->blockSignals(true);
|
|
|
|
volumeSlider->setValue((int)chan->volume());
|
|
volumeSpinBox->setValue(chan->volume());
|
|
|
|
volumeSlider->blockSignals(false);
|
|
volumeSpinBox->blockSignals(false);
|
|
break;
|
|
}
|
|
case Channel::Prop::PAN: {
|
|
panSlider->blockSignals(true);
|
|
panSpinBox->blockSignals(true);
|
|
|
|
panSlider->setValue((int)chan->pan());
|
|
panSpinBox->setValue(chan->pan());
|
|
|
|
panSlider->blockSignals(false);
|
|
panSpinBox->blockSignals(false);
|
|
break;
|
|
}
|
|
case Channel::Prop::CHORUS: {
|
|
chorusSlider->blockSignals(true);
|
|
chorusSpinBox->blockSignals(true);
|
|
|
|
chorusSlider->setValue((int)chan->chorus());
|
|
chorusSpinBox->setValue(chan->chorus());
|
|
|
|
chorusSlider->blockSignals(false);
|
|
chorusSpinBox->blockSignals(false);
|
|
break;
|
|
}
|
|
case Channel::Prop::REVERB: {
|
|
reverbSlider->blockSignals(true);
|
|
reverbSpinBox->blockSignals(true);
|
|
|
|
reverbSlider->setValue((int)chan->reverb());
|
|
reverbSpinBox->setValue(chan->reverb());
|
|
|
|
reverbSlider->blockSignals(false);
|
|
reverbSpinBox->blockSignals(false);
|
|
break;
|
|
}
|
|
case Channel::Prop::COLOR: {
|
|
trackColorChanged(chan->color());
|
|
break;
|
|
}
|
|
case Channel::Prop::NAME: {
|
|
partNameLineEdit->blockSignals(true);
|
|
Part* part = _mti->part();
|
|
QString partName = part->partName();
|
|
partNameLineEdit->setText(partName);
|
|
partNameLineEdit->blockSignals(false);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// volumeChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::volumeChanged(double value)
|
|
{
|
|
if (!_mti)
|
|
return;
|
|
|
|
_mti->setVolume(value);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
// panChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::panChanged(double value)
|
|
{
|
|
if (!_mti)
|
|
return;
|
|
|
|
_mti->setPan(value);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
// reverbChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::reverbChanged(double v)
|
|
{
|
|
if (!_mti)
|
|
return;
|
|
|
|
_mti->setReverb(v);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// chorusChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::chorusChanged(double v)
|
|
{
|
|
if (!_mti)
|
|
return;
|
|
|
|
_mti->setChorus(v);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// patchChanged
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::patchChanged(int n)
|
|
{
|
|
if (!_mti)
|
|
return;
|
|
|
|
const MidiPatch* p = (MidiPatch*)patchCombo->itemData(n, Qt::UserRole).value<void*>();
|
|
if (p == 0) {
|
|
qDebug("PartEdit::patchChanged: no patch");
|
|
return;
|
|
}
|
|
|
|
Part* part = _mti->midiMap()->part();
|
|
Channel* channel = _mti->midiMap()->articulation();
|
|
Score* score = part->score();
|
|
if (score) {
|
|
score->startCmd();
|
|
score->undo(new ChangePatch(score, channel, p));
|
|
score->undo(new SetUserBankController(channel, true));
|
|
score->setLayoutAll();
|
|
score->endCmd();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// drumkitToggled
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::drumkitToggled(bool val)
|
|
{
|
|
if (_mti == 0)
|
|
return;
|
|
|
|
Part* part = _mti->part();
|
|
Channel* channel = _mti->focusedChan();
|
|
|
|
|
|
Instrument *instr;
|
|
if (_mti->trackType() == MixerTrackItem::TrackType::CHANNEL)
|
|
instr = _mti->instrument();
|
|
else
|
|
instr = part->instrument(Fraction(0,1));
|
|
|
|
if (instr->useDrumset() == val)
|
|
return;
|
|
|
|
const MidiPatch* newPatch = 0;
|
|
const QList<MidiPatch*> pl = synti->getPatchInfo();
|
|
for (const MidiPatch* p : pl) {
|
|
if (p->drum == val) {
|
|
newPatch = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Score* score = part->score();
|
|
if (newPatch) {
|
|
score->startCmd();
|
|
part->undoChangeProperty(Pid::USE_DRUMSET, val);
|
|
score->undo(new ChangePatch(score, channel, newPatch));
|
|
score->setLayoutAll();
|
|
score->endCmd();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// midiChannelChanged
|
|
// handles MIDI port & channel change
|
|
//---------------------------------------------------------
|
|
|
|
void MixerDetails::midiChannelChanged(int)
|
|
{
|
|
if (_mti == 0)
|
|
return;
|
|
|
|
Part* part = _mti->part();
|
|
Channel* channel = _mti->focusedChan();
|
|
|
|
seq->stopNotes(channel->channel());
|
|
int p = portSpinBox->value() - 1;
|
|
int c = channelSpinBox->value() - 1;
|
|
|
|
MidiMapping* midiMap = _mti->midiMap();
|
|
part->masterScore()->updateMidiMapping(midiMap->articulation(), part, p, c);
|
|
|
|
part->score()->setInstrumentsChanged(true);
|
|
part->score()->setLayoutAll();
|
|
seq->initInstruments();
|
|
|
|
// Update MIDI Out ports
|
|
int maxPort = max(p, part->score()->masterScore()->midiPortCount());
|
|
part->score()->masterScore()->setMidiPortCount(maxPort);
|
|
if (seq->driver() && (preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO)))
|
|
seq->driver()->updateOutPortCount(maxPort + 1);
|
|
}
|
|
|
|
|
|
}
|