1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2002-2011 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "instrtemplate.h"
14 #include "bracket.h"
15 #include "drumset.h"
16 #include "stafftype.h"
17 #include "style.h"
18 #include "sym.h"
19 #include "stringdata.h"
20 #include "scoreOrder.h"
21 #include "utils.h"
22 #include "xml.h"
23 
24 namespace Ms {
25 
26 QList<InstrumentGroup*>  instrumentGroups;
27 QList<MidiArticulation>  articulation;                // global articulations
28 QList<InstrumentGenre*>  instrumentGenres;
29 QList<InstrumentFamily*> instrumentFamilies;
30 
31 //---------------------------------------------------------
32 //   searchInstrumentGenre
33 //---------------------------------------------------------
34 
searchInstrumentGenre(const QString & genre)35 static InstrumentGenre * searchInstrumentGenre(const QString& genre)
36       {
37       for(InstrumentGenre* ig : qAsConst(instrumentGenres)) {
38             if (ig->id == genre)
39                   return ig;
40             }
41       return nullptr;
42       }
43 
44 //---------------------------------------------------------
45 //   searchInstrumentFamily
46 //---------------------------------------------------------
47 
searchInstrumentFamily(const QString & name)48 static InstrumentFamily * searchInstrumentFamily(const QString& name)
49       {
50       for(InstrumentFamily* fam : qAsConst(instrumentFamilies)) {
51             if (fam->id == name)
52                   return fam;
53             }
54       return nullptr;
55       }
56 
57 //---------------------------------------------------------
58 //   searchInstrumentGroup
59 //---------------------------------------------------------
60 
searchInstrumentGroup(const QString & name)61 InstrumentGroup* searchInstrumentGroup(const QString& name)
62       {
63       for(InstrumentGroup* g : qAsConst(instrumentGroups)) {
64             if (g->id == name)
65                   return g;
66             }
67       return nullptr;
68       }
69 
70 //---------------------------------------------------------
71 //   searchArticulation
72 //---------------------------------------------------------
73 
searchArticulation(const QString & name)74 static MidiArticulation searchArticulation(const QString& name)
75       {
76       for(MidiArticulation a : qAsConst(articulation)) {
77             if (a.name == name)
78                   return a;
79             }
80       return MidiArticulation();
81       }
82 
83 //---------------------------------------------------------
84 //   readStaffIdx
85 //---------------------------------------------------------
86 
readStaffIdx(XmlReader & e)87 static int readStaffIdx(XmlReader& e)
88       {
89       int idx = e.intAttribute("staff", 1) - 1;
90       if (idx >= MAX_STAVES)
91             idx = MAX_STAVES-1;
92       if (idx < 0)
93             idx = 0;
94       return idx;
95       }
96 
97 //---------------------------------------------------------
98 //   read InstrumentGroup
99 //---------------------------------------------------------
100 
read(XmlReader & e)101 void InstrumentGroup::read(XmlReader& e)
102       {
103       id       = e.attribute("id");
104       name     = qApp->translate("InstrumentsXML", e.attribute("name").toUtf8().data());
105       extended = e.intAttribute("extended", 0);
106 
107       while (e.readNextStartElement()) {
108             const QStringRef& tag(e.name());
109             if (tag == "instrument" || tag == "Instrument") {
110                   QString sid = e.attribute("id");
111                   InstrumentTemplate* t = searchTemplate(sid);
112                   if (t == 0) {
113                         t = new InstrumentTemplate;
114                         t->articulation.append(articulation);     // init with global articulation
115                         instrumentTemplates.append(t);
116                         }
117                   t->read(e);
118                   }
119             else if (tag == "ref") {
120                   InstrumentTemplate* ttt = searchTemplate(e.readElementText());
121                   if (ttt) {
122                         InstrumentTemplate* t = new InstrumentTemplate(*ttt);
123                         instrumentTemplates.append(t);
124                         }
125                   else
126                         qDebug("instrument reference not found <%s>", e.text().toUtf8().data());
127                   }
128             else if (tag == "name")
129                   name = qApp->translate("InstrumentsXML", e.readElementText().toUtf8().data());
130             else if (tag == "extended")
131                   extended = e.readInt();
132             else
133                   e.unknown();
134             }
135       if (id.isEmpty())
136             id = name.toLower().replace(" ", "-");
137       }
138 
139 //---------------------------------------------------------
140 //   clear InstrumentGroup
141 //---------------------------------------------------------
142 
clear()143 void InstrumentGroup::clear()
144       {
145       qDeleteAll(instrumentTemplates);
146       instrumentTemplates.clear();
147       }
148 
149 //---------------------------------------------------------
150 //   InstrumentTemplate
151 //---------------------------------------------------------
152 
InstrumentTemplate()153 InstrumentTemplate::InstrumentTemplate()
154       {
155       staves             = 1;
156       minPitchA          = 0;
157       maxPitchA          = 127;
158       minPitchP          = 0;
159       maxPitchP          = 127;
160       staffGroup         = StaffGroup::STANDARD;
161       staffTypePreset    = 0;
162       useDrumset         = false;
163       drumset            = 0;
164       extended           = false;
165       singleNoteDynamics = true;
166       family             = nullptr;
167 
168       for (int i = 0; i < MAX_STAVES; ++i) {
169             clefTypes[i]._concertClef = ClefType::G;
170             clefTypes[i]._transposingClef = ClefType::G;
171             staffLines[i]  = 5;
172             smallStaff[i]  = false;
173             bracket[i]     = BracketType::NO_BRACKET;
174             bracketSpan[i] = 0;
175             barlineSpan[i] = false;
176             }
177       transpose.diatonic   = 0;
178       transpose.chromatic  = 0;
179       }
180 
InstrumentTemplate(const InstrumentTemplate & t)181 InstrumentTemplate::InstrumentTemplate(const InstrumentTemplate& t)
182       {
183       init(t);
184       }
185 
186 //---------------------------------------------------------
187 //   init
188 //---------------------------------------------------------
189 
init(const InstrumentTemplate & t)190 void InstrumentTemplate::init(const InstrumentTemplate& t)
191       {
192       longNames  = t.longNames;
193       shortNames = t.shortNames;
194       musicXMLid = t.musicXMLid;
195       staves     = t.staves;
196       extended   = t.extended;
197 
198       for (int i = 0; i < MAX_STAVES; ++i) {
199             clefTypes[i]   = t.clefTypes[i];
200             staffLines[i]  = t.staffLines[i];
201             smallStaff[i]  = t.smallStaff[i];
202             bracket[i]     = t.bracket[i];
203             bracketSpan[i] = t.bracketSpan[i];
204             barlineSpan[i] = t.barlineSpan[i];
205             }
206       minPitchA  = t.minPitchA;
207       maxPitchA  = t.maxPitchA;
208       minPitchP  = t.minPitchP;
209       maxPitchP  = t.maxPitchP;
210       transpose  = t.transpose;
211       staffGroup = t.staffGroup;
212       staffTypePreset   = t.staffTypePreset;
213       useDrumset = t.useDrumset;
214       if (t.drumset)
215             drumset = new Drumset(*t.drumset);
216       else
217             drumset = 0;
218       stringData  = t.stringData;
219       midiActions = t.midiActions;
220       channel     = t.channel;
221       family     = t.family;
222       singleNoteDynamics = t.singleNoteDynamics;
223       }
224 
~InstrumentTemplate()225 InstrumentTemplate::~InstrumentTemplate()
226       {
227       delete drumset;
228       }
229 
230 //---------------------------------------------------------
231 //   write
232 //---------------------------------------------------------
233 
write(XmlWriter & xml) const234 void InstrumentTemplate::write(XmlWriter& xml) const
235       {
236       xml.stag(QString("Instrument id=\"%1\"").arg(id));
237       longNames.write(xml, "longName");
238       shortNames.write(xml, "shortName");
239 
240       if (longNames.size() > 1)
241             xml.tag("trackName", trackName);
242       xml.tag("description", description);
243       xml.tag("musicXMLid", musicXMLid);
244       if (extended)
245             xml.tag("extended", extended);
246       stringData.write(xml);
247       if (staves > 1)
248             xml.tag("staves", staves);
249       for (int i = 0; i < staves; ++i) {
250             if (clefTypes[i]._concertClef == clefTypes[i]._transposingClef) {
251                   QString tag = ClefInfo::tag(clefTypes[i]._concertClef);
252                   if (i)
253                         xml.tag(QString("clef staff=\"%1\"").arg(i+1), tag);
254                   else
255                         xml.tag("clef", tag);
256                   }
257             else {
258                   QString tag1 = ClefInfo::tag(clefTypes[i]._concertClef);
259                   QString tag2 = ClefInfo::tag(clefTypes[i]._transposingClef);
260                   if (i) {
261                         xml.tag(QString("concertClef staff=\"%1\"").arg(i+1), tag1);
262                         xml.tag(QString("transposingClef staff=\"%1\"").arg(i+1), tag2);
263                         }
264                   else {
265                         xml.tag("concertClef", tag1);
266                         xml.tag("transposingClef", tag2);
267                         }
268                   }
269             if (staffLines[i] != 5) {
270                   if (i)
271                         xml.tag(QString("stafflines staff=\"%1\"").arg(i+1), staffLines[i]);
272                   else
273                         xml.tag("stafflines", staffLines[i]);
274                   }
275             if (smallStaff[i]) {
276                   if (i)
277                         xml.tag(QString("smallStaff staff=\"%1\"").arg(i+1), smallStaff[i]);
278                   else
279                         xml.tag("smallStaff", smallStaff[i]);
280                   }
281 
282             if (bracket[i] != BracketType::NO_BRACKET) {
283                   if (i)
284                         xml.tag(QString("bracket staff=\"%1\"").arg(i+1), int(bracket[i]));
285                   else
286                         xml.tag("bracket", int(bracket[i]));
287                   }
288             if (bracketSpan[i] != 0) {
289                   if (i)
290                         xml.tag(QString("bracketSpan staff=\"%1\"").arg(i+1), bracketSpan[i]);
291                   else
292                         xml.tag("bracketSpan", bracketSpan[i]);
293                   }
294             if (barlineSpan[i]) {
295                   if (i)
296                         xml.tag(QString("barlineSpan staff=\"%1\"").arg(i+1), barlineSpan[i]);
297                   else
298                         xml.tag("barlineSpan", barlineSpan[i]);
299                   }
300             }
301       if (minPitchA != 0 || maxPitchA != 127)
302             xml.tag("aPitchRange", QString("%1-%2").arg(int(minPitchA)).arg(int(maxPitchA)));
303       if (minPitchP != 0 || maxPitchP != 127)
304             xml.tag("pPitchRange", QString("%1-%2").arg(int(minPitchP)).arg(int(maxPitchP)));
305       if (transpose.diatonic)
306             xml.tag("transposeDiatonic", transpose.diatonic);
307       if (transpose.chromatic)
308             xml.tag("transposeChromatic", transpose.chromatic);
309       if (useDrumset)
310             xml.tag("drumset", int(useDrumset));
311       if (drumset)
312             drumset->save(xml);
313 
314       if (!singleNoteDynamics)      // default is true
315             xml.tag("singleNoteDynamics", singleNoteDynamics);
316 
317       for (const NamedEventList& a : midiActions)
318             a.write(xml, "MidiAction");
319       for (const Channel& a : channel)
320             a.write(xml, nullptr);
321       for (const MidiArticulation& ma : articulation) {
322             bool isGlobal = false;
323             for (const MidiArticulation& ga : qAsConst(Ms::articulation)) {
324                   if (ma == ga) {
325                         isGlobal = true;
326                         break;
327                         }
328                   }
329             if (!isGlobal)
330                   ma.write(xml);
331             }
332       if (!family)
333             xml.tag("family", family->id);
334       xml.etag();
335       }
336 
337 //---------------------------------------------------------
338 //   write1
339 //    output only translatable names
340 //---------------------------------------------------------
341 
write1(XmlWriter & xml) const342 void InstrumentTemplate::write1(XmlWriter& xml) const
343       {
344       xml.stag(QString("Instrument id=\"%1\"").arg(id));
345       longNames.write(xml, "longName");
346       shortNames.write(xml, "shortName");
347       if (longNames.size() > 1)
348             xml.tag("trackName", trackName);
349       xml.tag("description", description);
350       xml.etag();
351       }
352 
353 //---------------------------------------------------------
354 //   read
355 //---------------------------------------------------------
356 
read(XmlReader & e)357 void InstrumentTemplate::read(XmlReader& e)
358       {
359       id = e.attribute("id");
360       while (e.readNextStartElement()) {
361             const QStringRef& tag(e.name());
362 
363             if (tag == "longName" || tag == "name") {               // "name" is obsolete
364                   int pos = e.intAttribute("pos", 0);
365                   for (QList<StaffName>::iterator i = longNames.begin(); i != longNames.end(); ++i) {
366                         if ((*i).pos() == pos) {
367                               longNames.erase(i);
368                               break;
369                               }
370                         }
371                   longNames.append(StaffName(qApp->translate("InstrumentsXML", e.readElementText().toUtf8().data()), pos));
372                   }
373             else if (tag == "shortName" || tag == "short-name") {   // "short-name" is obsolete
374                   int pos = e.intAttribute("pos", 0);
375                   for (QList<StaffName>::iterator i = shortNames.begin(); i != shortNames.end(); ++i) {
376                         if ((*i).pos() == pos) {
377                               shortNames.erase(i);
378                               break;
379 							  }
380                         }
381                   shortNames.append(StaffName(qApp->translate("InstrumentsXML", e.readElementText().toUtf8().data()), pos));
382                   }
383             else if (tag == "trackName")
384                   trackName = qApp->translate("InstrumentsXML", e.readElementText().toUtf8().data());
385             else if (tag == "description")
386                   description = e.readElementText();
387             else if (tag == "extended")
388                   extended = e.readInt();
389             else if (tag == "staves") {
390                   staves = e.readInt();
391                   bracketSpan[0] = staves;
392 //                  for (int i = 0; i < staves-1; ++i)
393 //                        barlineSpan[i] = true;
394                   }
395             else if (tag == "clef") {           // sets both transposing and concert clef
396                   int idx = readStaffIdx(e);
397                   QString val(e.readElementText());
398                   bool ok;
399                   int i = val.toInt(&ok);
400                   ClefType ct = ok ? ClefType(i) : Clef::clefType(val);
401                   clefTypes[idx]._concertClef = ct;
402                   clefTypes[idx]._transposingClef = ct;
403                   }
404             else if (tag == "concertClef") {
405                   int idx = readStaffIdx(e);
406                   QString val(e.readElementText());
407                   bool ok;
408                   int i = val.toInt(&ok);
409                   clefTypes[idx]._concertClef = ok ? ClefType(i) : Clef::clefType(val);
410                   }
411             else if (tag == "transposingClef") {
412                   int idx = readStaffIdx(e);
413                   QString val(e.readElementText());
414                   bool ok;
415                   int i = val.toInt(&ok);
416                   clefTypes[idx]._transposingClef = ok ? ClefType(i) : Clef::clefType(val);
417                   }
418             else if (tag == "stafflines") {
419                   int idx = readStaffIdx(e);
420                   staffLines[idx] = e.readInt();
421                   }
422             else if (tag == "smallStaff") {
423                   int idx = readStaffIdx(e);
424                   smallStaff[idx] = e.readInt();
425                   }
426             else if (tag == "bracket") {
427                   int idx = readStaffIdx(e);
428                   bracket[idx] = BracketType(e.readInt());
429                   }
430             else if (tag == "bracketSpan") {
431                   int idx = readStaffIdx(e);
432                   bracketSpan[idx] = e.readInt();
433                   }
434             else if (tag == "barlineSpan") {
435                   int idx = readStaffIdx(e);
436                   int span = e.readInt();
437                   for (int i = 0; i < span-1; ++i)
438                         barlineSpan[idx+i] = true;
439                   }
440             else if (tag == "aPitchRange")
441                   setPitchRange(e.readElementText(), &minPitchA, &maxPitchA);
442             else if (tag == "pPitchRange")
443                   setPitchRange(e.readElementText(), &minPitchP, &maxPitchP);
444             else if (tag == "transposition") {    // obsolete
445                   int i = e.readInt();
446                   transpose.chromatic = i;
447                   transpose.diatonic = chromatic2diatonic(i);
448                   }
449             else if (tag == "transposeChromatic")
450                   transpose.chromatic = e.readInt();
451             else if (tag == "transposeDiatonic")
452                   transpose.diatonic = e.readInt();
453             else if (tag == "StringData")
454                   stringData.read(e);
455             else if (tag == "drumset")
456                   useDrumset = e.readInt();
457             else if (tag == "Drum") {
458                   // if we see one of this tags, a custom drumset will
459                   // be created
460                   if (drumset == 0) {
461                         drumset = new Drumset(*smDrumset);
462                         drumset->clear();
463                         }
464                   drumset->load(e);
465                   }
466             else if (tag == "MidiAction") {
467                   NamedEventList a;
468                   a.read(e);
469                   midiActions.append(a);
470                   }
471             else if (tag == "Channel" || tag == "channel") {
472                   Channel a;
473                   a.read(e, nullptr);
474                   channel.append(a);
475                   }
476             else if (tag == "Articulation") {
477                   MidiArticulation a;
478                   a.read(e);
479                   int n = articulation.size();
480                   int i;
481                   for(i = 0; i < n; ++i) {
482                         if (articulation[i].name == a.name) {
483                               articulation[i] = a;
484                               break;
485                               }
486                         }
487                   if (i == n)
488                         articulation.append(a);
489                   }
490             else if (tag == "stafftype") {
491                   int staffIdx = readStaffIdx(e);
492                   QString xmlPresetName = e.attribute("staffTypePreset", "");
493                   QString stfGroup = e.readElementText();
494                   if (stfGroup == "percussion")
495                         staffGroup = StaffGroup::PERCUSSION;
496                   else if (stfGroup == "tablature")
497                         staffGroup = StaffGroup::TAB;
498                   else
499                         staffGroup = StaffGroup::STANDARD;
500                   staffTypePreset = 0;
501                   if (!xmlPresetName.isEmpty())
502                         staffTypePreset = StaffType::presetFromXmlName(xmlPresetName);
503                   if (!staffTypePreset || staffTypePreset->group() != staffGroup)
504                         staffTypePreset = StaffType::getDefaultPreset(staffGroup);
505                   if (staffTypePreset)
506                         staffLines[staffIdx] = staffTypePreset->lines();
507                   }
508             else if (tag == "init") {
509                   QString val(e.readElementText());
510                   InstrumentTemplate* ttt = searchTemplate(val);
511                   if (ttt)
512                         init(*ttt);
513                   else
514                         qDebug("InstrumentTemplate:: init instrument <%s> not found", qPrintable(val));
515                   }
516             else if (tag == "musicXMLid") {
517                   musicXMLid = e.readElementText();
518                   }
519             else if (tag == "family") {
520                   family = searchInstrumentFamily(e.readElementText());
521                   }
522             else if (tag == "genre") {
523                   QString val(e.readElementText());
524                   linkGenre(val);
525                   }
526             else if (tag == "singleNoteDynamics")
527                   singleNoteDynamics = e.readBool();
528             else
529                   e.unknown();
530             }
531       if (channel.empty()) {
532             Channel a;
533             a.setChorus(0);
534             a.setReverb(0);
535             a.setName(Channel::DEFAULT_NAME);
536             a.setProgram(0);
537             a.setBank(0);
538             a.setVolume(90);
539             a.setPan(0);
540             channel.append(a);
541             }
542       if (useDrumset) {
543             if (channel[0].bank() == 0 && channel[0].synti().toLower() != "zerberus")
544                   channel[0].setBank(128);
545             }
546       if (trackName.isEmpty() && !longNames.isEmpty())
547             trackName = longNames[0].name();
548       if (description.isEmpty() && !longNames.isEmpty())
549             description = longNames[0].name();
550       if (id.isEmpty())
551             id = trackName.toLower().replace(" ", "-");
552       if (staves == 0)
553             qDebug(" 2Instrument: staves == 0 <%s>", qPrintable(id));
554       }
555 
556 
557 //---------------------------------------------------------
558 //   setPitchRange
559 //---------------------------------------------------------
560 
setPitchRange(const QString & s,char * a,char * b) const561 void InstrumentTemplate::setPitchRange(const QString& s, char* a, char* b) const
562       {
563       QStringList sl = s.split("-");
564       if (sl.size() != 2) {
565             *a = 0;
566             *b = 127;
567             return;
568             }
569       *a = sl[0].toInt();
570       *b = sl[1].toInt();
571       }
572 
573 //---------------------------------------------------------
574 //   saveInstrumentTemplates
575 //---------------------------------------------------------
576 
saveInstrumentTemplates(const QString & instrTemplates)577 bool saveInstrumentTemplates(const QString& instrTemplates)
578       {
579       QFile qf(instrTemplates);
580       if (!qf.open(QIODevice::WriteOnly)) {
581             qDebug("cannot save instrument templates at <%s>", qPrintable(instrTemplates));
582             return false;
583             }
584       XmlWriter xml(0, &qf);
585       xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
586       xml.stag("museScore");
587       foreach(const InstrumentGenre* genre, instrumentGenres)
588             genre->write(xml);
589       xml << "\n";
590       foreach(const InstrumentFamily* fam, instrumentFamilies)
591             fam->write(xml);
592       xml << "\n";
593       foreach(const MidiArticulation& a, articulation)
594             a.write(xml);
595       xml << "\n";
596       foreach(InstrumentGroup* group, instrumentGroups) {
597             xml.stag(QString("InstrumentGroup id=\"%1\"").arg(group->id));
598             xml.tag("name", group->name);
599             if (group->extended)
600                   xml.tag("extended", group->extended);
601             for (InstrumentTemplate* it : qAsConst(group->instrumentTemplates)) {
602                   it->write(xml);
603                   xml << "\n";
604                   }
605             xml.etag();
606             xml << "\n";
607             }
608       xml.etag();
609       qf.close();
610       return true;
611       }
612 
613 //---------------------------------------------------------
614 //   saveInstrumentTemplates1
615 //---------------------------------------------------------
616 
saveInstrumentTemplates1(const QString & instrTemplates)617 bool saveInstrumentTemplates1(const QString& instrTemplates)
618       {
619       QFile qf(instrTemplates);
620       if (!qf.open(QIODevice::WriteOnly)) {
621             qDebug("cannot save instrument templates at <%s>", qPrintable(instrTemplates));
622             return false;
623             }
624       XmlWriter xml(0, &qf);
625       xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
626       xml.stag("museScore");
627       foreach(const InstrumentGenre* genre, instrumentGenres)
628             genre->write1(xml);
629       foreach(const InstrumentFamily* fam, instrumentFamilies)
630             fam->write1(xml);
631       xml << "\n";
632       foreach(InstrumentGroup* group, instrumentGroups) {
633             xml.stag(QString("InstrumentGroup id=\"%1\"").arg(group->id));
634             xml.tag("name", group->name);
635             for (InstrumentTemplate* it : qAsConst(group->instrumentTemplates)) {
636                   it->write1(xml);
637                   xml << "\n";
638                   }
639             xml.etag();
640             xml << "\n";
641             }
642       xml.etag();
643       qf.close();
644       return true;
645       }
646 
647 //---------------------------------------------------------
648 //   clearInstrumentTemplates
649 //---------------------------------------------------------
650 
clearInstrumentTemplates()651 void clearInstrumentTemplates()
652       {
653       for (InstrumentGroup* g : qAsConst(instrumentGroups))
654             g->clear();
655       qDeleteAll(instrumentGroups);
656       instrumentGroups.clear();
657       qDeleteAll(instrumentGenres);
658       instrumentGenres.clear();
659       qDeleteAll(instrumentFamilies);
660       instrumentFamilies.clear();
661       articulation.clear();
662       }
663 
664 //---------------------------------------------------------
665 //   loadInstrumentTemplates
666 //---------------------------------------------------------
667 
loadInstrumentTemplates(const QString & instrTemplates)668 bool loadInstrumentTemplates(const QString& instrTemplates)
669       {
670       QFile qf(instrTemplates);
671       if (!qf.open(QIODevice::Text | QIODevice::ReadOnly)) {
672             qDebug("cannot load instrument templates at <%s>", qPrintable(instrTemplates));
673             return false;
674             }
675 
676       XmlReader e(&qf);
677       while (e.readNextStartElement()) {
678             if (e.name() == "museScore") {
679                   while (e.readNextStartElement()) {
680                         const QStringRef& tag(e.name());
681                         if (tag == "instrument-group" || tag == "InstrumentGroup") {
682                               QString idGroup(e.attribute("id"));
683                               InstrumentGroup* group = searchInstrumentGroup(idGroup);
684                               if (group == 0) {
685                                     group = new InstrumentGroup;
686                                     instrumentGroups.append(group);
687                                     }
688                               group->read(e);
689                               }
690                         else if (tag == "Articulation") {
691                               // read global articulation
692                               QString name(e.attribute("name"));
693                               MidiArticulation a = searchArticulation(name);
694                               a.read(e);
695                               articulation.append(a);
696                               }
697                         else if (tag == "Genre") {
698                               QString idGenre(e.attribute("id"));
699                               InstrumentGenre* genre = searchInstrumentGenre(idGenre);
700                               if (!genre) {
701                                     genre = new InstrumentGenre;
702                                     instrumentGenres.append(genre);
703                                     }
704                               genre->read(e);
705                               }
706                         else if (tag == "Family") {
707                               QString idFamily(e.attribute("id"));
708                               InstrumentFamily* fam = searchInstrumentFamily(idFamily);
709                               if (!fam) {
710                                     fam = new InstrumentFamily;
711                                     instrumentFamilies.append(fam);
712                                     }
713                               fam->read(e);
714                               }
715                         else
716                               e.unknown();
717                         }
718                   }
719             }
720       // saveInstrumentTemplates1("/home/ws/mops.xml");
721       return true;
722       }
723 
724 //---------------------------------------------------------
725 //   searchTemplate
726 //---------------------------------------------------------
727 
searchTemplate(const QString & name)728 InstrumentTemplate* searchTemplate(const QString& name)
729       {
730       for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
731             for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
732                   if (it->id == name)
733                         return it;
734                   }
735             }
736       return 0;
737       }
738 
739 //---------------------------------------------------------
740 //   searchTemplateForMusicXMLid
741 //---------------------------------------------------------
742 
searchTemplateForMusicXmlId(const QString & mxmlId)743 InstrumentTemplate* searchTemplateForMusicXmlId(const QString& mxmlId)
744       {
745       for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
746             for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
747                   if (it->musicXMLid == mxmlId)
748                         return it;
749                   }
750             }
751       return 0;
752       }
753 
searchTemplateForInstrNameList(const QList<QString> & nameList)754 InstrumentTemplate* searchTemplateForInstrNameList(const QList<QString>& nameList)
755       {
756       for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
757             for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
758                   for (const QString& name : nameList) {
759                         if (it->trackName == name ||
760                             it->longNames.contains(StaffName(name)) ||
761                             it->shortNames.contains(StaffName(name)))
762                               return it;
763                         }
764                   }
765             }
766       return nullptr;
767       }
768 
searchTemplateForMidiProgram(int midiProgram,const bool useDrumSet)769 InstrumentTemplate* searchTemplateForMidiProgram(int midiProgram, const bool useDrumSet)
770       {
771       for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
772             for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
773                   if (it->channel.empty() || it->useDrumset != useDrumSet)
774                         continue;
775 
776                   if (it->channel[0].program() == midiProgram)
777                         return it;
778                   }
779             }
780       return 0;
781       }
782 
guessTemplateByNameData(const QList<QString> & nameDataList)783 InstrumentTemplate* guessTemplateByNameData(const QList<QString>& nameDataList)
784       {
785       for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
786             for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
787                   for (const QString& name : nameDataList) {
788                         if (name.contains(it->trackName, Qt::CaseInsensitive) ||
789                             name.contains(it->longNames.value(0).name(), Qt::CaseInsensitive) ||
790                             name.contains(it->shortNames.value(0).name(), Qt::CaseInsensitive)) {
791                               return it;
792                               }
793                         }
794                   }
795             }
796 
797       for (const QString& name : nameDataList) {
798             if (name.contains("drum", Qt::CaseInsensitive))
799                   return searchTemplate("drumset");
800 
801             if (name.contains("piano", Qt::CaseInsensitive))
802                   return searchTemplate("piano");
803             }
804 
805       return nullptr;
806       }
807 
808 //---------------------------------------------------------
809 //   searchTemplateIndexForTrackName
810 //---------------------------------------------------------
811 
searchTemplateIndexForTrackName(const QString & trackName)812 InstrumentIndex searchTemplateIndexForTrackName(const QString& trackName)
813       {
814       int instIndex = 0;
815       int grpIndex = 0;
816       for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
817             for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
818                   if (it->trackName == trackName)
819                         return InstrumentIndex(grpIndex, instIndex, it);
820                   ++instIndex;
821                   }
822             ++grpIndex;
823             }
824       return InstrumentIndex(-1, -1, nullptr);
825       }
826 
827 //---------------------------------------------------------
828 //   searchTemplateIndexForId
829 //---------------------------------------------------------
830 
searchTemplateIndexForId(const QString & id)831 InstrumentIndex searchTemplateIndexForId(const QString& id)
832       {
833       int instIndex = 0;
834       int grpIndex = 0;
835       for (InstrumentGroup* g : instrumentGroups) {
836             for (InstrumentTemplate* it : g->instrumentTemplates) {
837                   if (it->id == id)
838                         return InstrumentIndex(grpIndex, instIndex, it);
839                   ++instIndex;
840                   }
841             ++grpIndex;
842             }
843       return InstrumentIndex(-1, -1, nullptr);
844       }
845 
846 //---------------------------------------------------------
847 //   linkGenre
848 //      link the current instrument template to the genre list specified by "genre"
849 //      Each genre is a list of pointers to instrument templates
850 //      The list of genres is at application level
851 //---------------------------------------------------------
852 
linkGenre(const QString & genre)853 void InstrumentTemplate::linkGenre(const QString& genre)
854       {
855       InstrumentGenre *ig = searchInstrumentGenre(genre);
856       if (ig)
857             genres.append(ig);
858       }
859 
860 //---------------------------------------------------------
861 //   genreMember
862 //      is this instrument template a member of the supplied genre
863 //---------------------------------------------------------
864 
genreMember(const QString & name)865 bool InstrumentTemplate::genreMember(const QString& name)
866       {
867             bool rVal=false;
868             foreach(InstrumentGenre *instrumentGenre, genres ) {
869                 if(instrumentGenre->id == name) {
870                       rVal = true;
871                       break;
872                 }
873             }
874             return rVal;
875       }
876 
write(XmlWriter & xml) const877 void InstrumentGenre::write(XmlWriter& xml) const
878       {
879       xml.stag(QString("Genre id=\"%1\"").arg(id));
880       xml.tag("name", name);
881       xml.etag();
882       }
883 
write1(XmlWriter & xml) const884 void InstrumentGenre::write1(XmlWriter& xml) const
885       {
886       write(xml);
887       }
888 
read(XmlReader & e)889 void InstrumentGenre::read(XmlReader& e)
890       {
891       id = e.attribute("id");
892       while (e.readNextStartElement()) {
893             const QStringRef& tag(e.name());
894             if (tag == "name") {
895                   name = qApp->translate("InstrumentsXML", e.readElementText().toUtf8().data());
896             }
897             else
898                   e.unknown();
899             }
900      }
901 
902 //---------------------------------------------------------
903 //   familyMember
904 //      is this instrument template a member of the supplied family
905 //---------------------------------------------------------
906 
familyMember(const QString & name)907 bool InstrumentTemplate::familyMember(const QString& name)
908       {
909       return family->id == name;
910       }
911 
write(XmlWriter & xml) const912 void InstrumentFamily::write(XmlWriter& xml) const
913       {
914       xml.stag(QString("Family id=\"%1\"").arg(id));
915       xml.tag("name", name);
916       xml.etag();
917       }
918 
write1(XmlWriter & xml) const919 void InstrumentFamily::write1(XmlWriter& xml) const
920       {
921       write(xml);
922       }
923 
read(XmlReader & e)924 void InstrumentFamily::read(XmlReader& e)
925       {
926       id = e.attribute("id");
927       while (e.readNextStartElement()) {
928             const QStringRef& tag(e.name());
929             if (tag == "name") {
930                   name = qApp->translate("InstrumentsXML", e.readElementText().toUtf8().data());
931             }
932             else
933                   e.unknown();
934             }
935      }
936 
937 //---------------------------------------------------------
938 //   clefType
939 //---------------------------------------------------------
940 
clefType(int staffIdx) const941 ClefTypeList InstrumentTemplate::clefType(int staffIdx) const
942       {
943       if (staffIdx < staves)
944             return clefTypes[staffIdx];
945       return clefTypes[0];
946       }
947 
948 //---------------------------------------------------------
949 //   defaultClef
950 //    traverse the instrument list for first instrument
951 //    with midi patch 'program'. Return the default clef
952 //    for this instrument.
953 //---------------------------------------------------------
954 
defaultClef(int program)955 ClefType defaultClef(int program)
956       {
957       if (program >= 24 && program < 32)              // this are guitars
958             return ClefType::G8_VB;
959       else if (program >= 32 && program < 40)         // this is bass
960             return ClefType::F8_VB;
961 
962       for (InstrumentGroup* g : qAsConst(instrumentGroups)) {
963             for (InstrumentTemplate* it : qAsConst(g->instrumentTemplates)) {
964                   if (it->channel[0].bank() == 0 && it->channel[0].program() == program){
965                         return (it->clefTypes[0]._concertClef);
966                         }
967                   }
968             }
969       return ClefType::G;
970       }
971 
972 }
973