LCOV - code coverage report
Current view: top level - src/engraving/dom - instrtemplate.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 69.9 % 648 453
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include <limits>
      24              : #include "instrtemplate.h"
      25              : 
      26              : #include "io/file.h"
      27              : #include "containers.h"
      28              : #include "translation.h"
      29              : 
      30              : #include "rw/xmlreader.h"
      31              : #include "rw/xmlwriter.h"
      32              : #include "rw/write/twrite.h"
      33              : #include "rw/read400/tread.h"
      34              : #include "types/typesconv.h"
      35              : 
      36              : #include "bracket.h"
      37              : #include "drumset.h"
      38              : #include "scoreorder.h"
      39              : #include "stafftype.h"
      40              : #include "stringdata.h"
      41              : #include "stringutils.h"
      42              : #include "utils.h"
      43              : 
      44              : #include "log.h"
      45              : 
      46              : using namespace mu;
      47              : using namespace muse::io;
      48              : using namespace mu::engraving;
      49              : 
      50              : namespace mu::engraving {
      51              : std::vector<const InstrumentGroup*> instrumentGroups;
      52              : std::vector<const InstrumentGenre*> instrumentGenres;
      53              : std::vector<const InstrumentFamily*> instrumentFamilies;
      54              : std::vector<MidiArticulation> midiArticulations;            // global articulations
      55              : std::vector<ScoreOrder> instrumentOrders;
      56              : 
      57         1036 : InstrumentIndex::InstrumentIndex(int g, int i, const InstrumentTemplate* it)
      58         1036 :     : groupIndex{g}, instrIndex{i}, instrTemplate{it}
      59              : {
      60         1036 :     templateCount = 0;
      61        13468 :     for (const InstrumentGroup* ig : instrumentGroups) {
      62        12432 :         templateCount += ig->instrumentTemplates.size();
      63              :     }
      64         1036 : }
      65              : 
      66        13312 : const InstrumentGenre* searchInstrumentGenre(const String& id)
      67              : {
      68        80832 :     for (const InstrumentGenre* ig : instrumentGenres) {
      69        80656 :         if (ig->id == id) {
      70        13136 :             return ig;
      71              :         }
      72              :     }
      73          176 :     return nullptr;
      74              : }
      75              : 
      76        12224 : static const InstrumentFamily* searchInstrumentFamily(const String& name)
      77              : {
      78       742320 :     for (const InstrumentFamily* fam : instrumentFamilies) {
      79       740400 :         if (fam->id == name) {
      80        10304 :             return fam;
      81              :         }
      82              :     }
      83         1920 :     return nullptr;
      84              : }
      85              : 
      86          192 : static InstrumentGroup* searchInstrumentGroup(const String& id)
      87              : {
      88         1248 :     for (const InstrumentGroup* g : instrumentGroups) {
      89         1056 :         if (g->id == id) {
      90            0 :             return const_cast<InstrumentGroup*>(g);
      91              :         }
      92              :     }
      93          192 :     return nullptr;
      94              : }
      95              : 
      96          160 : static MidiArticulation searchArticulation(const String& name)
      97              : {
      98          880 :     for (MidiArticulation a : midiArticulations) {
      99          720 :         if (a.name == name) {
     100            0 :             return a;
     101              :         }
     102          720 :     }
     103          160 :     return MidiArticulation();
     104              : }
     105              : 
     106        25936 : static int readStaffIdx(XmlReader& e)
     107              : {
     108        25936 :     int idx = e.intAttribute("staff", 1) - 1;
     109        25936 :     if (idx >= MAX_STAVES) {
     110            0 :         idx = MAX_STAVES - 1;
     111              :     }
     112        25936 :     if (idx < 0) {
     113            0 :         idx = 0;
     114              :     }
     115        25936 :     return idx;
     116              : }
     117              : 
     118         1968 : static TraitType traitTypeFromString(const String& str)
     119              : {
     120              :     static const std::unordered_map<String, TraitType> types {
     121            0 :         { u"transposition", TraitType::Transposition },
     122            0 :         { u"tuning", TraitType::Tuning },
     123            0 :         { u"course", TraitType::Course }
     124         2048 :     };
     125              : 
     126         1968 :     return muse::value(types, str.toLower(), TraitType::Unknown);
     127           16 : }
     128              : 
     129              : //---------------------------------------------------------
     130              : //   read InstrumentGroup
     131              : //---------------------------------------------------------
     132              : 
     133          192 : void InstrumentGroup::read(XmlReader& e)
     134              : {
     135          192 :     id       = e.attribute("id");
     136          192 :     name     = muse::mtrc("engraving/instruments/group", e.attribute("name"));
     137          192 :     extended = e.intAttribute("extended", 0);
     138              : 
     139        10688 :     while (e.readNextStartElement()) {
     140        10496 :         const AsciiStringView tag(e.name());
     141        10496 :         if (tag == "instrument" || tag == "Instrument") {
     142        10304 :             String sid = e.attribute("id");
     143        10304 :             InstrumentTemplate* t = const_cast<InstrumentTemplate*>(searchTemplate(sid));
     144        10304 :             if (t == 0) {
     145        10304 :                 t = new InstrumentTemplate;
     146              :                 // init with global articulation
     147        10304 :                 t->midiArticulations.insert(t->midiArticulations.end(), midiArticulations.begin(), midiArticulations.end());
     148        10304 :                 t->sequenceOrder = static_cast<int>(instrumentTemplates.size());
     149        10304 :                 instrumentTemplates.push_back(t);
     150              :             }
     151        10304 :             t->read(e);
     152        10496 :         } else if (tag == "ref") {
     153            0 :             const InstrumentTemplate* ttt = searchTemplate(e.readText());
     154            0 :             if (ttt) {
     155            0 :                 InstrumentTemplate* t = new InstrumentTemplate(*ttt);
     156            0 :                 instrumentTemplates.push_back(t);
     157              :             } else {
     158            0 :                 LOGD("instrument reference not found <%s>", e.text().toUtf8().data());
     159              :             }
     160          192 :         } else if (tag == "name") {
     161          192 :             name = muse::mtrc("engraving/instruments/group", e.readAsciiText().ascii());
     162            0 :         } else if (tag == "extended") {
     163            0 :             extended = e.readInt();
     164              :         } else {
     165            0 :             e.unknown();
     166              :         }
     167              :     }
     168          192 :     if (id.isEmpty()) {
     169            0 :         id = name.toLower().replace(u" ", u"-");
     170              :     }
     171              : 
     172        10496 :     for (const InstrumentTemplate* templ : instrumentTemplates) {
     173        10304 :         const_cast<InstrumentTemplate*>(templ)->groupId = id;
     174              :     }
     175          192 : }
     176              : 
     177              : //---------------------------------------------------------
     178              : //   clear InstrumentGroup
     179              : //---------------------------------------------------------
     180              : 
     181            0 : void InstrumentGroup::clear()
     182              : {
     183            0 :     muse::DeleteAll(instrumentTemplates);
     184            0 :     instrumentTemplates.clear();
     185            0 : }
     186              : 
     187              : //---------------------------------------------------------
     188              : //   InstrumentTemplate
     189              : //---------------------------------------------------------
     190              : 
     191        51520 : InstrumentTemplate::InstrumentTemplate()
     192              : {
     193        10304 :     staffCount         = 1;
     194        10304 :     minPitchA          = MIN_PITCH;
     195        10304 :     maxPitchA          = MAX_PITCH;
     196        10304 :     minPitchP          = MIN_PITCH;
     197        10304 :     maxPitchP          = MAX_PITCH;
     198        10304 :     staffGroup         = StaffGroup::STANDARD;
     199        10304 :     staffTypePreset    = 0;
     200        10304 :     useDrumset         = false;
     201        10304 :     drumset            = 0;
     202        10304 :     extended           = false;
     203        10304 :     singleNoteDynamics = true;
     204        10304 :     family             = nullptr;
     205              : 
     206        51520 :     for (int i = 0; i < MAX_STAVES; ++i) {
     207        41216 :         clefTypes[i].concertClef = ClefType::G;
     208        41216 :         clefTypes[i].transposingClef = ClefType::G;
     209        41216 :         staffLines[i]  = 5;
     210        41216 :         smallStaff[i]  = false;
     211        41216 :         bracket[i]     = BracketType::NO_BRACKET;
     212        41216 :         bracketSpan[i] = 0;
     213              :     }
     214        10304 :     transpose.diatonic   = 0;
     215        10304 :     transpose.chromatic  = 0;
     216        10304 : }
     217              : 
     218            0 : InstrumentTemplate::InstrumentTemplate(const InstrumentTemplate& t)
     219              : {
     220            0 :     init(t);
     221            0 : }
     222              : 
     223              : //---------------------------------------------------------
     224              : //   init
     225              : //---------------------------------------------------------
     226              : 
     227          352 : void InstrumentTemplate::init(const InstrumentTemplate& t)
     228              : {
     229          352 :     id = t.id;
     230          352 :     soundId = t.soundId;
     231          352 :     trackName = t.trackName;
     232          352 :     instrumentName = t.instrumentName;
     233          352 :     description = t.description;
     234          352 :     musicXmlId = t.musicXmlId;
     235          352 :     staffCount = t.staffCount;
     236          352 :     extended = t.extended;
     237          352 :     minPitchA = t.minPitchA;
     238          352 :     maxPitchA = t.maxPitchA;
     239          352 :     minPitchP = t.minPitchP;
     240          352 :     maxPitchP = t.maxPitchP;
     241          352 :     transpose = t.transpose;
     242          352 :     staffGroup = t.staffGroup;
     243          352 :     staffTypePreset = t.staffTypePreset;
     244          352 :     useDrumset = t.useDrumset;
     245          352 :     drumset = t.drumset ? new Drumset(*t.drumset) : nullptr;
     246          352 :     stringData = t.stringData;
     247          352 :     midiActions = t.midiActions;
     248          352 :     genres = t.genres;
     249          352 :     channel = t.channel;
     250          352 :     family = t.family;
     251          352 :     singleNoteDynamics = t.singleNoteDynamics;
     252          352 :     sequenceOrder = t.sequenceOrder;
     253          352 :     trait = t.trait;
     254          352 :     groupId = t.groupId;
     255          352 :     glissandoStyle = t.glissandoStyle;
     256          352 :     barlineSpan = t.barlineSpan;
     257              : 
     258         1760 :     for (int i = 0; i < MAX_STAVES; ++i) {
     259         1408 :         clefTypes[i]   = t.clefTypes[i];
     260         1408 :         staffLines[i]  = t.staffLines[i];
     261         1408 :         smallStaff[i]  = t.smallStaff[i];
     262         1408 :         bracket[i]     = t.bracket[i];
     263         1408 :         bracketSpan[i] = t.bracketSpan[i];
     264              :     }
     265          352 : }
     266              : 
     267            0 : InstrumentTemplate::~InstrumentTemplate()
     268              : {
     269            0 :     delete drumset;
     270            0 :     drumset = nullptr;
     271            0 : }
     272              : 
     273            0 : InstrumentTemplate& InstrumentTemplate::operator=(const InstrumentTemplate& templ)
     274              : {
     275            0 :     init(templ);
     276            0 :     return *this;
     277              : }
     278              : 
     279            0 : bool InstrumentTemplate::isValid() const
     280              : {
     281            0 :     return !id.empty();
     282              : }
     283              : 
     284              : //---------------------------------------------------------
     285              : //   write
     286              : //---------------------------------------------------------
     287              : 
     288            0 : void InstrumentTemplate::write(XmlWriter& xml) const
     289              : {
     290            0 :     xml.startElement("Instrument",  { { "id", id } });
     291              : 
     292            0 :     if (!soundId.empty()) {
     293            0 :         xml.tag("soundId", soundId);
     294              :     }
     295              : 
     296            0 :     write::TWrite::write(instrumentName, xml);
     297              : 
     298            0 :     if (!instrumentName.longName().empty()) {
     299            0 :         xml.tag("trackName", instrumentName.longName());
     300              :     }
     301            0 :     xml.tag("description", description);
     302            0 :     xml.tag("musicXMLid", musicXmlId);
     303            0 :     if (extended) {
     304            0 :         xml.tag("extended", extended);
     305              :     }
     306            0 :     write::TWrite::write(&stringData, xml);
     307            0 :     if (staffCount > 1) {
     308            0 :         xml.tag("staves", static_cast<int>(staffCount));
     309              :     }
     310            0 :     for (staff_idx_t i = 0; i < staffCount; ++i) {
     311            0 :         if (clefTypes[i].concertClef == clefTypes[i].transposingClef) {
     312            0 :             if (i) {
     313            0 :                 xml.tag("clef", { { "staff", i + 1 } }, TConv::toXml(clefTypes[i].concertClef));
     314              :             } else {
     315            0 :                 xml.tag("clef", TConv::toXml(clefTypes[i].concertClef));
     316              :             }
     317              :         } else {
     318            0 :             if (i) {
     319            0 :                 xml.tag("concertClef", { { "staff", i + 1 } }, TConv::toXml(clefTypes[i].concertClef));
     320            0 :                 xml.tag("transposingClef", { { "staff", i + 1 } }, TConv::toXml(clefTypes[i].transposingClef));
     321              :             } else {
     322            0 :                 xml.tag("concertClef", TConv::toXml(clefTypes[i].concertClef));
     323            0 :                 xml.tag("transposingClef", TConv::toXml(clefTypes[i].transposingClef));
     324              :             }
     325              :         }
     326            0 :         if (staffLines[i] != 5) {
     327            0 :             if (i) {
     328            0 :                 xml.tag("stafflines", { { "staff", i + 1 } }, staffLines[i]);
     329              :             } else {
     330            0 :                 xml.tag("stafflines", staffLines[i]);
     331              :             }
     332              :         }
     333            0 :         if (smallStaff[i]) {
     334            0 :             if (i) {
     335            0 :                 xml.tag("smallStaff", { { "staff", i + 1 } }, smallStaff[i]);
     336              :             } else {
     337            0 :                 xml.tag("smallStaff", smallStaff[i]);
     338              :             }
     339              :         }
     340              : 
     341            0 :         if (bracket[i] != BracketType::NO_BRACKET) {
     342            0 :             if (i) {
     343            0 :                 xml.tag("bracket", { { "staff", i + 1 } }, int(bracket[i]));
     344              :             } else {
     345            0 :                 xml.tag("bracket", int(bracket[i]));
     346              :             }
     347              :         }
     348            0 :         if (bracketSpan[i] != 0) {
     349            0 :             if (i) {
     350            0 :                 xml.tag("bracketSpan", { { "staff", i + 1 } }, bracketSpan[i]);
     351              :             } else {
     352            0 :                 xml.tag("bracketSpan", bracketSpan[i]);
     353              :             }
     354              :         }
     355            0 :         if (barlineSpan[i]) {
     356            0 :             if (i > 0) {
     357            0 :                 xml.tag("barlineSpan", { { "staff", i + 1 } }, int { barlineSpan[i] });
     358              :             } else {
     359            0 :                 xml.tag("barlineSpan", int { barlineSpan[i] });
     360              :             }
     361              :         }
     362              :     }
     363            0 :     if (minPitchA != MIN_PITCH || maxPitchA != MAX_PITCH) {
     364            0 :         xml.tag("aPitchRange", String(u"%1-%2").arg(minPitchA).arg(maxPitchA));
     365              :     }
     366            0 :     if (minPitchP != MIN_PITCH || maxPitchP != MAX_PITCH) {
     367            0 :         xml.tag("pPitchRange", String(u"%1-%2").arg(minPitchP).arg(maxPitchP));
     368              :     }
     369            0 :     if (transpose.diatonic) {
     370            0 :         xml.tag("transposeDiatonic", transpose.diatonic);
     371              :     }
     372            0 :     if (transpose.chromatic) {
     373            0 :         xml.tag("transposeChromatic", transpose.chromatic);
     374              :     }
     375            0 :     if (useDrumset) {
     376            0 :         xml.tag("drumset", int(useDrumset));
     377              :     }
     378            0 :     if (drumset) {
     379            0 :         drumset->save(xml);
     380              :     }
     381              : 
     382            0 :     if (!singleNoteDynamics) {      // default is true
     383            0 :         xml.tag("singleNoteDynamics", singleNoteDynamics);
     384              :     }
     385              : 
     386            0 :     xml.tag("glissandoStyle", TConv::toXml(glissandoStyle));
     387              : 
     388            0 :     for (const NamedEventList& a : midiActions) {
     389            0 :         write::TWrite::write(&a, xml, "MidiAction");
     390              :     }
     391            0 :     for (const InstrChannel& a : channel) {
     392            0 :         write::TWrite::write(&a, xml, nullptr);
     393              :     }
     394            0 :     for (const MidiArticulation& ma : midiArticulations) {
     395            0 :         bool isGlobal = false;
     396            0 :         for (const MidiArticulation& ga : mu::engraving::midiArticulations) {
     397            0 :             if (ma == ga) {
     398            0 :                 isGlobal = true;
     399            0 :                 break;
     400              :             }
     401              :         }
     402            0 :         if (!isGlobal) {
     403            0 :             write::TWrite::write(&ma, xml);
     404              :         }
     405              :     }
     406            0 :     if (family) {
     407            0 :         xml.tag("family", family->id);
     408              :     }
     409              : 
     410            0 :     xml.endElement();
     411            0 : }
     412              : 
     413              : //---------------------------------------------------------
     414              : //   read
     415              : //---------------------------------------------------------
     416              : 
     417        41504 : String translateInstrumentName(const String& instrumentId, const String& nameType, const String& text)
     418              : {
     419        41504 :     String disambiguation = instrumentId + u' ' + nameType;
     420        83008 :     return muse::mtrc("engraving/instruments", text, disambiguation);
     421        41504 : }
     422              : 
     423        10304 : void InstrumentTemplate::read(XmlReader& e)
     424              : {
     425        10304 :     id = e.attribute("id");
     426              : 
     427       159808 :     while (e.readNextStartElement()) {
     428       149504 :         const AsciiStringView tag(e.name());
     429              : 
     430       149504 :         if (tag == "soundId") {
     431            0 :             soundId = e.readText();
     432       149504 :         } else if (tag == "longName" || tag == "name") {                   // "name" is obsolete
     433         9856 :             int pos = e.intAttribute("pos", 0);
     434              :             UNUSED(pos);
     435         9856 :             instrumentName.setLongName(translateInstrumentName(id, u"longName", e.readText()));
     436       139648 :         } else if (tag == "shortName" || tag == "short-name") {     // "short-name" is obsolete
     437         9520 :             int pos = e.intAttribute("pos", 0);
     438              :             UNUSED(pos);
     439         9520 :             instrumentName.setShortName(translateInstrumentName(id, u"shortName", e.readText()));
     440       130128 :         } else if (tag == "trackName") {
     441         9856 :             trackName = translateInstrumentName(id, u"trackName", e.readText());
     442       120272 :         } else if (tag == "description") {
     443        10304 :             description = translateInstrumentName(id, u"description", e.readText());
     444       109968 :         } else if (tag == "extended") {
     445            0 :             extended = e.readInt();
     446       109968 :         } else if (tag == "staves") {
     447         1104 :             staffCount = e.readInt();
     448         1104 :             bracketSpan[0] = static_cast<int>(staffCount);
     449              : //                  for (int i = 0; i < staves-1; ++i)
     450              : //                        barlineSpan[i] = true;
     451       108864 :         } else if (tag == "clef") {             // sets both transposing and concert clef
     452        10000 :             int idx = readStaffIdx(e);
     453        10000 :             ClefType ct = TConv::fromXml(e.readAsciiText(), ClefType::G);
     454        10000 :             clefTypes[idx].concertClef = ct;
     455        10000 :             clefTypes[idx].transposingClef = ct;
     456        98864 :         } else if (tag == "concertClef") {
     457         1072 :             int idx = readStaffIdx(e);
     458         1072 :             clefTypes[idx].concertClef = TConv::fromXml(e.readAsciiText(), ClefType::G);
     459        97792 :         } else if (tag == "transposingClef") {
     460         1072 :             int idx = readStaffIdx(e);
     461         1072 :             clefTypes[idx].transposingClef = TConv::fromXml(e.readAsciiText(), ClefType::G);
     462        96720 :         } else if (tag == "stafflines") {
     463            0 :             int idx = readStaffIdx(e);
     464            0 :             staffLines[idx] = e.readInt();
     465        96720 :         } else if (tag == "smallStaff") {
     466            0 :             int idx = readStaffIdx(e);
     467            0 :             smallStaff[idx] = e.readInt();
     468        96720 :         } else if (tag == "bracket") {
     469         1040 :             int idx = readStaffIdx(e);
     470         1040 :             bracket[idx] = BracketType(e.readInt());
     471        95680 :         } else if (tag == "bracketSpan") {
     472         1040 :             int idx = readStaffIdx(e);
     473         1040 :             bracketSpan[idx] = e.readInt();
     474        94640 :         } else if (tag == "barlineSpan") {
     475         9632 :             int idx = readStaffIdx(e);
     476         9632 :             int span = e.readInt();
     477        10672 :             for (int i = 0; i < span - 1; ++i) {
     478         1040 :                 barlineSpan[idx + i] = true;
     479              :             }
     480        85008 :         } else if (tag == "aPitchRange") {
     481         8048 :             setPitchRange(e.readText(), minPitchA, maxPitchA);
     482        76960 :         } else if (tag == "pPitchRange") {
     483         8048 :             setPitchRange(e.readText(), minPitchP, maxPitchP);
     484        68912 :         } else if (tag == "transposition") {      // obsolete
     485            0 :             int i = e.readInt();
     486            0 :             transpose.chromatic = i;
     487            0 :             transpose.diatonic = Interval::chromatic2diatonic(i);
     488        68912 :         } else if (tag == "transposeChromatic") {
     489         2432 :             transpose.chromatic = e.readInt();
     490        66480 :         } else if (tag == "transposeDiatonic") {
     491         2432 :             transpose.diatonic = e.readInt();
     492        64048 :         } else if (tag == "traitName") {
     493         1968 :             trait.type = traitTypeFromString(e.attribute("type"));
     494         1968 :             String traitName = translateInstrumentName(id, u"traitName", e.readText());
     495         1968 :             trait.isDefault = traitName.contains(u'*');
     496         1968 :             trait.isHiddenOnScore = traitName.contains(u'(') && traitName.contains(u')');
     497         1968 :             trait.name = traitName.remove(u'*').remove(u'(').remove(u')');
     498        64048 :         } else if (tag == "StringData") {
     499         1216 :             read400::TRead::read(&stringData, e);
     500        60864 :         } else if (tag == "drumset") {
     501         1728 :             useDrumset = e.readInt();
     502        59136 :         } else if (tag == "Drum") {
     503              :             // if we see one of these tags, a custom drumset will be created
     504         4592 :             if (!drumset) {
     505         1712 :                 drumset = new Drumset(*smDrumset);
     506         1712 :                 drumset->clear();
     507              :             }
     508         4592 :             drumset->loadDrum(e);
     509        54544 :         } else if (tag == "percussionPanelColumns") {
     510              :             // if we see one of these tags, a custom drumset will be created
     511            0 :             if (!drumset) {
     512            0 :                 drumset = new Drumset(*smDrumset);
     513            0 :                 drumset->clear();
     514              :             }
     515            0 :             drumset->setPercussionPanelColumns(e.readInt());
     516        54544 :         } else if (tag == "MidiAction") {
     517            0 :             NamedEventList a;
     518            0 :             read400::TRead::read(&a, e);
     519            0 :             midiActions.push_back(a);
     520        54544 :         } else if (tag == "Channel" || tag == "channel") {
     521        12416 :             InstrChannel a;
     522        12416 :             InstrumentTrackId tId;
     523        12416 :             read400::ReadContext rctx(nullptr);
     524        12416 :             read400::TRead::read(&a, e, rctx, nullptr, tId);
     525        12416 :             channel.push_back(a);
     526        54544 :         } else if (tag == "Articulation") {
     527          304 :             MidiArticulation a;
     528          304 :             read400::TRead::read(&a, e);
     529          304 :             size_t n = midiArticulations.size();
     530              :             size_t i;
     531         1008 :             for (i = 0; i < n; ++i) {
     532         1008 :                 if (midiArticulations[i].name == a.name) {
     533          304 :                     midiArticulations[i] = a;
     534          304 :                     break;
     535              :                 }
     536              :             }
     537          304 :             if (i == n) {
     538            0 :                 midiArticulations.push_back(a);
     539              :             }
     540        42128 :         } else if (tag == "stafftype") {
     541         2080 :             int staffIdx = readStaffIdx(e);
     542         2080 :             String xmlPresetName = e.attribute("staffTypePreset");
     543         2080 :             String stfGroup = e.readText();
     544         2080 :             if (stfGroup == "percussion") {
     545         1728 :                 staffGroup = StaffGroup::PERCUSSION;
     546          352 :             } else if (stfGroup == "tablature") {
     547          352 :                 staffGroup = StaffGroup::TAB;
     548              :             } else {
     549            0 :                 staffGroup = StaffGroup::STANDARD;
     550              :             }
     551         2080 :             staffTypePreset = 0;
     552         2080 :             if (!xmlPresetName.isEmpty()) {
     553         2080 :                 staffTypePreset = StaffType::presetFromXmlName(xmlPresetName);
     554              :             }
     555         2080 :             if (!staffTypePreset || staffTypePreset->group() != staffGroup) {
     556            0 :                 staffTypePreset = StaffType::getDefaultPreset(staffGroup);
     557              :             }
     558         2080 :             if (staffTypePreset) {
     559         2080 :                 staffLines[staffIdx] = staffTypePreset->lines();
     560              :             }
     561        41824 :         } else if (tag == "init") {
     562          368 :             String val(e.readText());
     563          368 :             const InstrumentTemplate* ttt = searchTemplate(val);
     564          368 :             if (ttt) {
     565          352 :                 String id_ = id;
     566          352 :                 init(*ttt);
     567          352 :                 id = id_;
     568          352 :             } else {
     569           16 :                 LOGD("InstrumentTemplate:: init instrument <%s> not found", muPrintable(val));
     570              :             }
     571        39744 :         } else if (tag == "musicXMLid") {
     572         9760 :             musicXmlId = e.readText();
     573        29616 :         } else if (tag == "family") {
     574        10304 :             family = searchInstrumentFamily(e.readText());
     575        19312 :         } else if (tag == "genre") {
     576        13136 :             String val(e.readText());
     577        13136 :             linkGenre(val);
     578        19312 :         } else if (tag == "singleNoteDynamics") {
     579         3712 :             singleNoteDynamics = e.readBool();
     580         2464 :         } else if (tag == "glissandoStyle") {
     581         2464 :             glissandoStyle = TConv::fromXml(e.readAsciiText(), GlissandoStyle::CHROMATIC);
     582              :         } else {
     583            0 :             e.unknown();
     584              :         }
     585              :     }
     586        10304 :     if (channel.empty()) {
     587           16 :         InstrChannel a;
     588           16 :         a.setChorus(0);
     589           16 :         a.setReverb(0);
     590           16 :         a.setName(String::fromUtf8(InstrChannel::DEFAULT_NAME));
     591           16 :         a.setProgram(0);
     592           16 :         a.setBank(0);
     593           16 :         a.setVolume(90);
     594           16 :         a.setPan(0);
     595           16 :         channel.push_back(a);
     596           16 :     }
     597              : 
     598        10304 :     if (trackName.isEmpty() && !instrumentName.longName().isEmpty()) {
     599            0 :         trackName = instrumentName.longName();
     600              :     }
     601        10304 :     if (description.isEmpty() && !instrumentName.shortName().isEmpty()) {
     602            0 :         description = instrumentName.shortName();
     603              :     }
     604        10304 :     if (id.empty()) {
     605            0 :         id = trackName.toLower().replace(u' ', u'-');
     606              :     }
     607              : 
     608        10304 :     if (staffCount == 0) {
     609            0 :         LOGD(" 2Instrument: staves == 0 <%s>", muPrintable(id));
     610              :     }
     611        10304 : }
     612              : 
     613              : //---------------------------------------------------------
     614              : //   setPitchRange
     615              : //---------------------------------------------------------
     616              : 
     617        16096 : void InstrumentTemplate::setPitchRange(const String& s, int& a, int& b) const
     618              : {
     619        16096 :     StringList sl = s.split(u'-');
     620        16096 :     if (sl.size() != 2) {
     621            0 :         a = MIN_PITCH;
     622            0 :         b = MAX_PITCH;
     623            0 :         return;
     624              :     }
     625        16096 :     a = sl.at(0).toInt();
     626        16096 :     b = sl.at(1).toInt();
     627        16096 : }
     628              : 
     629              : //---------------------------------------------------------
     630              : //   clearInstrumentTemplates
     631              : //---------------------------------------------------------
     632              : 
     633            0 : void clearInstrumentTemplates()
     634              : {
     635            0 :     for (const InstrumentGroup* g : instrumentGroups) {
     636            0 :         const_cast<InstrumentGroup*>(g)->clear();
     637              :     }
     638            0 :     muse::DeleteAll(instrumentGroups);
     639            0 :     instrumentGroups.clear();
     640            0 :     muse::DeleteAll(instrumentGenres);
     641            0 :     instrumentGenres.clear();
     642            0 :     muse::DeleteAll(instrumentFamilies);
     643            0 :     instrumentFamilies.clear();
     644            0 :     midiArticulations.clear();
     645            0 :     instrumentOrders.clear();
     646            0 : }
     647              : 
     648              : //---------------------------------------------------------
     649              : //   loadInstrumentTemplates
     650              : //---------------------------------------------------------
     651              : 
     652           16 : bool loadInstrumentTemplates(const muse::io::path_t& instrTemplatesPath)
     653              : {
     654           16 :     File qf(instrTemplatesPath);
     655           16 :     if (!qf.open(IODevice::ReadOnly)) {
     656            0 :         LOGE() << "Could not load instrument templates from " << instrTemplatesPath;
     657            0 :         return false;
     658              :     }
     659              : 
     660           16 :     XmlReader e(&qf);
     661           48 :     while (e.readNextStartElement()) {
     662           16 :         if (e.name() == "museScore") {
     663         2464 :             while (e.readNextStartElement()) {
     664         2448 :                 const AsciiStringView tag(e.name());
     665         2448 :                 if (tag == "instrument-group" || tag == "InstrumentGroup") {
     666          192 :                     String idGroup(e.attribute("id"));
     667          192 :                     InstrumentGroup* group = searchInstrumentGroup(idGroup);
     668          192 :                     if (group == 0) {
     669          192 :                         group = new InstrumentGroup;
     670          192 :                         instrumentGroups.push_back(group);
     671              :                     }
     672          192 :                     group->read(e);
     673         2448 :                 } else if (tag == "Articulation") {
     674              :                     // read global articulation
     675          160 :                     String name(e.attribute("name"));
     676          160 :                     MidiArticulation a = searchArticulation(name);
     677          160 :                     read400::TRead::read(&a, e);
     678          160 :                     midiArticulations.push_back(a);
     679         2256 :                 } else if (tag == "Genre") {
     680          176 :                     String idGenre(e.attribute("id"));
     681          176 :                     InstrumentGenre* genre = const_cast<InstrumentGenre*>(searchInstrumentGenre(idGenre));
     682          176 :                     if (!genre) {
     683          176 :                         genre = new InstrumentGenre;
     684          176 :                         instrumentGenres.push_back(genre);
     685              :                     }
     686          176 :                     genre->read(e);
     687         2096 :                 } else if (tag == "Family") {
     688         1920 :                     String idFamily(e.attribute("id"));
     689         1920 :                     InstrumentFamily* fam = const_cast<InstrumentFamily*>(searchInstrumentFamily(idFamily));
     690         1920 :                     if (!fam) {
     691         1920 :                         fam = new InstrumentFamily;
     692         1920 :                         instrumentFamilies.push_back(fam);
     693              :                     }
     694         1920 :                     fam->read(e);
     695         1920 :                 } else if (tag == "Order") {
     696            0 :                     ScoreOrder order;
     697            0 :                     order.read(e);
     698            0 :                     instrumentOrders.push_back(order);
     699            0 :                 } else {
     700            0 :                     e.unknown();
     701              :                 }
     702              :             }
     703              :         }
     704              :     }
     705              : 
     706           16 :     return true;
     707           16 : }
     708              : 
     709        14653 : const InstrumentTemplate* searchTemplate(const String& name)
     710              : {
     711        97563 :     for (const InstrumentGroup* g : instrumentGroups) {
     712      5321841 :         for (const InstrumentTemplate* it : g->instrumentTemplates) {
     713      5238931 :             if (it->id == name) {
     714         3491 :                 return it;
     715              :             }
     716              :         }
     717              :     }
     718        11162 :     return 0;
     719              : }
     720              : 
     721              : // Given an unknown instrument (i.e. one that lacks a valid MuseScore ID),
     722              : // compare it to each of our built-in instruments and return the strongest
     723              : // available match, or `nullptr` if no match is found.
     724              : //
     725              : // Match strength is determined according to multiple factors (see code). Feel
     726              : // free to add more factors to this function. Please don't create new functions
     727              : // that only consider a subset of factors. You can disable matching against a
     728              : // specific factor by setting it to its default value in the input instrument.
     729              : //
     730              : // Use this to recognize instruments in pre-3.6 score files, and in non-native
     731              : // formats like MIDI and MusicXML (e.g. see importmusicxmlpass2.cpp). This
     732              : // ensures the correct sound and engraving properties are loaded.
     733              : 
     734          478 : const InstrumentTemplate* combinedTemplateSearch(const Instrument& instrument)
     735              : {
     736          478 :     const InstrChannel* const channel = instrument.channel(0);
     737          478 :     const int bank = channel->bank();
     738          478 :     const int program = channel->program();
     739          478 :     const int transposeChromatic = instrument.transpose().chromatic;
     740          478 :     const int transposeKey = (transposeChromatic % 12) + (transposeChromatic < 0 ? 12 : 0); // 0=C, 1=C#/Db, 2=D...
     741          478 :     const String& trackName = instrument.trackName();
     742          478 :     const String& traitName = instrument.trait().name;
     743          478 :     String musicXmlId = instrument.musicXmlId();
     744              : 
     745          478 :     if (musicXmlId.isEmpty() && trackName.isEmpty() && bank == 0 && program == -1) {
     746            6 :         return nullptr; // Not enough information to find a matching template.
     747              :     }
     748              : 
     749          472 :     if (musicXmlId.isEmpty()) {
     750          378 :         if (trackName.contains(u"drum", muse::CaseInsensitive)) {
     751            6 :             musicXmlId = u"drum.group.set";
     752          372 :         } else if (trackName == u"MusicXML Part" || trackName.contains(u"piano", muse::CaseInsensitive)) {
     753          106 :             musicXmlId = u"keyboard.piano";
     754              :         }
     755              :     } else {
     756              :         // Workaround for old generic instrument templates.
     757          186 :         if ((musicXmlId == u"wind.reed.clarinet" || musicXmlId == u"brass.trumpet")
     758          186 :             && (traitName == u"B♭" || transposeKey == 10)
     759              :             ) {
     760            2 :             musicXmlId.append(u".bflat");
     761              :         }
     762              :     }
     763              : 
     764              :     // Perform a weighted search over instrument names. Current limitations:
     765              :     // * We only check in the Preferences language. (Ideally we'd check English, German & Italian.)
     766              :     // * We don't consider aliases (e.g. we look for "Violoncello" but not "Double Bass").
     767              :     static constexpr int TRACK_NAME_WEIGHT = 128;
     768              :     static constexpr int LONG_NAME_WEIGHT = 64;
     769              :     static constexpr int SHORT_NAME_WEIGHT = 16; // Not standardized outside MuseScore.
     770              :     static constexpr int TRAIT_NAME_WEIGHT = 4; // More reliable than transposition interval.
     771              : 
     772              :     // Also search over these parameters, which don't depend on the language.
     773              :     static constexpr int MUSICXML_ID_WEIGHT = 32; // Not unique. Newer, more specialist IDs aren't in old files.
     774              :     static constexpr int MIDI_WEIGHT = 8; // Standardization limited to General MIDI Level 2.
     775              :     static constexpr int TRANSPOSITION_KEY_WEIGHT = 2; // Unreliable (e.g. in concert-pitch MusicXML).
     776              :     static constexpr int TRANSPOSITION_OCTAVE_WEIGHT = 1;
     777              : 
     778              :     // Optimize performance: Ignore low-strength matches.
     779              :     static constexpr int MIN_MATCH_STRENGTH = MIDI_WEIGHT;
     780              : 
     781              :     // Also consider name almost-matches (i.e. fuzzy text search).
     782              :     static constexpr size_t INITIAL_LEVENSHTEIN_DISTANCE = std::numeric_limits<size_t>::max();
     783              : 
     784          472 :     const InstrumentTemplate* bestMatch = nullptr;
     785          472 :     int bestMatchStrength = MIN_MATCH_STRENGTH;
     786          472 :     auto bestMatchLevDist = INITIAL_LEVENSHTEIN_DISTANCE;
     787              : 
     788         6136 :     for (const InstrumentGroup* group : instrumentGroups) {
     789       309632 :         for (const InstrumentTemplate* templ : group->instrumentTemplates) {
     790       303968 :             if (templ->trait.name == u"[hide]") {
     791       301744 :                 continue;
     792              :             }
     793              : 
     794       303968 :             int matchStrength = 0;
     795              : 
     796       303968 :             if (!trackName.isEmpty() && trackName == templ->trackName) {
     797          492 :                 matchStrength += TRACK_NAME_WEIGHT;
     798              :             }
     799              : 
     800       303968 :             if (!instrument.longName().empty() && instrument.longName() == templ->instrumentName.longName()) {
     801          605 :                 matchStrength += LONG_NAME_WEIGHT;
     802              :             }
     803              : 
     804       303968 :             if (!instrument.shortName().empty() && instrument.shortName() == templ->instrumentName.shortName()) {
     805            3 :                 matchStrength += SHORT_NAME_WEIGHT;
     806              :             }
     807              : 
     808       303968 :             if (!traitName.isEmpty() && traitName == templ->trait.name) {
     809          461 :                 matchStrength += TRAIT_NAME_WEIGHT;
     810              :             }
     811              : 
     812       303968 :             if (!musicXmlId.empty() && musicXmlId == templ->musicXmlId) {
     813          349 :                 matchStrength += MUSICXML_ID_WEIGHT;
     814              :             }
     815              : 
     816       685591 :             for (const InstrChannel& templChannel : templ->channel) {
     817       385831 :                 if (bank == templChannel.bank() && program == templChannel.program()) {
     818         4208 :                     matchStrength += MIDI_WEIGHT;
     819         4208 :                     break;
     820              :                 }
     821              :             }
     822              : 
     823       303968 :             const int chromaticDiff = std::abs(transposeChromatic - templ->transpose.chromatic);
     824              : 
     825       303968 :             if (chromaticDiff % 12 == 0) { // Same key.
     826       239064 :                 matchStrength += TRANSPOSITION_KEY_WEIGHT;
     827              :             }
     828              : 
     829       303968 :             if (chromaticDiff < 12) { // Same octave.
     830       263624 :                 matchStrength += TRANSPOSITION_OCTAVE_WEIGHT;
     831              :             }
     832              : 
     833       303968 :             if (matchStrength < bestMatchStrength) {
     834       301744 :                 continue; // Keep looking.
     835              :             }
     836              : 
     837         2224 :             if (matchStrength > bestMatchStrength) {
     838          915 :                 bestMatch = templ;
     839          915 :                 bestMatchStrength = matchStrength;
     840          915 :                 bestMatchLevDist = INITIAL_LEVENSHTEIN_DISTANCE; // Reset.
     841              :             }
     842              : 
     843              :             // Find smallest lev. dist. among templates with match strength equal to best.
     844         2224 :             auto levDist = INITIAL_LEVENSHTEIN_DISTANCE;
     845              : 
     846         2224 :             if (!trackName.isEmpty()) {
     847         2113 :                 levDist = muse::strings::levenshteinDistance(
     848         4226 :                     trackName.toStdString(),
     849         4226 :                     templ->trackName.toStdString()
     850              :                     );
     851              :             }
     852              : 
     853              :             levDist
     854         4448 :                 = std::min(levDist,
     855         2224 :                            muse::strings::levenshteinDistance(instrument.longName().toStdString(),
     856         4448 :                                                               templ->instrumentName.longName().toStdString()));
     857              : 
     858         2224 :             if (levDist < bestMatchLevDist) {
     859         1146 :                 bestMatch = templ;
     860         1146 :                 bestMatchLevDist = levDist;
     861              :             }
     862              :         }
     863              :     }
     864              : 
     865          472 :     return bestMatch;
     866          478 : }
     867              : 
     868            0 : const InstrumentTemplate* searchTemplateForMusicXmlId(const String& mxmlId)
     869              : {
     870            0 :     for (const InstrumentGroup* g : instrumentGroups) {
     871            0 :         for (const InstrumentTemplate* it : g->instrumentTemplates) {
     872            0 :             if (it->musicXmlId == mxmlId) {
     873            0 :                 return it;
     874              :             }
     875              :         }
     876              :     }
     877            0 :     return 0;
     878              : }
     879              : 
     880          952 : const InstrumentTemplate* searchTemplateForInstrNameList(const std::vector<String>& nameList, bool useDrumset, bool caseSensitive)
     881              : {
     882          952 :     const InstrumentTemplate* bestMatch = nullptr; // default if no matches
     883          952 :     int bestMatchStrength = 0; // higher for better matches
     884              : 
     885         3734 :     for (const String& name : nameList) {
     886         2782 :         if (name.isEmpty()) {
     887         1014 :             continue;
     888              :         }
     889              : 
     890              :         auto stringEqualsName = caseSensitive
     891       921452 :                                 ? std::function([&name](const String& s) { return s == name; })
     892         5764 :                                 : std::function([&name](const String& s) { return s.isEqualIgnoreCase(name); });
     893              : 
     894              :         auto staffNameEqualsName = caseSensitive
     895      1841136 :                                    ? std::function([&name](const String& sn) { return sn == name; })
     896         9760 :                                    : std::function([&name](const String& sn) { return sn.isEqualIgnoreCase(name); });
     897              : 
     898        22984 :         for (const InstrumentGroup* g : instrumentGroups) {
     899      1159808 :             for (const InstrumentTemplate* it : g->instrumentTemplates) {
     900      1138592 :                 if (it->useDrumset != useDrumset) {
     901       214912 :                     continue;
     902              :                 }
     903              : 
     904              :                 int matchStrength
     905              :                     = 0
     906       923680 :                       + (4 * (stringEqualsName(it->trackName) ? 1 : 0)) // most weight to track name since there are fewer duplicates
     907       923680 :                       + (2 * (staffNameEqualsName(it->instrumentName.longName()) ? 1 : 0))
     908       923680 :                       + (1 * (staffNameEqualsName(it->instrumentName.shortName()) ? 1 : 0)); // least weight to short name
     909       923680 :                 const int perfectMatchStrength = 7;
     910       923680 :                 assert(matchStrength <= perfectMatchStrength);
     911       923680 :                 if (matchStrength > bestMatchStrength) {
     912          559 :                     bestMatch = it;
     913          559 :                     bestMatchStrength = matchStrength;
     914          559 :                     if (bestMatchStrength == perfectMatchStrength) {
     915            0 :                         break; // stop looking for matches
     916              :                     }
     917              :                 }
     918              :             }
     919              :         }
     920         1768 :     }
     921              : 
     922          952 :     if (!bestMatch) {
     923          393 :         static const std::wregex drumsetRegex(L"drum ?(set|kit)", std::regex_constants::icase);
     924              : 
     925         1471 :         for (const String& name : nameList) {
     926         1092 :             if (name.contains(drumsetRegex)) {
     927            3 :                 return searchTemplate(u"drumset"); // Large Drum Kit
     928              :             }
     929              : 
     930         1089 :             if (name.contains(u"drum", muse::CaseInsensitive) || name.contains(u"percussion", muse::CaseInsensitive)) {
     931            4 :                 return searchTemplate(u"percussion-synthesizer"); // General MIDI percussion
     932              :             }
     933              : 
     934         1085 :             if (name.contains(u"piano", muse::CaseInsensitive)) {
     935            7 :                 return searchTemplate(u"piano");
     936              :             }
     937              :         }
     938              :     }
     939              : 
     940          938 :     return bestMatch; // nullptr if no matches found
     941              : }
     942              : 
     943          344 : const InstrumentTemplate* searchTemplateForMidiProgram(int bank, int program, bool useDrumset)
     944              : {
     945         3759 :     for (const InstrumentGroup* g : instrumentGroups) {
     946       186016 :         for (const InstrumentTemplate* it : g->instrumentTemplates) {
     947       182601 :             if (it->useDrumset != useDrumset) {
     948        36612 :                 continue;
     949              :             }
     950              : 
     951       321654 :             for (const InstrChannel& channel : it->channel) {
     952       175986 :                 if (channel.bank() == bank && channel.program() == program) {
     953          321 :                     return it;
     954              :                 }
     955              :             }
     956              :         }
     957              :     }
     958              : 
     959           23 :     return nullptr;
     960              : }
     961              : 
     962            0 : void addTemplateToGroup(const InstrumentTemplate* templ, const String& groupId)
     963              : {
     964            0 :     IF_ASSERT_FAILED(templ) {
     965            0 :         return;
     966              :     }
     967              : 
     968            0 :     InstrumentGroup* group = searchInstrumentGroup(groupId);
     969            0 :     if (group) {
     970            0 :         group->instrumentTemplates.push_back(templ);
     971              :     }
     972              : }
     973              : 
     974              : //---------------------------------------------------------
     975              : //   searchTemplateIndexForTrackName
     976              : //---------------------------------------------------------
     977              : 
     978            0 : InstrumentIndex searchTemplateIndexForTrackName(const String& trackName)
     979              : {
     980            0 :     int instIndex = 0;
     981            0 :     int grpIndex = 0;
     982            0 :     for (const InstrumentGroup* g : instrumentGroups) {
     983            0 :         for (const InstrumentTemplate* it : g->instrumentTemplates) {
     984            0 :             if (it->trackName == trackName) {
     985            0 :                 return InstrumentIndex(grpIndex, instIndex, it);
     986              :             }
     987            0 :             ++instIndex;
     988              :         }
     989            0 :         ++grpIndex;
     990              :     }
     991            0 :     return InstrumentIndex(-1, -1, nullptr);
     992              : }
     993              : 
     994              : //---------------------------------------------------------
     995              : //   searchTemplateIndexForId
     996              : //---------------------------------------------------------
     997              : 
     998         1036 : InstrumentIndex searchTemplateIndexForId(const String& id)
     999              : {
    1000         1036 :     int instIndex = 0;
    1001         1036 :     int grpIndex = 0;
    1002         8178 :     for (const InstrumentGroup* g : instrumentGroups) {
    1003       424397 :         for (const InstrumentTemplate* it : g->instrumentTemplates) {
    1004       417255 :             if (it->id == id) {
    1005         1015 :                 return InstrumentIndex(grpIndex, instIndex, it);
    1006              :             }
    1007       416240 :             ++instIndex;
    1008              :         }
    1009         7142 :         ++grpIndex;
    1010              :     }
    1011           21 :     return InstrumentIndex(-1, instIndex, nullptr);
    1012              : }
    1013              : 
    1014              : //---------------------------------------------------------
    1015              : //   linkGenre
    1016              : //      link the current instrument template to the genre list specified by "genre"
    1017              : //      Each genre is a list of pointers to instrument templates
    1018              : //      The list of genres is at application level
    1019              : //---------------------------------------------------------
    1020              : 
    1021        13136 : void InstrumentTemplate::linkGenre(const String& genre)
    1022              : {
    1023        13136 :     const InstrumentGenre* ig = searchInstrumentGenre(genre);
    1024        13136 :     if (ig) {
    1025        13136 :         genres.push_back(ig);
    1026              :     }
    1027        13136 : }
    1028              : 
    1029            0 : void InstrumentGenre::write(XmlWriter& xml) const
    1030              : {
    1031            0 :     xml.startElement("Genre", { { "id", id } });
    1032            0 :     xml.tag("name", name);
    1033            0 :     xml.endElement();
    1034            0 : }
    1035              : 
    1036          176 : void InstrumentGenre::read(XmlReader& e)
    1037              : {
    1038          176 :     id = e.attribute("id");
    1039          352 :     while (e.readNextStartElement()) {
    1040          176 :         const AsciiStringView tag(e.name());
    1041          176 :         if (tag == "name") {
    1042          176 :             name = muse::mtrc("engraving/instruments/genre", e.readText());
    1043              :         } else {
    1044            0 :             e.unknown();
    1045              :         }
    1046              :     }
    1047          176 : }
    1048              : 
    1049            0 : void InstrumentFamily::write(XmlWriter& xml) const
    1050              : {
    1051            0 :     xml.startElement("Family", { { "id", id } });
    1052            0 :     xml.tag("name", name);
    1053            0 :     xml.endElement();
    1054            0 : }
    1055              : 
    1056         1920 : void InstrumentFamily::read(XmlReader& e)
    1057              : {
    1058         1920 :     id = e.attribute("id");
    1059         3840 :     while (e.readNextStartElement()) {
    1060         1920 :         const AsciiStringView tag(e.name());
    1061         1920 :         if (tag == "name") {
    1062         1920 :             name = muse::mtrc("engraving/instruments/family", e.readText());
    1063              :         } else {
    1064            0 :             e.unknown();
    1065              :         }
    1066              :     }
    1067         1920 : }
    1068              : 
    1069              : //---------------------------------------------------------
    1070              : //   clefType
    1071              : //---------------------------------------------------------
    1072              : 
    1073           18 : ClefTypeList InstrumentTemplate::clefType(staff_idx_t staffIdx) const
    1074              : {
    1075           18 :     if (staffIdx < staffCount) {
    1076           18 :         return clefTypes[staffIdx];
    1077              :     }
    1078            0 :     return clefTypes[0];
    1079              : }
    1080              : 
    1081          627 : String InstrumentTemplate::familyId() const
    1082              : {
    1083          627 :     return family ? family->id : String();
    1084              : }
    1085              : 
    1086            0 : bool InstrumentTemplate::containsGenre(const String& genreId) const
    1087              : {
    1088            0 :     for (const InstrumentGenre* genre : genres) {
    1089            0 :         if (genre->id == genreId) {
    1090            0 :             return true;
    1091              :         }
    1092              :     }
    1093              : 
    1094            0 :     return false;
    1095              : }
    1096              : 
    1097              : //---------------------------------------------------------
    1098              : //   defaultClef
    1099              : //    traverse the instrument list for first instrument
    1100              : //    with midi patch 'program'. Return the default clef
    1101              : //    for this instrument.
    1102              : //---------------------------------------------------------
    1103              : 
    1104           77 : ClefType defaultClef(int program)
    1105              : {
    1106           77 :     if (program >= 24 && program < 32) {              // this are guitars
    1107           71 :         return ClefType::G8_VB;
    1108            6 :     } else if (program >= 32 && program < 40) {       // this is bass
    1109            2 :         return ClefType::F8_VB;
    1110              :     }
    1111              : 
    1112           31 :     for (const InstrumentGroup* g : instrumentGroups) {
    1113         1811 :         for (const InstrumentTemplate* it : g->instrumentTemplates) {
    1114         1784 :             if (it->channel[0].bank() == 0 && it->channel[0].program() == program) {
    1115            4 :                 return it->clefTypes[0].concertClef;
    1116              :             }
    1117              :         }
    1118              :     }
    1119            0 :     return ClefType::G;
    1120              : }
    1121              : }
        

Generated by: LCOV version 2.0-1