MuseScore/src/engraving/libmscore/instrtemplate.cpp
Peter Jonas 28fa24467c Update instruments.xml to add new families and improve sorting
Run share/instruments/update_instruments_xml.py to fetch the latest
version of the online spreadsheet. In this version, every instrument
has been assigned a family. Also, instruments are now sorted based on
their group, family and minimum professional pitch, among other things.
This ensures they appear roughly in score order according to standard
orchestral layout. Finally, descriptions have been improved and a few
small fixed made to fix incorrect or missing data.

In addition, some compatibility code was changed to improve instrument
recognition in older MSCX score files that lack the appropriate
MuseScore or MusicXML instrument IDs. The old code returned the *first*
instrument that matched a given criteria (e.g. same trackName), whereas
the new code returns the instrument that gives the *best* match
according to multiple criteria. This means the return value is less
dependent on the order in which instruments are defined within
instruments.xml.
2021-07-08 16:13:51 +01:00

944 lines
30 KiB
C++

/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA 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 3 as
* published by the Free Software Foundation.
*
* 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, see <https://www.gnu.org/licenses/>.
*/
#include "instrtemplate.h"
#include "translation.h"
#include "bracket.h"
#include "drumset.h"
#include "stafftype.h"
#include "style.h"
#include "stringdata.h"
#include "utils.h"
#include "xml.h"
using namespace mu;
namespace Ms {
QList<InstrumentGroup*> instrumentGroups;
QList<MidiArticulation> articulation; // global articulations
QList<InstrumentGenre*> instrumentGenres;
QList<InstrumentFamily*> instrumentFamilies;
//---------------------------------------------------------
// searchInstrumentGenre
//---------------------------------------------------------
static InstrumentGenre* searchInstrumentGenre(const QString& genre)
{
for (InstrumentGenre* ig : qAsConst(instrumentGenres)) {
if (ig->id == genre) {
return ig;
}
}
return nullptr;
}
//---------------------------------------------------------
// searchInstrumentFamily
//---------------------------------------------------------
static InstrumentFamily* searchInstrumentFamily(const QString& name)
{
for (InstrumentFamily* fam : qAsConst(instrumentFamilies)) {
if (fam->id == name) {
return fam;
}
}
return nullptr;
}
//---------------------------------------------------------
// searchInstrumentGroup
//---------------------------------------------------------
InstrumentGroup* searchInstrumentGroup(const QString& name)
{
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
if (g->id == name) {
return g;
}
}
return nullptr;
}
//---------------------------------------------------------
// searchArticulation
//---------------------------------------------------------
static MidiArticulation searchArticulation(const QString& name)
{
for (MidiArticulation a : qAsConst(articulation)) {
if (a.name == name) {
return a;
}
}
return MidiArticulation();
}
//---------------------------------------------------------
// readStaffIdx
//---------------------------------------------------------
static int readStaffIdx(XmlReader& e)
{
int idx = e.intAttribute("staff", 1) - 1;
if (idx >= MAX_STAVES) {
idx = MAX_STAVES - 1;
}
if (idx < 0) {
idx = 0;
}
return idx;
}
//---------------------------------------------------------
// read InstrumentGroup
//---------------------------------------------------------
void InstrumentGroup::read(XmlReader& e)
{
id = e.attribute("id");
name = qtrc("InstrumentsXML", e.attribute("name").toUtf8().data());
extended = e.intAttribute("extended", 0);
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "instrument" || tag == "Instrument") {
QString sid = e.attribute("id");
InstrumentTemplate* t = searchTemplate(sid);
if (t == 0) {
t = new InstrumentTemplate;
t->articulation.append(articulation); // init with global articulation
instrumentTemplates.append(t);
}
t->read(e);
} else if (tag == "ref") {
InstrumentTemplate* ttt = searchTemplate(e.readElementText());
if (ttt) {
InstrumentTemplate* t = new InstrumentTemplate(*ttt);
instrumentTemplates.append(t);
} else {
qDebug("instrument reference not found <%s>", e.text().toUtf8().data());
}
} else if (tag == "name") {
name = qtrc("InstrumentsXML", e.readElementText().toUtf8().data());
} else if (tag == "extended") {
extended = e.readInt();
} else {
e.unknown();
}
}
if (id.isEmpty()) {
id = name.toLower().replace(" ", "-");
}
}
//---------------------------------------------------------
// clear InstrumentGroup
//---------------------------------------------------------
void InstrumentGroup::clear()
{
qDeleteAll(instrumentTemplates);
instrumentTemplates.clear();
}
//---------------------------------------------------------
// InstrumentTemplate
//---------------------------------------------------------
InstrumentTemplate::InstrumentTemplate()
{
staves = 1;
minPitchA = 0;
maxPitchA = 127;
minPitchP = 0;
maxPitchP = 127;
staffGroup = StaffGroup::STANDARD;
staffTypePreset = 0;
useDrumset = false;
drumset = 0;
extended = false;
singleNoteDynamics = true;
family = nullptr;
for (int i = 0; i < MAX_STAVES; ++i) {
clefTypes[i]._concertClef = ClefType::G;
clefTypes[i]._transposingClef = ClefType::G;
staffLines[i] = 5;
smallStaff[i] = false;
bracket[i] = BracketType::NO_BRACKET;
bracketSpan[i] = 0;
barlineSpan[i] = false;
}
transpose.diatonic = 0;
transpose.chromatic = 0;
}
InstrumentTemplate::InstrumentTemplate(const InstrumentTemplate& t)
{
init(t);
}
//---------------------------------------------------------
// init
//---------------------------------------------------------
void InstrumentTemplate::init(const InstrumentTemplate& t)
{
longNames = t.longNames;
shortNames = t.shortNames;
musicXMLid = t.musicXMLid;
staves = t.staves;
extended = t.extended;
for (int i = 0; i < MAX_STAVES; ++i) {
clefTypes[i] = t.clefTypes[i];
staffLines[i] = t.staffLines[i];
smallStaff[i] = t.smallStaff[i];
bracket[i] = t.bracket[i];
bracketSpan[i] = t.bracketSpan[i];
barlineSpan[i] = t.barlineSpan[i];
}
minPitchA = t.minPitchA;
maxPitchA = t.maxPitchA;
minPitchP = t.minPitchP;
maxPitchP = t.maxPitchP;
transpose = t.transpose;
staffGroup = t.staffGroup;
staffTypePreset = t.staffTypePreset;
useDrumset = t.useDrumset;
if (t.drumset) {
drumset = new Drumset(*t.drumset);
} else {
drumset = 0;
}
stringData = t.stringData;
midiActions = t.midiActions;
channel = t.channel;
family = t.family;
singleNoteDynamics = t.singleNoteDynamics;
}
InstrumentTemplate::~InstrumentTemplate()
{
delete drumset;
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void InstrumentTemplate::write(XmlWriter& xml) const
{
xml.stag(QString("Instrument id=\"%1\"").arg(id));
longNames.write(xml, "longName");
shortNames.write(xml, "shortName");
if (longNames.size() > 1) {
xml.tag("trackName", trackName);
}
xml.tag("description", description);
xml.tag("musicXMLid", musicXMLid);
if (extended) {
xml.tag("extended", extended);
}
stringData.write(xml);
if (staves > 1) {
xml.tag("staves", staves);
}
for (int i = 0; i < staves; ++i) {
if (clefTypes[i]._concertClef == clefTypes[i]._transposingClef) {
QString tag = ClefInfo::tag(clefTypes[i]._concertClef);
if (i) {
xml.tag(QString("clef staff=\"%1\"").arg(i + 1), tag);
} else {
xml.tag("clef", tag);
}
} else {
QString tag1 = ClefInfo::tag(clefTypes[i]._concertClef);
QString tag2 = ClefInfo::tag(clefTypes[i]._transposingClef);
if (i) {
xml.tag(QString("concertClef staff=\"%1\"").arg(i + 1), tag1);
xml.tag(QString("transposingClef staff=\"%1\"").arg(i + 1), tag2);
} else {
xml.tag("concertClef", tag1);
xml.tag("transposingClef", tag2);
}
}
if (staffLines[i] != 5) {
if (i) {
xml.tag(QString("stafflines staff=\"%1\"").arg(i + 1), staffLines[i]);
} else {
xml.tag("stafflines", staffLines[i]);
}
}
if (smallStaff[i]) {
if (i) {
xml.tag(QString("smallStaff staff=\"%1\"").arg(i + 1), smallStaff[i]);
} else {
xml.tag("smallStaff", smallStaff[i]);
}
}
if (bracket[i] != BracketType::NO_BRACKET) {
if (i) {
xml.tag(QString("bracket staff=\"%1\"").arg(i + 1), int(bracket[i]));
} else {
xml.tag("bracket", int(bracket[i]));
}
}
if (bracketSpan[i] != 0) {
if (i) {
xml.tag(QString("bracketSpan staff=\"%1\"").arg(i + 1), bracketSpan[i]);
} else {
xml.tag("bracketSpan", bracketSpan[i]);
}
}
if (barlineSpan[i]) {
if (i) {
xml.tag(QString("barlineSpan staff=\"%1\"").arg(i + 1), barlineSpan[i]);
} else {
xml.tag("barlineSpan", barlineSpan[i]);
}
}
}
if (minPitchA != 0 || maxPitchA != 127) {
xml.tag("aPitchRange", QString("%1-%2").arg(int(minPitchA)).arg(int(maxPitchA)));
}
if (minPitchP != 0 || maxPitchP != 127) {
xml.tag("pPitchRange", QString("%1-%2").arg(int(minPitchP)).arg(int(maxPitchP)));
}
if (transpose.diatonic) {
xml.tag("transposeDiatonic", transpose.diatonic);
}
if (transpose.chromatic) {
xml.tag("transposeChromatic", transpose.chromatic);
}
if (useDrumset) {
xml.tag("drumset", int(useDrumset));
}
if (drumset) {
drumset->save(xml);
}
if (!singleNoteDynamics) { // default is true
xml.tag("singleNoteDynamics", singleNoteDynamics);
}
for (const NamedEventList& a : midiActions) {
a.write(xml, "MidiAction");
}
for (const Channel& a : channel) {
a.write(xml, nullptr);
}
for (const MidiArticulation& ma : articulation) {
bool isGlobal = false;
for (const MidiArticulation& ga : qAsConst(Ms::articulation)) {
if (ma == ga) {
isGlobal = true;
break;
}
}
if (!isGlobal) {
ma.write(xml);
}
}
if (family) {
xml.tag("family", family->id);
}
xml.etag();
}
//---------------------------------------------------------
// write1
// output only translatable names
//---------------------------------------------------------
void InstrumentTemplate::write1(XmlWriter& xml) const
{
xml.stag(QString("Instrument id=\"%1\"").arg(id));
longNames.write(xml, "longName");
shortNames.write(xml, "shortName");
if (longNames.size() > 1) {
xml.tag("trackName", trackName);
}
xml.tag("description", description);
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void InstrumentTemplate::read(XmlReader& e)
{
id = e.attribute("id");
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "longName" || tag == "name") { // "name" is obsolete
int pos = e.intAttribute("pos", 0);
for (QList<StaffName>::iterator i = longNames.begin(); i != longNames.end(); ++i) {
if ((*i).pos() == pos) {
longNames.erase(i);
break;
}
}
longNames.append(StaffName(qtrc("InstrumentsXML", e.readElementText().toUtf8().data()), pos));
} else if (tag == "shortName" || tag == "short-name") { // "short-name" is obsolete
int pos = e.intAttribute("pos", 0);
for (QList<StaffName>::iterator i = shortNames.begin(); i != shortNames.end(); ++i) {
if ((*i).pos() == pos) {
shortNames.erase(i);
break;
}
}
shortNames.append(StaffName(qtrc("InstrumentsXML", e.readElementText().toUtf8().data()), pos));
} else if (tag == "trackName") {
trackName = qtrc("InstrumentsXML", e.readElementText().toUtf8().data());
} else if (tag == "description") {
description = e.readElementText();
} else if (tag == "extended") {
extended = e.readInt();
} else if (tag == "staves") {
staves = e.readInt();
bracketSpan[0] = staves;
// for (int i = 0; i < staves-1; ++i)
// barlineSpan[i] = true;
} else if (tag == "clef") { // sets both transposing and concert clef
int idx = readStaffIdx(e);
QString val(e.readElementText());
bool ok;
int i = val.toInt(&ok);
ClefType ct = ok ? ClefType(i) : Clef::clefType(val);
clefTypes[idx]._concertClef = ct;
clefTypes[idx]._transposingClef = ct;
} else if (tag == "concertClef") {
int idx = readStaffIdx(e);
QString val(e.readElementText());
bool ok;
int i = val.toInt(&ok);
clefTypes[idx]._concertClef = ok ? ClefType(i) : Clef::clefType(val);
} else if (tag == "transposingClef") {
int idx = readStaffIdx(e);
QString val(e.readElementText());
bool ok;
int i = val.toInt(&ok);
clefTypes[idx]._transposingClef = ok ? ClefType(i) : Clef::clefType(val);
} else if (tag == "stafflines") {
int idx = readStaffIdx(e);
staffLines[idx] = e.readInt();
} else if (tag == "smallStaff") {
int idx = readStaffIdx(e);
smallStaff[idx] = e.readInt();
} else if (tag == "bracket") {
int idx = readStaffIdx(e);
bracket[idx] = BracketType(e.readInt());
} else if (tag == "bracketSpan") {
int idx = readStaffIdx(e);
bracketSpan[idx] = e.readInt();
} else if (tag == "barlineSpan") {
int idx = readStaffIdx(e);
int span = e.readInt();
for (int i = 0; i < span - 1; ++i) {
barlineSpan[idx + i] = true;
}
} else if (tag == "aPitchRange") {
setPitchRange(e.readElementText(), &minPitchA, &maxPitchA);
} else if (tag == "pPitchRange") {
setPitchRange(e.readElementText(), &minPitchP, &maxPitchP);
} else if (tag == "transposition") { // obsolete
int i = e.readInt();
transpose.chromatic = i;
transpose.diatonic = chromatic2diatonic(i);
} else if (tag == "transposeChromatic") {
transpose.chromatic = e.readInt();
} else if (tag == "transposeDiatonic") {
transpose.diatonic = e.readInt();
} else if (tag == "StringData") {
stringData.read(e);
} else if (tag == "drumset") {
useDrumset = e.readInt();
} else if (tag == "Drum") {
// if we see one of this tags, a custom drumset will
// be created
if (drumset == 0) {
drumset = new Drumset(*smDrumset);
drumset->clear();
}
drumset->load(e);
} else if (tag == "MidiAction") {
NamedEventList a;
a.read(e);
midiActions.append(a);
} else if (tag == "Channel" || tag == "channel") {
Channel a;
a.read(e, nullptr);
channel.append(a);
} else if (tag == "Articulation") {
MidiArticulation a;
a.read(e);
int n = articulation.size();
int i;
for (i = 0; i < n; ++i) {
if (articulation[i].name == a.name) {
articulation[i] = a;
break;
}
}
if (i == n) {
articulation.append(a);
}
} else if (tag == "stafftype") {
int staffIdx = readStaffIdx(e);
QString xmlPresetName = e.attribute("staffTypePreset", "");
QString stfGroup = e.readElementText();
if (stfGroup == "percussion") {
staffGroup = StaffGroup::PERCUSSION;
} else if (stfGroup == "tablature") {
staffGroup = StaffGroup::TAB;
} else {
staffGroup = StaffGroup::STANDARD;
}
staffTypePreset = 0;
if (!xmlPresetName.isEmpty()) {
staffTypePreset = StaffType::presetFromXmlName(xmlPresetName);
}
if (!staffTypePreset || staffTypePreset->group() != staffGroup) {
staffTypePreset = StaffType::getDefaultPreset(staffGroup);
}
if (staffTypePreset) {
staffLines[staffIdx] = staffTypePreset->lines();
}
} else if (tag == "init") {
QString val(e.readElementText());
InstrumentTemplate* ttt = searchTemplate(val);
if (ttt) {
init(*ttt);
} else {
qDebug("InstrumentTemplate:: init instrument <%s> not found", qPrintable(val));
}
} else if (tag == "musicXMLid") {
musicXMLid = e.readElementText();
} else if (tag == "family") {
family = searchInstrumentFamily(e.readElementText());
} else if (tag == "genre") {
QString val(e.readElementText());
linkGenre(val);
} else if (tag == "singleNoteDynamics") {
singleNoteDynamics = e.readBool();
} else {
e.unknown();
}
}
if (channel.empty()) {
Channel a;
a.setChorus(0);
a.setReverb(0);
a.setName(Channel::DEFAULT_NAME);
a.setProgram(0);
a.setBank(0);
a.setVolume(90);
a.setPan(0);
channel.append(a);
}
if (trackName.isEmpty() && !longNames.isEmpty()) {
trackName = longNames[0].name();
}
if (description.isEmpty() && !longNames.isEmpty()) {
description = longNames[0].name();
}
if (id.isEmpty()) {
id = trackName.toLower().replace(" ", "-");
}
if (staves == 0) {
qDebug(" 2Instrument: staves == 0 <%s>", qPrintable(id));
}
}
//---------------------------------------------------------
// setPitchRange
//---------------------------------------------------------
void InstrumentTemplate::setPitchRange(const QString& s, char* a, char* b) const
{
QStringList sl = s.split("-");
if (sl.size() != 2) {
*a = 0;
*b = 127;
return;
}
*a = sl[0].toInt();
*b = sl[1].toInt();
}
//---------------------------------------------------------
// clearInstrumentTemplates
//---------------------------------------------------------
void clearInstrumentTemplates()
{
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
g->clear();
}
qDeleteAll(instrumentGroups);
instrumentGroups.clear();
qDeleteAll(instrumentGenres);
instrumentGenres.clear();
qDeleteAll(instrumentFamilies);
instrumentFamilies.clear();
articulation.clear();
}
//---------------------------------------------------------
// loadInstrumentTemplates
//---------------------------------------------------------
bool loadInstrumentTemplates(const QString& instrTemplates)
{
QFile qf(instrTemplates);
if (!qf.open(QIODevice::Text | QIODevice::ReadOnly)) {
qDebug("cannot load instrument templates at <%s>", qPrintable(instrTemplates));
return false;
}
XmlReader e(&qf);
while (e.readNextStartElement()) {
if (e.name() == "museScore") {
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "instrument-group" || tag == "InstrumentGroup") {
QString idGroup(e.attribute("id"));
InstrumentGroup* group = searchInstrumentGroup(idGroup);
if (group == 0) {
group = new InstrumentGroup;
instrumentGroups.append(group);
}
group->read(e);
} else if (tag == "Articulation") {
// read global articulation
QString name(e.attribute("name"));
MidiArticulation a = searchArticulation(name);
a.read(e);
articulation.append(a);
} else if (tag == "Genre") {
QString idGenre(e.attribute("id"));
InstrumentGenre* genre = searchInstrumentGenre(idGenre);
if (!genre) {
genre = new InstrumentGenre;
instrumentGenres.append(genre);
}
genre->read(e);
} else if (tag == "Family") {
QString idFamily(e.attribute("id"));
InstrumentFamily* fam = searchInstrumentFamily(idFamily);
if (!fam) {
fam = new InstrumentFamily;
instrumentFamilies.append(fam);
}
fam->read(e);
} else {
e.unknown();
}
}
}
}
return true;
}
//---------------------------------------------------------
// searchTemplate
//---------------------------------------------------------
InstrumentTemplate* searchTemplate(const QString& name)
{
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
if (it->id == name) {
return it;
}
}
}
return 0;
}
//---------------------------------------------------------
// searchTemplateForMusicXMLid
//---------------------------------------------------------
InstrumentTemplate* searchTemplateForMusicXmlId(const QString& mxmlId)
{
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
if (it->musicXMLid == mxmlId) {
return it;
}
}
}
return 0;
}
InstrumentTemplate* searchTemplateForInstrNameList(const QList<QString>& nameList)
{
InstrumentTemplate* bestMatch = nullptr; // default if no matches
int bestMatchStrength = 0; // higher for better matches
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
for (const QString& name : nameList) {
int matchStrength = 0
+ (4 * (it->trackName == name)) // most weight to track name since there are fewer duplicates
+ (2 * it->longNames.contains(StaffName(name)))
+ (1 * it->shortNames.contains(StaffName(name))); // least weight to short name
const int perfectMatchStrength = 7;
Q_ASSERT(matchStrength <= perfectMatchStrength);
if (matchStrength > bestMatchStrength) {
bestMatch = it;
bestMatchStrength = matchStrength;
if (bestMatchStrength == perfectMatchStrength) {
break; // stop looking for matches
}
}
}
}
}
return bestMatch; // nullptr if no matches found
}
InstrumentTemplate* searchTemplateForMidiProgram(int midiProgram, const bool useDrumSet)
{
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
if (it->channel.empty() || it->useDrumset != useDrumSet) {
continue;
}
if (it->channel[0].program() == midiProgram) {
return it;
}
}
}
return 0;
}
InstrumentTemplate* guessTemplateByNameData(const QList<QString>& nameDataList)
{
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
for (const QString& name : nameDataList) {
if (name.contains(it->trackName, Qt::CaseInsensitive)
|| name.contains(it->longNames.value(0).name(), Qt::CaseInsensitive)
|| name.contains(it->shortNames.value(0).name(), Qt::CaseInsensitive)) {
return it;
}
}
}
}
for (const QString& name : nameDataList) {
if (name.contains("drum", Qt::CaseInsensitive)) {
return searchTemplate("drumset");
}
if (name.contains("piano", Qt::CaseInsensitive)) {
return searchTemplate("piano");
}
}
return nullptr;
}
//---------------------------------------------------------
// searchTemplateIndexForTrackName
//---------------------------------------------------------
InstrumentIndex searchTemplateIndexForTrackName(const QString& trackName)
{
int instIndex = 0;
int grpIndex = 0;
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
if (it->trackName == trackName) {
return InstrumentIndex(grpIndex, instIndex, it);
}
++instIndex;
}
++grpIndex;
}
return InstrumentIndex(-1, -1, nullptr);
}
//---------------------------------------------------------
// searchTemplateIndexForId
//---------------------------------------------------------
InstrumentIndex searchTemplateIndexForId(const QString& id)
{
int instIndex = 0;
int grpIndex = 0;
for (InstrumentGroup* g : instrumentGroups) {
for (InstrumentTemplate* it : g->instrumentTemplates) {
if (it->id == id) {
return InstrumentIndex(grpIndex, instIndex, it);
}
++instIndex;
}
++grpIndex;
}
return InstrumentIndex(-1, -1, nullptr);
}
//---------------------------------------------------------
// linkGenre
// link the current instrument template to the genre list specified by "genre"
// Each genre is a list of pointers to instrument templates
// The list of genres is at application level
//---------------------------------------------------------
void InstrumentTemplate::linkGenre(const QString& genre)
{
InstrumentGenre* ig = searchInstrumentGenre(genre);
if (ig) {
genres.append(ig);
}
}
//---------------------------------------------------------
// genreMember
// is this instrument template a member of the supplied genre
//---------------------------------------------------------
bool InstrumentTemplate::genreMember(const QString& name)
{
bool rVal=false;
foreach (InstrumentGenre* instrumentGenre, genres) {
if (instrumentGenre->id == name) {
rVal = true;
break;
}
}
return rVal;
}
void InstrumentGenre::write(XmlWriter& xml) const
{
xml.stag(QString("Genre id=\"%1\"").arg(id));
xml.tag("name", name);
xml.etag();
}
void InstrumentGenre::write1(XmlWriter& xml) const
{
write(xml);
}
void InstrumentGenre::read(XmlReader& e)
{
id = e.attribute("id");
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "name") {
name = qtrc("InstrumentsXML", e.readElementText().toUtf8().data());
} else {
e.unknown();
}
}
}
//---------------------------------------------------------
// familyMember
// is this instrument template a member of the supplied family
//---------------------------------------------------------
bool InstrumentTemplate::familyMember(const QString& name)
{
return family->id == name;
}
void InstrumentFamily::write(XmlWriter& xml) const
{
xml.stag(QString("Family id=\"%1\"").arg(id));
xml.tag("name", name);
xml.etag();
}
void InstrumentFamily::write1(XmlWriter& xml) const
{
write(xml);
}
void InstrumentFamily::read(XmlReader& e)
{
id = e.attribute("id");
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "name") {
name = qtrc("InstrumentsXML", e.readElementText().toUtf8().data());
} else {
e.unknown();
}
}
}
//---------------------------------------------------------
// clefType
//---------------------------------------------------------
ClefTypeList InstrumentTemplate::clefType(int staffIdx) const
{
if (staffIdx < staves) {
return clefTypes[staffIdx];
}
return clefTypes[0];
}
//---------------------------------------------------------
// defaultClef
// traverse the instrument list for first instrument
// with midi patch 'program'. Return the default clef
// for this instrument.
//---------------------------------------------------------
ClefType defaultClef(int program)
{
if (program >= 24 && program < 32) { // this are guitars
return ClefType::G8_VB;
} else if (program >= 32 && program < 40) { // this is bass
return ClefType::F8_VB;
}
for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
if (it->channel[0].bank() == 0 && it->channel[0].program() == program) {
return it->clefTypes[0]._concertClef;
}
}
}
return ClefType::G;
}
}