Merge pull request #4655 from dmitrio95/parts-playback

fix #109941: Single parts playback
This commit is contained in:
anatoly-os 2019-02-16 11:07:56 +02:00 committed by GitHub
commit 27dc9000ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 589 additions and 368 deletions

View file

@ -25,6 +25,7 @@
namespace Ms {
Instrument InstrumentList::defaultInstrument;
constexpr std::initializer_list<Channel::Prop> PartChannelSettingsLink::excerptProperties;
//---------------------------------------------------------
// write
@ -197,7 +198,7 @@ void StaffName::read(XmlReader& e)
// Instrument::write
//---------------------------------------------------------
void Instrument::write(XmlWriter& xml, Part* part) const
void Instrument::write(XmlWriter& xml, const Part* part) const
{
xml.stag("Instrument");
_longNames.write(xml, "longName");
@ -600,7 +601,7 @@ void Channel::setSolo(bool value)
// write
//---------------------------------------------------------
void Channel::write(XmlWriter& xml, Part* part) const
void Channel::write(XmlWriter& xml, const Part* part) const
{
if (_name.isEmpty() || _name == DEFAULT_NAME)
xml.stag("Channel");
@ -640,8 +641,8 @@ void Channel::write(XmlWriter& xml, Part* part) const
if (_solo)
xml.tag("solo", _solo);
if (part && part->masterScore()->exportMidiMapping() && part->score() == part->masterScore()) {
xml.tag("midiPort", part->masterScore()->midiMapping(_channel)->port);
xml.tag("midiChannel", part->masterScore()->midiMapping(_channel)->channel);
xml.tag("midiPort", part->masterScore()->midiMapping(_channel)->port());
xml.tag("midiChannel", part->masterScore()->midiMapping(_channel)->channel());
}
for (const NamedEventList& a : midiActions)
a.write(xml, "MidiAction");
@ -727,20 +728,13 @@ void Channel::read(XmlReader& e, Part* part)
_solo = e.readInt();
else if (tag == "midiPort") {
int midiPort = e.readInt();
if (part) {
MidiMapping mm;
mm.port = midiPort;
mm.channel = -1;
mm.part = part;
mm.articulation = this;
part->masterScore()->midiMapping()->append(mm);
_channel = part->masterScore()->midiMapping()->size() - 1;
}
if (part && part->score()->isMaster())
part->masterScore()->addMidiMapping(this, part, midiPort, -1);
}
else if (tag == "midiChannel") {
int midiChannel = e.readInt();
if (part)
part->masterScore()->midiMapping(_channel)->channel = midiChannel;
if (part && part->score()->isMaster())
part->masterScore()->updateMidiMapping(this, part, -1, midiChannel);
}
else
e.unknown();
@ -802,6 +796,138 @@ void Channel::removeListener(ChannelListener* l)
_notifier.removeListener(l);
}
//---------------------------------------------------------
// PartChannelSettingsLink
//---------------------------------------------------------
PartChannelSettingsLink::PartChannelSettingsLink(Channel* main, Channel* bound, bool excerpt)
: _main(main), _bound(bound), _excerpt(excerpt)
{
if (excerpt) {
for (Channel::Prop p : excerptProperties)
applyProperty(p, /* from */ bound, /* to */ main);
}
// Maybe it would be good to assign common properties if the link
// is constructed in non-excerpt mode. But it is not currently
// necessary as playback channels are currently recreated on each
// MIDI remapping.
main->addListener(this);
}
//---------------------------------------------------------
// PartChannelSettingsLink
//---------------------------------------------------------
PartChannelSettingsLink::PartChannelSettingsLink(PartChannelSettingsLink&& other)
: _main(nullptr), _bound(nullptr), _excerpt(false)
{
swap(*this, other);
}
//---------------------------------------------------------
// PartChannelSettingsLink::operator=
//---------------------------------------------------------
PartChannelSettingsLink& PartChannelSettingsLink::operator=(PartChannelSettingsLink&& other)
{
if (this != &other)
swap(*this, other);
return *this;
}
//---------------------------------------------------------
// ~PartChannelSettingsLink
//---------------------------------------------------------
PartChannelSettingsLink::~PartChannelSettingsLink()
{
if (_main)
_main->removeListener(this);
}
//---------------------------------------------------------
// swap
//---------------------------------------------------------
void swap(PartChannelSettingsLink& l1, PartChannelSettingsLink& l2)
{
using std::swap;
if (l1._main)
l1._main->removeListener(&l1);
if (l2._main)
l2._main->removeListener(&l2);
swap(l1._main, l2._main);
swap(l1._bound, l2._bound);
swap(l1._excerpt, l2._excerpt);
if (l1._main)
l1._main->addListener(&l1);
if (l2._main)
l2._main->addListener(&l2);
}
//---------------------------------------------------------
// PartChannelSettingsLink::applyProperty
//---------------------------------------------------------
void PartChannelSettingsLink::applyProperty(Channel::Prop p, const Channel* from, Channel* to)
{
switch (p) {
case Channel::Prop::VOLUME:
to->setVolume(from->volume());
break;
case Channel::Prop::PAN:
to->setPan(from->pan());
break;
case Channel::Prop::CHORUS:
to->setChorus(from->chorus());
break;
case Channel::Prop::REVERB:
to->setReverb(from->reverb());
break;
case Channel::Prop::NAME:
to->setName(from->name());
break;
case Channel::Prop::DESCR:
to->setDescr(from->descr());
break;
case Channel::Prop::PROGRAM:
to->setProgram(from->program());
break;
case Channel::Prop::BANK:
to->setBank(from->bank());
break;
case Channel::Prop::COLOR:
to->setColor(from->color());
break;
case Channel::Prop::SOLOMUTE:
to->setSoloMute(from->soloMute());
break;
case Channel::Prop::SOLO:
to->setSolo(from->solo());
break;
case Channel::Prop::MUTE:
to->setMute(from->mute());
break;
case Channel::Prop::SYNTI:
to->setSynti(from->synti());
break;
case Channel::Prop::CHANNEL:
to->setChannel(from->channel());
break;
};
}
//---------------------------------------------------------
// PartChannelSettingsLink::propertyChanged
//---------------------------------------------------------
void PartChannelSettingsLink::propertyChanged(Channel::Prop p)
{
if (isExcerptProperty(p) == _excerpt)
applyProperty(p, _main, _bound);
}
//---------------------------------------------------------
// channelIdx
//---------------------------------------------------------
@ -1202,6 +1328,25 @@ Instrument Instrument::fromTemplate(const InstrumentTemplate* t)
return instr;
}
//---------------------------------------------------------
// Instrument::playbackChannel
//---------------------------------------------------------
const Channel* Instrument::playbackChannel(int idx, const MasterScore* score) const
{
return score->playbackChannel(channel(idx));
}
//---------------------------------------------------------
// Instrument::playbackChannel
//---------------------------------------------------------
Channel* Instrument::playbackChannel(int idx, MasterScore* score)
{
return score->playbackChannel(channel(idx));
}
//---------------------------------------------------------
// StaffNameList::write
//---------------------------------------------------------

View file

@ -25,6 +25,7 @@
namespace Ms {
class InstrumentTemplate;
class MasterScore;
class XmlWriter;
class XmlReader;
class Drumset;
@ -141,7 +142,6 @@ public:
mutable std::vector<MidiCoreEvent> init;
QString name() const { return _name; }
void setName(const QString& value);
QString descr() const { return _descr; }
@ -178,7 +178,7 @@ public:
QList<MidiArticulation> articulation;
Channel();
void write(XmlWriter&, Part *part) const;
void write(XmlWriter&, const Part* part) const;
void read(XmlReader&, Part *part);
void updateInitList() const;
bool operator==(const Channel& c) { return (_name == c._name) && (_channel == c._channel); }
@ -200,6 +200,39 @@ class ChannelListener : public Listener<Channel::Prop> {
void receive(Channel::Prop prop) override { propertyChanged(prop); }
};
//---------------------------------------------------------
// PartChannelSettingsLink
//---------------------------------------------------------
class PartChannelSettingsLink final : private ChannelListener {
// A list of properties which may vary for different excerpts.
static constexpr std::initializer_list<Channel::Prop> excerptProperties {
Channel::Prop::SOLOMUTE,
Channel::Prop::SOLO,
Channel::Prop::MUTE,
};
private:
Channel* _main;
Channel* _bound;
bool _excerpt;
static bool isExcerptProperty(Channel::Prop p) { return std::find(excerptProperties.begin(), excerptProperties.end(), p) != excerptProperties.end(); }
static void applyProperty(Channel::Prop p, const Channel* from, Channel* to);
void propertyChanged(Channel::Prop p) override;
public:
PartChannelSettingsLink() : _main(nullptr), _bound(nullptr), _excerpt(false) {}
PartChannelSettingsLink(Channel* main, Channel* bound, bool excerpt);
PartChannelSettingsLink(const PartChannelSettingsLink&) = delete;
PartChannelSettingsLink(PartChannelSettingsLink&&);
PartChannelSettingsLink& operator=(const PartChannelSettingsLink&) = delete;
PartChannelSettingsLink& operator=(PartChannelSettingsLink&&);
~PartChannelSettingsLink();
friend void swap(PartChannelSettingsLink&, PartChannelSettingsLink&);
};
//---------------------------------------------------------
// Instrument
//---------------------------------------------------------
@ -230,7 +263,7 @@ class Instrument {
void read(XmlReader&, Part *part);
bool readProperties(XmlReader&, Part* , bool* customDrumset);
void write(XmlWriter& xml, Part *part) const;
void write(XmlWriter& xml, const Part* part) const;
NamedEventList* midiAction(const QString& s, int channel) const;
int channelIdx(const QString& s) const;
void updateVelocity(int* velocity, int channel, const QString& name);
@ -256,6 +289,8 @@ class Instrument {
void setProfessionalPitchRange(int a, int b) { _minPitchP = a; _maxPitchP = b; }
Channel* channel(int idx) { return _channel[idx]; }
const Channel* channel(int idx) const { return _channel[idx]; }
Channel* playbackChannel(int idx, MasterScore*);
const Channel* playbackChannel(int idx, const MasterScore*) const;
ClefTypeList clefType(int staffIdx) const;
void setClefType(int staffIdx, const ClefTypeList& c);

View file

@ -12,6 +12,7 @@
#include "score.h"
#include "excerpt.h"
#include "instrument.h"
#include "part.h"
@ -23,10 +24,16 @@ namespace Ms {
void MasterScore::rebuildMidiMapping()
{
Score* playbackScore = _playbackScore ? _playbackScore : this;
setPlaybackScore(nullptr);
removeDeletedMidiMapping();
int maxport = updateMidiMapping();
reorderMidiMapping();
rebuildExcerptsMidiMapping();
masterScore()->setMidiPortCount(maxport);
setPlaybackScore(playbackScore);
}
//---------------------------------------------------------
@ -53,12 +60,12 @@ void MasterScore::checkMidiMapping()
int lastChannel = -1; // port*16+channel
int lastDrumPort = -1;
int index = 0;
for (MidiMapping m : _midiMapping) {
for (const MidiMapping& m : _midiMapping) {
if (index >= drum.size())
break;
if (drum[index]) {
lastDrumPort++;
if (m.port != lastDrumPort) {
if (m.port() != lastDrumPort) {
isSimpleMidiMaping = false;
return;
}
@ -69,7 +76,7 @@ void MasterScore::checkMidiMapping()
lastChannel++;
int p = lastChannel / 16;
int c = lastChannel % 16;
if (m.port != p || m.channel != c) {
if (m.port() != p || m.channel() != c) {
isSimpleMidiMaping = false;
return;
}
@ -126,6 +133,36 @@ int MasterScore::getNextFreeDrumMidiMapping()
}
}
//---------------------------------------------------------
// rebuildExcerptsMidiMapping
//---------------------------------------------------------
void MasterScore::rebuildExcerptsMidiMapping()
{
for (Excerpt* ex : excerpts()) {
for (Part* p : ex->partScore()->parts()) {
const Part* masterPart = p->masterPart();
if (!masterPart->score()->isMaster()) {
qWarning("reorderMidiMapping: no part in master score is linked");
continue;
}
Q_ASSERT(p->instruments()->size() == masterPart->instruments()->size());
for (const auto& item : *masterPart->instruments()) {
const Instrument* iMaster = item.second;
const int tick = item.first;
Instrument* iLocal = p->instrument(tick);
Q_ASSERT(iLocal->channel().size() == iMaster->channel().size());
const int nchannels = iLocal->channel().size();
for (int c = 0; c < nchannels; ++c) {
Channel* cLocal = iLocal->channel(c);
const Channel* cMaster = iMaster->channel(c);
cLocal->setChannel(cMaster->channel());
}
}
}
}
}
//---------------------------------------------------------
// reorderMidiMapping
// Set mappings in order you see in Add->Instruments
@ -133,19 +170,20 @@ int MasterScore::getNextFreeDrumMidiMapping()
void MasterScore::reorderMidiMapping()
{
using std::swap;
int sequenceNumber = 0;
for (Part* part : parts()) {
const InstrumentList* il = part->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (Channel* channel : instr->channel()) {
if (!(_midiMapping[sequenceNumber].part == part
&& _midiMapping[sequenceNumber].articulation == channel)) {
if (!(_midiMapping[sequenceNumber].part() == part
&& _midiMapping[sequenceNumber].masterChannel == channel)) {
int shouldBe = channel->channel();
_midiMapping.swap(sequenceNumber, shouldBe);
_midiMapping[sequenceNumber].articulation->setChannel(sequenceNumber);
swap(_midiMapping[sequenceNumber], _midiMapping[shouldBe]);
_midiMapping[sequenceNumber].articulation()->setChannel(sequenceNumber);
channel->setChannel(sequenceNumber);
_midiMapping[shouldBe].articulation->setChannel(shouldBe);
_midiMapping[shouldBe].articulation()->setChannel(shouldBe);
}
sequenceNumber++;
}
@ -163,7 +201,7 @@ void MasterScore::removeDeletedMidiMapping()
int removeOffset = 0;
int mappingSize = _midiMapping.size();
for (int index = 0; index < mappingSize; index++) {
Part* p = midiMapping(index)->part;
Part* p = midiMapping(index)->part();
if (!parts().contains(p)) {
removeOffset++;
continue;
@ -173,9 +211,9 @@ void MasterScore::removeDeletedMidiMapping()
const InstrumentList* il = p->instruments();
for (auto i = il->begin(); i != il->end() && !channelExists; ++i) {
const Instrument* instr = i->second;
channelExists = (_midiMapping[index].articulation->channel() != -1
&& instr->channel().contains(_midiMapping[index].articulation)
&& !(_midiMapping[index].port == -1 && _midiMapping[index].channel == -1));
channelExists = (_midiMapping[index].articulation()->channel() != -1
&& instr->channel().contains(_midiMapping[index].masterChannel)
&& !(_midiMapping[index].port() == -1 && _midiMapping[index].channel() == -1));
if (channelExists)
break;
}
@ -185,15 +223,15 @@ void MasterScore::removeDeletedMidiMapping()
}
// Let's do a left shift by 'removeOffset' items if necessary
if (index != 0 && removeOffset != 0) {
_midiMapping[index-removeOffset] = _midiMapping[index];
_midiMapping[index-removeOffset] = std::move(_midiMapping[index]);
int chanVal = _midiMapping[index-removeOffset].articulation->channel();
_midiMapping[index-removeOffset].articulation->setChannel(chanVal - removeOffset);
const int chanVal = _midiMapping[index-removeOffset].articulation()->channel();
_midiMapping[index-removeOffset].articulation()->setChannel(chanVal - removeOffset);
}
}
// We have 'removeOffset' deleted instruments, let's remove their mappings
for (int index = 0; index < removeOffset; index++)
_midiMapping.removeLast();
_midiMapping.pop_back();
}
//---------------------------------------------------------
@ -208,12 +246,12 @@ int MasterScore::updateMidiMapping()
searchMidiMappingFrom = 0;
occupiedMidiChannels.reserve(_midiMapping.size()); // Bringing down the complexity of insertion to amortized O(1)
for(MidiMapping mm :_midiMapping) {
if (mm.port == -1 || mm.channel == -1)
for (const MidiMapping& mm :_midiMapping) {
if (mm.port() == -1 || mm.channel() == -1)
continue;
occupiedMidiChannels.insert((int)(mm.port)*16+(int)mm.channel);
if (maxport < mm.port)
maxport = mm.port;
occupiedMidiChannels.insert((int)(mm.port())*16+(int)mm.channel());
if (maxport < mm.port())
maxport = mm.port();
}
for (Part* part : parts()) {
@ -223,55 +261,100 @@ int MasterScore::updateMidiMapping()
bool drum = instr->useDrumset();
for (Channel* channel : instr->channel()) {
bool channelExists = false;
for (MidiMapping mapping: _midiMapping) {
if (channel == mapping.articulation && channel->channel() != -1) {
for (const MidiMapping& mapping: _midiMapping) {
if (channel == mapping.masterChannel && channel->channel() != -1) {
channelExists = true;
break;
}
}
// Channel could already exist, but have unassigned port or channel. Repair and continue
if (channelExists) {
if (_midiMapping[channel->channel()].port == -1) {
int nm = getNextFreeMidiMapping(-1, _midiMapping[channel->channel()].channel);
_midiMapping[channel->channel()].port = nm / 16;
if (_midiMapping[channel->channel()].port() == -1) {
const int nm = getNextFreeMidiMapping(-1, _midiMapping[channel->channel()].channel());
_midiMapping[channel->channel()]._port = nm / 16;
}
else if (_midiMapping[channel->channel()].channel == -1) {
else if (_midiMapping[channel->channel()].channel() == -1) {
if (drum) {
_midiMapping[channel->channel()].port = getNextFreeDrumMidiMapping() / 16;
_midiMapping[channel->channel()].channel = 9;
_midiMapping[channel->channel()]._port = getNextFreeDrumMidiMapping() / 16;
_midiMapping[channel->channel()]._channel = 9;
continue;
}
int nm = getNextFreeMidiMapping(_midiMapping[channel->channel()].port);
_midiMapping[channel->channel()].port = nm / 16;
_midiMapping[channel->channel()].channel = nm % 16;
int nm = getNextFreeMidiMapping(_midiMapping[channel->channel()].port());
_midiMapping[channel->channel()]._port = nm / 16;
_midiMapping[channel->channel()]._channel = nm % 16;
}
continue;
}
MidiMapping mm;
int midiPort;
int midiChannel;
if (drum) {
mm.port = getNextFreeDrumMidiMapping() / 16;
mm.channel = 9;
midiPort = getNextFreeDrumMidiMapping() / 16;
midiChannel = 9;
}
else {
int nm = getNextFreeMidiMapping();
mm.port = nm / 16;
mm.channel = nm % 16;
midiPort = nm / 16;
midiChannel = nm % 16;
}
if (mm.port > maxport)
maxport = mm.port;
if (midiPort > maxport)
maxport = midiPort;
mm.part = part;
mm.articulation = channel;
_midiMapping.append(mm);
channel->setChannel(_midiMapping.size() - 1);
addMidiMapping(channel, part, midiPort, midiChannel);
}
}
}
return maxport;
}
//---------------------------------------------------------
// addMidiMapping
//---------------------------------------------------------
void MasterScore::addMidiMapping(Channel* channel, Part* part, int midiPort, int midiChannel)
{
if (!part->score()->isMaster())
return;
MidiMapping mm;
mm._part = part;
mm.masterChannel = channel;
mm._articulation.reset(new Channel(*channel));
mm.link = PartChannelSettingsLink(mm.articulation(), mm.masterChannel, /* excerpt */ false);
mm._port = midiPort;
mm._channel = midiChannel;
const int mscoreChannel = _midiMapping.size();
mm._articulation->setChannel(mscoreChannel);
mm.masterChannel->setChannel(mscoreChannel);
_midiMapping.push_back(std::move(mm));
}
//---------------------------------------------------------
// updateMidiMapping
//---------------------------------------------------------
void MasterScore::updateMidiMapping(Channel* channel, Part* part, int midiPort, int midiChannel)
{
const int c = channel->channel();
if (c < 0)
return;
if (c >= int(masterScore()->midiMapping().size())) {
qDebug("Can't set midi channel: midiMapping is empty!");
return;
}
MidiMapping& mm = _midiMapping[c];
if (midiChannel != -1)
mm._channel = midiChannel;
if (midiPort != -1)
mm._port = midiPort;
if (part)
mm._part = part->masterPart();
}
} // namespace Ms

View file

@ -92,30 +92,6 @@ Part* Part::masterPart()
return const_cast<Part*>(const_cast<const Part*>(this)->masterPart());
}
//---------------------------------------------------------
// Part::redirectPart
//---------------------------------------------------------
const Part* Part::redirectPart() const
{
const Part* p = masterPart();
if (p != this)
return p;
return nullptr;
}
//---------------------------------------------------------
// Part::redirectPart
//---------------------------------------------------------
Part* Part::redirectPart()
{
Part* p = masterPart();
if (p != this)
return p;
return nullptr;
}
//---------------------------------------------------------
// readProperties
//---------------------------------------------------------
@ -178,7 +154,7 @@ void Part::write(XmlWriter& xml) const
xml.tag("trackName", _partName);
if (_color != DEFAULT_COLOR)
xml.tag("color", _color);
instrument()->write(xml, const_cast<Part*>(this)); // Safe, we do not write anything to it
instrument()->write(xml, this);
xml.etag();
}
@ -260,103 +236,13 @@ void Part::setMidiProgram(int program, int bank)
// instrument()->setChannel(0, c);
}
//---------------------------------------------------------
// volume
//---------------------------------------------------------
double Part::volume() const
{
return instrument()->channel(0)->volume();
}
//---------------------------------------------------------
// setVolume
//---------------------------------------------------------
void Part::setVolume(double volume)
{
instrument()->channel(0)->setVolume(volume);
}
//---------------------------------------------------------
// mute
//---------------------------------------------------------
bool Part::mute() const
{
return instrument()->channel(0)->mute();
}
//---------------------------------------------------------
// setMute
//---------------------------------------------------------
void Part::setMute(bool mute)
{
instrument()->channel(0)->setMute(mute);
}
//---------------------------------------------------------
// reverb
//---------------------------------------------------------
double Part::reverb() const
{
return instrument()->channel(0)->reverb();
}
//---------------------------------------------------------
// setReverb
//---------------------------------------------------------
void Part::setReverb(double val)
{
instrument()->channel(0)->setReverb(val);
}
//---------------------------------------------------------
// chorus
//---------------------------------------------------------
double Part::chorus() const
{
return instrument()->channel(0)->chorus();
}
//---------------------------------------------------------
// setChorus
//---------------------------------------------------------
void Part::setChorus(double val)
{
instrument()->channel(0)->setChorus(val);
}
//---------------------------------------------------------
// pan
//---------------------------------------------------------
double Part::pan() const
{
return instrument()->channel(0)->pan();
}
//---------------------------------------------------------
// setPan
//---------------------------------------------------------
void Part::setPan(double pan)
{
instrument()->channel(0)->setPan(pan);
}
//---------------------------------------------------------
// midiProgram
//---------------------------------------------------------
int Part::midiProgram() const
{
return instrument()->channel(0)->program();
return instrument()->playbackChannel(0, masterScore())->program();
}
//---------------------------------------------------------
@ -390,33 +276,10 @@ int Part::midiPort() const
void Part::setMidiChannel(int ch, int port, int tick)
{
Channel* channel = instrument(tick)->channel(0);
if (channel->channel() == -1) {
// Add new mapping
MidiMapping mm;
mm.part = this;
mm.articulation = channel;
mm.channel = -1;
mm.port = -1;
if (ch != -1)
mm.channel = ch;
if (port != -1)
mm.port = port;
channel->setChannel(masterScore()->midiMapping()->size());
masterScore()->midiMapping()->append(mm);
}
else {
// Update existing mapping
if (channel->channel() >= masterScore()->midiMapping()->size()) {
qDebug()<<"Can't' set midi channel: midiMapping is empty!";
return;
}
if (ch != -1)
masterScore()->midiMapping(channel->channel())->channel = ch;
if (port != -1)
masterScore()->midiMapping(channel->channel())->port = port;
masterScore()->midiMapping(channel->channel())->part = this;
}
if (channel->channel() == -1)
masterScore()->addMidiMapping(channel, this, port, ch);
else
masterScore()->updateMidiMapping(channel, this, port, ch);
}
//---------------------------------------------------------
@ -457,8 +320,6 @@ void Part::removeInstrument(int tick)
Instrument* Part::instrument(int tick)
{
if (Part* p = redirectPart())
return p->instrument(tick);
return _instruments.instrument(tick);
}
@ -468,8 +329,6 @@ Instrument* Part::instrument(int tick)
const Instrument* Part::instrument(int tick) const
{
if (const Part* p = redirectPart())
return p->instrument(tick);
return _instruments.instrument(tick);
}
@ -479,8 +338,6 @@ const Instrument* Part::instrument(int tick) const
const InstrumentList* Part::instruments() const
{
if (const Part* p = redirectPart())
return p->instruments();
return &_instruments;
}
@ -569,16 +426,6 @@ QVariant Part::getProperty(Pid id) const
return QVariant(_show);
case Pid::USE_DRUMSET:
return instrument()->useDrumset();
case Pid::PART_VOLUME:
return volume();
case Pid::PART_MUTE:
return mute();
case Pid::PART_PAN:
return pan();
case Pid::PART_REVERB:
return reverb();
case Pid::PART_CHORUS:
return chorus();
default:
return QVariant();
}
@ -597,21 +444,6 @@ bool Part::setProperty(Pid id, const QVariant& property)
case Pid::USE_DRUMSET:
instrument()->setUseDrumset(property.toBool());
break;
case Pid::PART_VOLUME:
setVolume(property.toInt());
break;
case Pid::PART_MUTE:
setMute(property.toBool());
break;
case Pid::PART_PAN:
setPan(property.toInt());
break;
case Pid::PART_REVERB:
setReverb(property.toInt());
break;
case Pid::PART_CHORUS:
setChorus(property.toInt());
break;
default:
qDebug("Part::setProperty: unknown id %d", int(id));
break;

View file

@ -54,11 +54,6 @@ class Part final : public ScoreElement {
static const int DEFAULT_COLOR = 0x3399ff;
int _color; ///User specified color for helping to label parts
const Part* masterPart() const;
Part* masterPart();
const Part* redirectPart() const;
Part* redirectPart();
public:
Part(Score* = 0);
void initFromInstrTemplate(const InstrumentTemplate*);
@ -97,17 +92,6 @@ class Part final : public ScoreElement {
void setStaves(int);
double volume() const;
void setVolume(double volume);
bool mute() const;
void setMute(bool mute);
double reverb() const;
void setReverb(double);
double chorus() const;
void setChorus(double);
double pan() const;
void setPan(double pan);
int midiProgram() const;
void setMidiProgram(int, int bank = 0);
@ -144,6 +128,9 @@ class Part final : public ScoreElement {
bool hasTabStaff();
bool hasDrumStaff();
const Part* masterPart() const;
Part* masterPart();
// Allows not reading the same instrument twice on importing 2.X scores.
// TODO: do we need instruments info in parts at all?
friend void readPart206(Part*, XmlReader&);

View file

@ -202,16 +202,11 @@ static constexpr PropertyMetaData propertyList[] = {
{ Pid::LINE_VISIBLE, true, "lineVisible", P_TYPE::BOOL, DUMMY_QT_TRANSLATE_NOOP("propertyName", "visible line") },
{ Pid::MAG, false, "mag", P_TYPE::REAL, DUMMY_QT_TRANSLATE_NOOP("propertyName", "mag") },
{ Pid::USE_DRUMSET, false, "useDrumset", P_TYPE::BOOL, DUMMY_QT_TRANSLATE_NOOP("propertyName", "using drumset") },
{ Pid::PART_VOLUME, false, "volume", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "volume") },
{ Pid::PART_MUTE, false, "mute", P_TYPE::BOOL, DUMMY_QT_TRANSLATE_NOOP("propertyName", "mute") },
{ Pid::PART_PAN, false, "pan", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "pan") },
{ Pid::PART_REVERB, false, "reverb", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "reverb") },
{ Pid::PART_CHORUS, false, "chorus", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "chorus") },
{ Pid::DURATION, false, 0, P_TYPE::FRACTION, DUMMY_QT_TRANSLATE_NOOP("propertyName", "duration") },
{ Pid::DURATION_TYPE, false, 0, P_TYPE::TDURATION, DUMMY_QT_TRANSLATE_NOOP("propertyName", "duration type") },
{ Pid::ROLE, false, "role", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "role") },
{ Pid::TRACK, false, 0, P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "track") },
{ Pid::GLISSANDO_STYLE, true, "glissandoStyle", P_TYPE::GLISSANDO_STYLE, DUMMY_QT_TRANSLATE_NOOP("propertyName", "glissando style") },
{ Pid::FRET_STRINGS, false, "strings", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "strings") },
{ Pid::FRET_FRETS, false, "frets", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "frets") },

View file

@ -220,16 +220,11 @@ enum class Pid {
LINE_VISIBLE,
MAG,
USE_DRUMSET,
PART_VOLUME,
PART_MUTE,
PART_PAN,
PART_REVERB,
PART_CHORUS,
DURATION,
DURATION_TYPE,
ROLE,
TRACK,
GLISSANDO_STYLE,
FRET_STRINGS,
FRET_FRETS,

View file

@ -3721,13 +3721,13 @@ ChordRest* Score::findCRinStaff(int tick, int staffIdx) const
void MasterScore::setSoloMute()
{
for (int i = 0; i < _midiMapping.size(); i++) {
Channel* b = _midiMapping[i].articulation;
for (unsigned i = 0; i < _midiMapping.size(); i++) {
Channel* b = _midiMapping[i].articulation();
if (b->solo()) {
b->setSoloMute(false);
for (int j = 0; j < _midiMapping.size(); j++) {
Channel* a = _midiMapping[j].articulation;
bool sameMidiMapping = _midiMapping[i].port == _midiMapping[j].port && _midiMapping[i].channel == _midiMapping[j].channel;
for (unsigned j = 0; j < _midiMapping.size(); j++) {
Channel* a = _midiMapping[j].articulation();
bool sameMidiMapping = _midiMapping[i].port() == _midiMapping[j].port() && _midiMapping[i].channel() == _midiMapping[j].channel();
a->setSoloMute((i != j && !a->solo() && !sameMidiMapping));
a->setSolo(i == j || a->solo() || sameMidiMapping);
}
@ -4445,6 +4445,37 @@ void MasterScore::setLayoutAll()
_cmdState.setTick(measures()->last() ? measures()->last()->endTick() : 0);
}
//---------------------------------------------------------
// setPlaybackScore
//---------------------------------------------------------
void MasterScore::setPlaybackScore(Score* score)
{
if (_playbackScore == score)
return;
_playbackScore = score;
_playbackSettingsLinks.clear();
if (!_playbackScore)
return;
for (MidiMapping& mm : _midiMapping)
mm.articulation()->setSoloMute(true);
for (Part* part : score->parts()) {
for (auto& i : *part->instruments()) {
Instrument* instr = i.second;
for (Channel* ch : instr->channel()) {
Channel* pChannel = playbackChannel(ch);
Q_ASSERT(pChannel);
if (!pChannel)
continue;
_playbackSettingsLinks.emplace_back(pChannel, ch, /* excerpt */ true);
}
}
}
}
//---------------------------------------------------------
// setLayout
//---------------------------------------------------------

View file

@ -20,6 +20,7 @@
#include "config.h"
#include "input.h"
#include "instrument.h"
#include "select.h"
#include "synthesizerstate.h"
#include "mscoreview.h"
@ -159,11 +160,24 @@ class MeasureBaseList {
// MidiMapping
//---------------------------------------------------------
struct MidiMapping {
Part* part;
Channel* articulation;
signed char port;
signed char channel;
class MidiMapping {
Part* _part;
std::unique_ptr<Channel> _articulation;
signed char _port;
signed char _channel;
Channel* masterChannel;
PartChannelSettingsLink link;
MidiMapping() = default; // should be created only within MasterScore
friend class MasterScore;
public:
Part* part() { return _part; }
const Part* part() const { return _part; }
Channel* articulation() { return _articulation.get(); }
const Channel* articulation() const { return _articulation.get(); }
signed char port() const { return _port; }
signed char channel() const { return _channel; }
};
//---------------------------------------------------------
@ -534,6 +548,8 @@ class Score : public QObject, public ScoreElement {
Score();
Score(MasterScore*, bool forcePartStyle = true);
Score(MasterScore*, const MStyle&);
Score(const Score&) = delete;
Score& operator=(const Score&) = delete;
virtual ~Score();
Score* clone();
@ -1186,6 +1202,8 @@ class MasterScore : public Score {
TempoMap* _tempomap;
RepeatList* _repeatList;
QList<Excerpt*> _excerpts;
std::vector<PartChannelSettingsLink> _playbackSettingsLinks;
Score* _playbackScore = nullptr;
Revisions* _revisions;
MasterScore* _next { 0 };
MasterScore* _prev { 0 };
@ -1201,7 +1219,7 @@ class MasterScore : public Score {
int _midiPortCount { 0 }; // A count of JACK/ALSA midi out ports
QQueue<MidiInputEvent> _midiInputQueue; // MIDI events that have yet to be processed
std::list<MidiInputEvent> _activeMidiPitches; // MIDI keys currently being held down
QList<MidiMapping> _midiMapping;
std::vector<MidiMapping> _midiMapping;
bool isSimpleMidiMaping; // midi mapping is simple if all ports and channels
// don't decrease and don't have gaps
QSet<int> occupiedMidiChannels; // each entry is port*16+channel, port range: 0-inf, channel: 0-15
@ -1209,6 +1227,7 @@ class MasterScore : public Score {
void parseVersion(const QString&);
void reorderMidiMapping();
void rebuildExcerptsMidiMapping();
void removeDeletedMidiMapping();
int updateMidiMapping();
@ -1280,10 +1299,12 @@ class MasterScore : public Score {
int midiPortCount() const { return _midiPortCount; }
void setMidiPortCount(int val) { _midiPortCount = val; }
QList<MidiMapping>* midiMapping() { return &_midiMapping; }
std::vector<MidiMapping>& midiMapping() { return _midiMapping; }
MidiMapping* midiMapping(int channel) { return &_midiMapping[channel]; }
int midiPort(int idx) const { return _midiMapping[idx].port; }
int midiChannel(int idx) const { return _midiMapping[idx].channel; }
void addMidiMapping(Channel* channel, Part* part, int midiPort, int midiChannel);
void updateMidiMapping(Channel* channel, Part* part, int midiPort, int midiChannel);
int midiPort(int idx) const { return _midiMapping[idx].port(); }
int midiChannel(int idx) const { return _midiMapping[idx].channel(); }
void rebuildMidiMapping();
void checkMidiMapping();
bool exportMidiMapping() { return !isSimpleMidiMaping; }
@ -1297,6 +1318,12 @@ class MasterScore : public Score {
void removeExcerpt(Excerpt*);
void deleteExcerpt(Excerpt*);
void setPlaybackScore(Score*);
Score* playbackScore() { return _playbackScore; }
const Score* playbackScore() const { return _playbackScore; }
Channel* playbackChannel(const Channel* c) { return _midiMapping[c->channel()].articulation(); }
const Channel* playbackChannel(const Channel* c) const { return _midiMapping[c->channel()].articulation(); }
QFileInfo* fileInfo() { return &info; }
const QFileInfo* fileInfo() const { return &info; }
void setName(const QString&);

View file

@ -95,13 +95,14 @@ bool MuseScore::saveAudio(Score* score, QIODevice *device, std::function<bool(fl
foreach(Part* part, score->parts()) {
const InstrumentList* il = part->instruments();
for(auto i = il->begin(); i!= il->end(); i++) {
for (const Channel* a : i->second->channel()) {
for (const Channel* instrChan : i->second->channel()) {
const Channel* a = score->masterScore()->playbackChannel(instrChan);
a->updateInitList();
for (MidiCoreEvent e : a->init) {
if (e.type() == ME_INVALID)
continue;
e.setChannel(a->channel());
int syntiIdx = synth->index(score->masterScore()->midiMapping(a->channel())->articulation->synti());
int syntiIdx = synth->index(score->masterScore()->midiMapping(a->channel())->articulation()->synti());
synth->play(e, syntiIdx);
}
}
@ -136,7 +137,7 @@ bool MuseScore::saveAudio(Score* score, QIODevice *device, std::function<bool(fl
const NPlayEvent& e = playPos->second;
if (e.isChannelEvent()) {
int channelIdx = e.channel();
Channel* c = score->masterScore()->midiMapping(channelIdx)->articulation;
const Channel* c = score->masterScore()->midiMapping(channelIdx)->articulation();
if (!c->mute()) {
synth->play(e, synth->index(c->synti()));
}

View file

@ -237,7 +237,8 @@ bool ExportMidi::write(QIODevice* device, bool midiExpandRepeats, bool exportRPN
// Pass through the all channels of the instrument
// "normal", "pizzicato", "tremolo" for Strings,
// "normal", "mute" for Trumpet
foreach(const Channel* ch, j->second->channel()) {
for (const Channel* instrChan : j->second->channel()) {
const Channel* ch = part->masterScore()->playbackChannel(instrChan);
char port = part->masterScore()->midiPort(ch->channel());
char channel = part->masterScore()->midiChannel(ch->channel());

View file

@ -5014,7 +5014,7 @@ static void partList(XmlWriter& xml, Score* score, const QList<Part*>& il, MxmlI
int instNr = ii.key();
int midiPort = part->midiPort() + 1;
if (ii.value()->channel().size() > 0)
midiPort = score->masterScore()->midiMapping(ii.value()->channel(0)->channel())->port + 1;
midiPort = score->masterScore()->midiMapping(ii.value()->channel(0)->channel())->port() + 1;
if (midiPort >= 1 && midiPort <= 16)
xml.tag(QString("midi-device %1 port=\"%2\"").arg(instrId(idx+1, instNr + 1)).arg(midiPort), "");
else

View file

@ -20,6 +20,7 @@
#include "musescore.h"
#include "parteditbase.h"
#include "libmscore/excerpt.h"
#include "libmscore/score.h"
#include "libmscore/part.h"
#include "mixer.h"
@ -177,7 +178,6 @@ void Mixer::keepScrollPosition()
_needToKeepScrollPosition = true;
}
//---------------------------------------------------------
// masterVolumeChanged
//---------------------------------------------------------
@ -197,6 +197,26 @@ void Mixer::masterVolumeChanged(double decibels)
masterSpin->blockSignals(false);
}
//---------------------------------------------------------
// on_partOnlyCheckBox_toggled
//---------------------------------------------------------
void Mixer::on_partOnlyCheckBox_toggled(bool checked)
{
if (!_activeScore->excerpt())
return;
mscore->setPlayPartOnly(checked);
setPlaybackScore(_activeScore->masterScore()->playbackScore());
// Prevent muted channels from sounding
for (const MidiMapping& mm : _activeScore->masterScore()->midiMapping()) {
const Channel* ch = mm.articulation();
if (ch && (ch->mute() || ch->soloMute()))
seq->stopNotes(ch->channel());
}
}
//---------------------------------------------------------
// retranslate
//---------------------------------------------------------
@ -278,10 +298,10 @@ PartEdit* Mixer::getPartAtIndex(int)
}
//---------------------------------------------------------
// setScore
// setPlaybackScore
//---------------------------------------------------------
void Mixer::setScore(MasterScore* score)
void Mixer::setPlaybackScore(Score* score)
{
if (_score != score) {
_score = score;
@ -290,6 +310,21 @@ void Mixer::setScore(MasterScore* score)
updateTracks();
}
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void Mixer::setScore(Score* score)
{
// No equality check, this function seems to need to cause
// mixer update every time it gets called.
_activeScore = score;
setPlaybackScore(_activeScore->masterScore()->playbackScore());
partOnlyCheckBox->setChecked(mscore->playPartOnly());
partOnlyCheckBox->setEnabled(_activeScore && !_activeScore->isMaster());
}
//---------------------------------------------------------
// updateTracks
//---------------------------------------------------------
@ -305,8 +340,8 @@ void Mixer::updateTracks()
//If nothing selected, select first available track
if (!_score->parts().isEmpty())
{
selPart = _score->parts()[0];
selChan = selPart->instrument(0)->channel(0);
selPart = _score->parts()[0]->masterPart();
selChan = selPart->instrument(0)->playbackChannel(0, _score->masterScore());
}
}
@ -333,7 +368,8 @@ void Mixer::updateTracks()
trackAreaLayout->addWidget(trackHolder);
for (Part* part : _score->parts()) {
for (Part* localPart : _score->parts()) {
Part* part = localPart->masterPart();
//Add per part tracks
bool expanded = expandedParts.contains(part);
const InstrumentList* il = part->instruments();
@ -342,7 +378,7 @@ void Mixer::updateTracks()
if (!il->empty()) {
il->begin();
proxyInstr = il->begin()->second;
proxyChan = proxyInstr->channel(0);
proxyChan = proxyInstr->playbackChannel(0, _score->masterScore());
}
MixerTrackItemPtr mti = std::make_shared<MixerTrackItem>(
@ -365,7 +401,7 @@ void Mixer::updateTracks()
for (auto it = il1->begin(); it != il1->end(); ++it) {
Instrument* instr = it->second;
for (int i = 0; i < instr->channel().size(); ++i) {
Channel *chan = instr->channel()[i];
Channel* chan = instr->playbackChannel(i, _score->masterScore());
MixerTrackItemPtr mti1 = std::make_shared<MixerTrackItem>(
MixerTrackItem::TrackType::CHANNEL, part, instr, chan);
// MixerTrackItemPtr mti = new MixerTrackItem(
@ -467,7 +503,7 @@ void MuseScore::showMixer(bool val)
connect(synti, SIGNAL(soundFontChanged()), mixer, SLOT(updateTracks()));
connect(mixer, SIGNAL(closed(bool)), a, SLOT(setChecked(bool)));
}
mixer->setScore(cs->masterScore());
mixer->setScore(cs);
mixer->setVisible(val);
}

View file

@ -63,7 +63,8 @@ class Mixer : public QDockWidget, public Ui::Mixer, public MixerTrackGroup
{
Q_OBJECT
MasterScore* _score;
Score* _score = nullptr; // playback score
Score* _activeScore = nullptr; // may be a _score itself or its excerpt;
QHBoxLayout* trackAreaLayout;
EnablePlayForWidget* enablePlay;
@ -84,6 +85,10 @@ class Mixer : public QDockWidget, public Ui::Mixer, public MixerTrackGroup
virtual void keyPressEvent(QKeyEvent*) override;
void readSettings();
void keepScrollPosition();
void setPlaybackScore(Score*);
private slots:
void on_partOnlyCheckBox_toggled(bool checked);
public slots:
void updateTracks();
@ -102,7 +107,7 @@ class Mixer : public QDockWidget, public Ui::Mixer, public MixerTrackGroup
public:
Mixer(QWidget* parent);
void setScore(MasterScore*);
void setScore(Score*);
PartEdit* getPartAtIndex(int index);
void writeSettings();
void expandToggled(Part* part, bool expanded) override;

View file

@ -27,6 +27,13 @@
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="partOnlyCheckBox">
<property name="text">
<string>Play part only</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="detailsArea" native="true">
<property name="sizePolicy">

View file

@ -161,7 +161,7 @@ void MixerDetails::updateFromTrack()
Channel* chan = _mti->focusedChan();
//Check if drumkit
bool drum = midiMap->part->instrument()->useDrumset();
const bool drum = midiMap->part()->instrument()->useDrumset();
drumkitCheck->blockSignals(true);
drumkitCheck->setChecked(drum);
drumkitCheck->blockSignals(false);
@ -235,8 +235,8 @@ void MixerDetails::updateFromTrack()
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);
portSpinBox->setValue(part->masterScore()->midiMapping(chan->channel())->port() + 1);
channelSpinBox->setValue(part->masterScore()->midiMapping(chan->channel())->channel() + 1);
trackColorLabel->blockSignals(false);
volumeSlider->blockSignals(false);
@ -351,7 +351,7 @@ void MixerDetails::propertyChanged(Channel::Prop property)
return;
MidiMapping* _midiMap = _mti->midiMap();
Channel* chan = _midiMap->articulation;
Channel* chan = _midiMap->articulation();
switch (property) {
case Channel::Prop::VOLUME: {
@ -480,8 +480,8 @@ void MixerDetails::patchChanged(int n)
return;
}
Part* part = _mti->midiMap()->part;
Channel* channel = _mti->midiMap()->articulation;
Part* part = _mti->midiMap()->part();
Channel* channel = _mti->midiMap()->articulation();
Score* score = part->score();
if (score) {
score->startCmd();
@ -552,8 +552,7 @@ void MixerDetails::midiChannelChanged(int)
int c = channelSpinBox->value() - 1;
MidiMapping* midiMap = _mti->midiMap();
midiMap->port = p;
midiMap->channel = c;
part->masterScore()->updateMidiMapping(midiMap->articulation(), part, p, c);
// channel->updateInitList();
part->score()->setInstrumentsChanged(true);

View file

@ -52,6 +52,15 @@ Channel *MixerTrackItem::focusedChan()
return _chan;
}
//---------------------------------------------------------
// playbackChannel
//---------------------------------------------------------
Channel* MixerTrackItem::playbackChannel(const Channel* channel)
{
return _part->masterScore()->playbackChannel(channel);
}
//---------------------------------------------------------
// color
//---------------------------------------------------------
@ -74,7 +83,8 @@ void MixerTrackItem::setVolume(char value)
const InstrumentList* il = _part->instruments();
for (auto it = il->begin(); it != il->end(); ++it) {
Instrument* instr = it->second;
for (Channel *chan: instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* chan = playbackChannel(instrChan);
if (chan->volume() != value) {
chan->setVolume(value);
seq->setController(chan->channel(), CTRL_VOLUME, chan->volume());
@ -104,7 +114,8 @@ void MixerTrackItem::setPan(char value)
const InstrumentList* il = _part->instruments();
for (auto it = il->begin(); it != il->end(); ++it) {
Instrument* instr = it->second;
for (Channel *chan: instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* chan = playbackChannel(instrChan);
if (chan->pan() != value) {
chan->setPan(value);
seq->setController(chan->channel(), CTRL_PANPOT, chan->pan());
@ -134,7 +145,8 @@ void MixerTrackItem::setChorus(char value)
const InstrumentList* il = _part->instruments();
for (auto it = il->begin(); it != il->end(); ++it) {
Instrument* instr = it->second;
for (Channel *chan: instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* chan = playbackChannel(instrChan);
if (chan->chorus() != value) {
chan->setChorus(value);
seq->setController(chan->channel(), CTRL_CHORUS_SEND, chan->chorus());
@ -164,7 +176,8 @@ void MixerTrackItem::setReverb(char value)
const InstrumentList* il = _part->instruments();
for (auto it = il->begin(); it != il->end(); ++it) {
Instrument* instr = it->second;
for (Channel *chan: instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* chan = playbackChannel(instrChan);
if (chan->reverb() != value) {
chan->setReverb(value);
seq->setController(chan->channel(), CTRL_REVERB_SEND, chan->reverb());
@ -194,7 +207,8 @@ void MixerTrackItem::setColor(int valueRgb)
const InstrumentList* il = _part->instruments();
for (auto it = il->begin(); it != il->end(); ++it) {
Instrument* instr = it->second;
for (Channel *chan: instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* chan = playbackChannel(instrChan);
chan->setColor(valueRgb);
}
}
@ -214,7 +228,8 @@ void MixerTrackItem::setMute(bool value)
const InstrumentList* il = _part->instruments();
for (auto it = il->begin(); it != il->end(); ++it) {
Instrument* instr = it->second;
for (Channel *chan: instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* chan = playbackChannel(instrChan);
if (value)
seq->stopNotes(chan->channel());
chan->setMute(value);
@ -238,7 +253,8 @@ void MixerTrackItem::setSolo(bool value)
const InstrumentList* il = _part->instruments();
for (auto it = il->begin(); it != il->end(); ++it) {
Instrument* instr = it->second;
for (Channel *chan: instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* chan = playbackChannel(instrChan);
chan->setSolo(value);
}
}
@ -253,7 +269,8 @@ void MixerTrackItem::setSolo(bool value)
const InstrumentList* il = p->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (Channel* a : instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* a = playbackChannel(instrChan);
if (a->solo()) {
numSolo++;
}
@ -265,7 +282,8 @@ void MixerTrackItem::setSolo(bool value)
const InstrumentList* il = p->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (Channel* a : instr->channel()) {
for (const Channel* instrChan: instr->channel()) {
Channel* a = playbackChannel(instrChan);
if (numSolo == 0) {
a->setSoloMute(false);
}

View file

@ -50,6 +50,8 @@ private:
Instrument* _instr;
Channel* _chan;
Channel* playbackChannel(const Channel* channel);
public:
MixerTrackItem(TrackType tt, Part* part, Instrument* _instr, Channel* _chan);

View file

@ -2391,6 +2391,9 @@ void MuseScore::setCurrentScoreView(ScoreView* view)
else
cs = 0;
if (cs)
cs->masterScore()->setPlaybackScore(_playPartOnly ? cs : cs->masterScore());
// set midi import panel
QString fileName = cs ? cs->importedFilePath() : "";
midiPanelOnSwitchToFile(fileName);
@ -2409,7 +2412,7 @@ void MuseScore::setCurrentScoreView(ScoreView* view)
if (selectionWindow)
selectionWindow->setScore(cs);
if (mixer)
mixer->setScore(cs ? cs->masterScore() : nullptr);
mixer->setScore(cs);
#ifdef OMR
if (omrPanel) {
if (cv && cv->omrView())
@ -4377,8 +4380,8 @@ void MuseScore::play(Element* e) const
seq->seek(tick);
Instrument* instr = part->instrument(tick);
for (Note* n : c->notes()) {
const Channel* channel = instr->channel(n->subchannel());
seq->startNote(channel->channel(), n->ppitch(), 80, n->tuning());
const int channel = instr->channel(n->subchannel())->channel();
seq->startNote(channel, n->ppitch(), 80, n->tuning());
}
seq->startNoteTimer(MScore::defaultPlayDuration);
}
@ -4405,8 +4408,8 @@ void MuseScore::play(Element* e, int pitch) const
if (tick < 0)
tick = 0;
Instrument* instr = masterNote->part()->instrument(tick);
const Channel* channel = instr->channel(masterNote->subchannel());
seq->startNote(channel->channel(), pitch, 80, MScore::defaultPlayDuration, masterNote->tuning());
const int channel = instr->channel(masterNote->subchannel())->channel();
seq->startNote(channel, pitch, 80, MScore::defaultPlayDuration, masterNote->tuning());
}
}
@ -5734,6 +5737,17 @@ void MuseScore::setPlayRepeats(bool repeat)
}
}
//---------------------------------------------------------
// setPlayPartOnly
//---------------------------------------------------------
void MuseScore::setPlayPartOnly(bool val)
{
_playPartOnly = val;
if (cs)
cs->masterScore()->setPlaybackScore(_playPartOnly ? cs : cs->masterScore());
}
//---------------------------------------------------------
// createScoreTab
//---------------------------------------------------------
@ -5760,7 +5774,7 @@ void MuseScore::cmd(QAction* a, const QString& cmd)
if (cmd == "instruments") {
editInstrList();
if (mixer)
mixer->setScore(cs->masterScore());
mixer->setScore(cs);
}
else if (cmd == "rewind") {
if (cs) {
@ -6245,7 +6259,7 @@ void MuseScore::noteTooShortForTupletDialog()
void MuseScore::instrumentChanged()
{
if (mixer)
mixer->setScore(cs->masterScore());
mixer->setScore(cs);
}
//---------------------------------------------------------
@ -6672,16 +6686,17 @@ bool MuseScore::saveMp3(Score* score, QIODevice* device, bool& wasCanceled)
//
// init instruments
//
foreach(Part* part, score->parts()) {
for (Part* part : score->parts()) {
const InstrumentList* il = part->instruments();
for(auto i = il->begin(); i!= il->end(); i++) {
for (const Channel* a : i->second->channel()) {
for (const Channel* channel : i->second->channel()) {
const Channel* a = score->masterScore()->playbackChannel(channel);
a->updateInitList();
for (MidiCoreEvent e : a->init) {
if (e.type() == ME_INVALID)
continue;
e.setChannel(a->channel());
int syntiIdx= synth->index(score->masterScore()->midiMapping(a->channel())->articulation->synti());
int syntiIdx= synth->index(score->masterScore()->midiMapping(a->channel())->articulation()->synti());
synth->play(e, syntiIdx);
}
}
@ -6731,7 +6746,7 @@ bool MuseScore::saveMp3(Score* score, QIODevice* device, bool& wasCanceled)
const NPlayEvent& e = playPos->second;
if (e.isChannelEvent()) {
int channelIdx = e.channel();
Channel* c = score->masterScore()->midiMapping(channelIdx)->articulation;
Channel* c = score->masterScore()->midiMapping(channelIdx)->articulation();
if (!c->mute()) {
synth->play(e, synth->index(c->synti()));
}

View file

@ -238,6 +238,8 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
static const std::list<const char*> _allPlaybackControlEntries;
std::list<const char*> _playbackControlEntries { _allPlaybackControlEntries };
bool _playPartOnly = true; // play part only vs. full score
QVBoxLayout* layout; // main window layout
QSplitter* splitter;
ScoreTab* tab1;
@ -847,6 +849,9 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
void setPlaybackControlEntries(std::list<const char*> l) { _playbackControlEntries = l; }
void populatePlaybackControls();
bool playPartOnly() const { return _playPartOnly; }
void setPlayPartOnly(bool val);
static void updateUiStyleAndTheme();
void showError();

View file

@ -290,10 +290,10 @@ void MuseScore::oscVolChannel(double val)
PathObject* po = (PathObject*) sender();
int i = po->path().mid(4).toInt() - 1;
QList<MidiMapping>* mms = cs->masterScore()->midiMapping();
if( i >= 0 && i < mms->size()) {
MidiMapping mm = mms->at(i);
Channel* channel = mm.articulation;
auto& mms = cs->masterScore()->midiMapping();
if( i >= 0 && i < int(mms.size())) {
MidiMapping& mm = mms[i];
Channel* channel = mm.articulation();
int iv = lrint(val*127);
seq->setController(channel->channel(), CTRL_VOLUME, iv);
channel->setVolume(val * 100.0);
@ -313,10 +313,10 @@ void MuseScore::oscPanChannel(double val)
PathObject* po = (PathObject*) sender();
int i = po->path().mid(4).toInt() - 1;
QList<MidiMapping>* mms = cs->masterScore()->midiMapping();
if (i >= 0 && i < mms->size()) {
MidiMapping mm = mms->at(i);
Channel* channel = mm.articulation;
auto& mms = cs->masterScore()->midiMapping();
if (i >= 0 && i < int(mms.size())) {
MidiMapping& mm = mms[i];
Channel* channel = mm.articulation();
int iv = lrint((val + 1) * 64);
seq->setController(channel->channel(), CTRL_PANPOT, iv);
channel->setPan(val * 180.0);
@ -336,10 +336,10 @@ void MuseScore::oscMuteChannel(double val)
PathObject* po = (PathObject*) sender();
int i = po->path().mid(5).toInt() - 1;
QList<MidiMapping>* mms = cs->masterScore()->midiMapping();
if (i >= 0 && i < mms->size()) {
MidiMapping mm = mms->at(i);
Channel* channel = mm.articulation;
auto& mms = cs->masterScore()->midiMapping();
if (i >= 0 && i < int(mms.size())) {
MidiMapping& mm = mms[i];
Channel* channel = mm.articulation();
channel->setMute(val==0.0f ? false : true);
if (mixer)
mixer->getPartAtIndex(i)->mute->setChecked(channel->mute());

View file

@ -113,8 +113,8 @@ void PartEdit::setPart(Part* p, Channel* a)
}
}
_setChecked(drumset, p->instrument()->useDrumset());
_setValue(portSpinBox, part->masterScore()->midiMapping(a->channel())->port + 1);
_setValue(channelSpinBox, part->masterScore()->midiMapping(a->channel())->channel + 1);
_setValue(portSpinBox, part->masterScore()->midiMapping(a->channel())->port() + 1);
_setValue(channelSpinBox, part->masterScore()->midiMapping(a->channel())->channel() + 1);
QHBoxLayout* hb = voiceButtonBox;
int idx = 0;
@ -286,7 +286,8 @@ void PartEdit::soloToggled(bool val, bool syncControls)
const InstrumentList* il = p->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (Channel* a : instr->channel()) {
for (Channel* instrChan : instr->channel()) {
Channel* a = part->masterScore()->playbackChannel(instrChan);
a->setSoloMute((channel != a && !a->solo()));
a->setSolo(channel == a || a->solo());
if (a->soloMute())
@ -302,7 +303,8 @@ void PartEdit::soloToggled(bool val, bool syncControls)
const InstrumentList* il = p->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (Channel* a : instr->channel()) {
for (Channel* instrChan : instr->channel()) {
Channel* a = part->masterScore()->playbackChannel(instrChan);
if (a->solo()){
found = true;
break;
@ -315,7 +317,8 @@ void PartEdit::soloToggled(bool val, bool syncControls)
const InstrumentList* il = p->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (Channel* a : instr->channel()) {
for (Channel* instrChan : instr->channel()) {
Channel* a = part->masterScore()->playbackChannel(instrChan);
a->setSoloMute(false);
a->setSolo(false);
}
@ -428,7 +431,7 @@ void PartEdit::midiChannelChanged(int)
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);
int direction = copysign(1, c - part->masterScore()->midiMapping(channel->channel())->channel());
// Channel 9 is special for drums
if (part->instrument()->useDrumset() && c != 9) {
@ -484,8 +487,8 @@ void PartEdit::midiChannelChanged(int)
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);
_setValue(channelSpinBox, part->masterScore()->midiMapping(channel->channel())->channel() + 1);
_setValue(portSpinBox, part->masterScore()->midiMapping(channel->channel())->port() + 1);
needSync = false;
break;
}
@ -495,10 +498,12 @@ void PartEdit::midiChannelChanged(int)
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;
const int port = newChannel / 16;
const int channelNo = newChannel % 16;
_setValue(channelSpinBox, channelNo + 1);
_setValue(portSpinBox, port + 1);
MasterScore* ms = part->masterScore();
ms->updateMidiMapping(ms->playbackChannel(channel), part, port, channelNo);
channel->setVolume(lrint(pe->volume->value()));
channel->setPan(lrint(pe->pan->value()));
channel->setReverb(lrint(pe->reverb->value()));
@ -522,12 +527,14 @@ void PartEdit::midiChannelChanged(int)
}
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();
const int port = newChannel / 16;
const int channelNo = newChannel % 16;
_setValue(channelSpinBox, channelNo + 1);
_setValue(portSpinBox, port + 1);
MasterScore* ms = part->masterScore();
ms->updateMidiMapping(ms->playbackChannel(channel), part, port, channelNo);
ms->setInstrumentsChanged(true);
ms->setLayoutAll();
seq->initInstruments();
}
else {

View file

@ -191,11 +191,6 @@ class Element : public Ms::PluginAPI::ScoreElement {
API_PROPERTY( lineVisible, LINE_VISIBLE )
API_PROPERTY( mag, MAG )
API_PROPERTY( useDrumset, USE_DRUMSET )
API_PROPERTY( partVolume, PART_VOLUME )
API_PROPERTY( partMute, PART_MUTE )
API_PROPERTY( partPan, PART_PAN )
API_PROPERTY( partReverb, PART_REVERB )
API_PROPERTY( partChorus, PART_CHORUS )
API_PROPERTY( duration, DURATION )
API_PROPERTY( durationType, DURATION_TYPE )
API_PROPERTY( role, ROLE )

View file

@ -518,7 +518,7 @@ void Seq::playEvent(const NPlayEvent& event, unsigned framePos)
if (note) {
Staff* staff = note->staff();
Instrument* instr = staff->part()->instrument(note->chord()->tick());
const Channel* a = instr->channel(note->subchannel());
const Channel* a = instr->playbackChannel(note->subchannel(), cs);
mute = a->mute() || a->soloMute() || !staff->playbackVoice(note->voice());
}
if (!mute) {
@ -968,8 +968,8 @@ void Seq::initInstruments(bool realTime)
_driver->updateOutPortCount(maxMidiOutPort + 1);
}
foreach(const MidiMapping& mm, *cs->midiMapping()) {
Channel* channel = mm.articulation;
for (const MidiMapping& mm : cs->midiMapping()) {
const Channel* channel = mm.articulation();
for (const MidiCoreEvent& e : channel->init) {
if (e.type() == ME_INVALID)
continue;
@ -980,7 +980,7 @@ void Seq::initInstruments(bool realTime)
sendEvent(event);
}
// Setting pitch bend sensitivity to 12 semitones for external synthesizers
if ((preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO)) && mm.channel != 9) {
if ((preferences.getBool(PREF_IO_JACK_USEJACKMIDI) || preferences.getBool(PREF_IO_ALSA_USEALSAAUDIO)) && mm.channel() != 9) {
if (realTime) {
putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_LRPN, 0));
putEvent(NPlayEvent(ME_CONTROLLER, channel->channel(), CTRL_HRPN, 0));
@ -1204,7 +1204,7 @@ void Seq::stopNotes(int channel, bool realTime)
};
// Stop notes in all channels
if (channel == -1) {
for(int ch = 0; ch < cs->midiMapping()->size(); ch++) {
for(int ch = 0; ch < int(cs->midiMapping().size()); ch++) {
send(NPlayEvent(ME_CONTROLLER, ch, CTRL_SUSTAIN, 0));
send(NPlayEvent(ME_CONTROLLER, ch, CTRL_ALL_NOTES_OFF, 0));
if (cs->midiChannel(ch) != 9)
@ -1423,13 +1423,13 @@ void Seq::putEvent(const NPlayEvent& event, unsigned framePos)
if (!cs)
return;
int channel = event.channel();
if (channel >= cs->midiMapping()->size()) {
qDebug("bad channel value %d >= %d", channel, cs->midiMapping()->size());
if (channel >= int(cs->midiMapping().size())) {
qDebug("bad channel value %d >= %d", channel, int(cs->midiMapping().size()));
return;
}
// audio
int syntiIdx= _synti->index(cs->midiMapping(channel)->articulation->synti());
int syntiIdx= _synti->index(cs->midiMapping(channel)->articulation()->synti());
_synti->play(event, syntiIdx);
// midi

View file

@ -383,7 +383,7 @@ void StaffTextProperties::channelItemChanged(QTreeWidgetItem* item, QTreeWidgetI
int channelIdx = item->data(0, Qt::UserRole).toInt();
int tick = static_cast<Segment*>(_staffText->parent())->tick();
Channel* channel = part->instrument(tick)->channel(channelIdx);
const Channel* channel = part->instrument(tick)->channel(channelIdx);
QString channelName = channel->name();
for (const NamedEventList& e : part->instrument(tick)->midiActions()) {
@ -441,7 +441,7 @@ void StaffTextProperties::saveValues()
if (vb[voice][row]->isChecked()) {
int idx = channelCombo[row]->currentIndex();
int instrId = static_cast<Segment*>(_staffText->parent())->tick();
_staffText->setChannelName(voice, part->instrument(instrId)->channel()[idx]->name());
_staffText->setChannelName(voice, part->instrument(instrId)->channel(idx)->name());
break;
}
}

View file

@ -2837,7 +2837,7 @@ void Timeline::requestInstrumentDialog()
QAction* act = getAction("instruments");
mscore->cmd(act);
if (mscore->getMixer())
mscore->getMixer()->setScore(_score->masterScore());
mscore->getMixer()->setScore(_score);
}
}