Line data Source code
1 : #include "gp67dombuilder.h"
2 :
3 : #include <set>
4 :
5 : #include "global/log.h"
6 :
7 : #include "engraving/types/constants.h"
8 :
9 : using namespace muse;
10 :
11 : namespace mu::iex::guitarpro {
12 221 : GP67DomBuilder::GP67DomBuilder()
13 : {
14 221 : _gpDom = std::make_unique<GPDomModel>();
15 221 : }
16 :
17 221 : void GP67DomBuilder::buildGPDomModel(XmlDomElement* domElem)
18 : {
19 221 : XmlDomNode revision;
20 : // Score node
21 221 : XmlDomNode scoreNode, masterTrack, audioTracks, eachTrack,
22 221 : masterBars, bars, voices,
23 221 : beats, notes, rhythms;
24 :
25 221 : XmlDomNode gpversion, encoding;
26 :
27 : std::map<String, XmlDomNode*> nodeMap =
28 : {
29 0 : { u"GPRevision", &revision },
30 0 : { u"Score", &scoreNode },
31 0 : { u"MasterTrack", &masterTrack },
32 0 : { u"AudioTracks", &audioTracks },
33 0 : { u"Tracks", &eachTrack },
34 0 : { u"MasterBars", &masterBars },
35 0 : { u"Bars", &bars },
36 0 : { u"Voices", &voices },
37 0 : { u"Beats", &beats },
38 0 : { u"Notes", ¬es },
39 0 : { u"Rhythms", &rhythms },
40 0 : { u"GPVersion", &gpversion },
41 0 : { u"Encoding", &encoding }
42 3315 : };
43 :
44 2661 : auto assignMap = [&nodeMap](XmlDomNode node)
45 : {
46 2661 : auto iter = nodeMap.find(node.nodeName());
47 2661 : if (iter != nodeMap.end()) {
48 2661 : *(iter->second) = node;
49 : }
50 2661 : };
51 :
52 2882 : for (XmlDomNode current = domElem->firstChild(); !current.isNull(); current = current.nextSibling()) {
53 2661 : assignMap(current);
54 221 : }
55 :
56 221 : buildGPRhythms(&rhythms);
57 221 : buildGPNotes(¬es);
58 221 : buildGPBeats(&beats);
59 221 : buildGPVoices(&voices);
60 221 : buildGPBars(&bars);
61 221 : buildGPMasterBars(&masterBars);
62 :
63 221 : buildGPScore(&scoreNode);
64 221 : buildGPMasterTracks(&masterTrack);
65 221 : buildGPAudioTracks(&audioTracks);
66 221 : buildGPTracks(&eachTrack, &gpversion);
67 442 : }
68 :
69 221 : std::unique_ptr<GPDomModel> GP67DomBuilder::getGPDomModel()
70 : {
71 221 : return std::move(_gpDom);
72 : }
73 :
74 221 : void GP67DomBuilder::buildGPScore(XmlDomNode* scoreNode)
75 : {
76 : // Contains list of unused info
77 : static const std::set<String> sUnusedNodes = {
78 : u"FirstPageFooter", u"FirstPageHeader",
79 : u"PageFooter", u"PageHeader",
80 : u"ScoreSystemsDefaultLayout", u"ScoreSystemsLayout", u"PageSetup"
81 221 : };
82 :
83 221 : std::unique_ptr<GPScore> score = std::make_unique<GPScore>();
84 221 : XmlDomNode currentNode = scoreNode->firstChild();
85 4448 : while (!currentNode.isNull()) {
86 4227 : String nodeName = currentNode.nodeName();
87 4227 : if (nodeName == u"Title") {
88 221 : score->setTitle(currentNode.toElement().text());
89 4006 : } else if (nodeName == u"Subtitle" || nodeName == u"SubTitle") {
90 221 : score->setSubTitle(currentNode.toElement().text());
91 3785 : } else if (nodeName == u"Artist") {
92 221 : score->setArtist(currentNode.toElement().text());
93 3564 : } else if (nodeName == u"Album") {
94 221 : score->setAlbum(currentNode.toElement().text());
95 3343 : } else if (nodeName == u"Words") {
96 221 : score->setPoet(currentNode.toElement().text());
97 3122 : } else if (nodeName == u"Music") {
98 221 : score->setComposer(currentNode.toElement().text());
99 2901 : } else if (nodeName == u"Copyright") {
100 : // Currently we ignore Copyright info
101 2680 : } else if (nodeName == u"Tabber") {
102 : // Currently we ignore Tabber info
103 2459 : } else if (nodeName == u"Instructions" || nodeName == u"Notices") {
104 : // Currently we ignore score unrelated texts
105 2017 : } else if (nodeName == u"MultiVoice") {
106 : /// gp saves the value "1>" instead of "1"
107 172 : String multiVoiceString = currentNode.toElement().text();
108 172 : if (!multiVoiceString.empty()) {
109 172 : score->setMultiVoice(String(multiVoiceString[0]).toInt());
110 : }
111 2017 : } else if (sUnusedNodes.find(nodeName) != sUnusedNodes.end()) {
112 : // Ignored nodes, which specify unused specifics (e.g. default layout, footers e.t.c.)
113 : }
114 :
115 4227 : currentNode = currentNode.nextSibling();
116 4227 : }
117 221 : _gpDom->addGPScore(std::move(score));
118 221 : }
119 :
120 221 : void GP67DomBuilder::buildGPMasterTracks(XmlDomNode* masterTrack)
121 : {
122 221 : std::unique_ptr<GPMasterTracks> masterTracks = std::make_unique<GPMasterTracks>();
123 :
124 221 : XmlDomNode currentNode = masterTrack->firstChild();
125 883 : while (!currentNode.isNull()) {
126 662 : String nodeName = currentNode.nodeName();
127 662 : if (nodeName == u"Automations") {
128 221 : masterTracks->setTempoMap(readTempoMap(¤tNode));
129 441 : } else if (nodeName == u"RSE") {
130 : //! TODO volume and pan(balance) of mixer are set here
131 221 : } else if (nodeName == u"Tracks") {
132 221 : String tracks = currentNode.toElement().text();
133 221 : size_t tracksCount = tracks.split(u' ').size();
134 221 : masterTracks->setTracksCount(tracksCount);
135 221 : }
136 :
137 662 : currentNode = currentNode.nextSibling();
138 662 : }
139 :
140 221 : _gpDom->addGPMasterTracks(std::move(masterTracks));
141 221 : }
142 :
143 221 : void GP67DomBuilder::buildGPAudioTracks(XmlDomNode* audioTrack)
144 : {
145 221 : XmlDomNode currentNode = audioTrack->firstChild();
146 221 : while (!currentNode.isNull()) {
147 0 : currentNode = currentNode.nextSibling();
148 : }
149 221 : }
150 :
151 221 : void GP67DomBuilder::buildGPTracks(XmlDomNode* tracksNode, XmlDomNode* versionNode)
152 : {
153 221 : std::map<int, std::unique_ptr<GPTrack> > tracks;
154 221 : XmlDomNode currentNode = tracksNode->firstChild();
155 460 : while (!currentNode.isNull()) {
156 239 : tracks.insert(createGPTrack(¤tNode, versionNode));
157 239 : currentNode = currentNode.nextSibling();
158 : }
159 :
160 221 : _gpDom->addGPTracks(std::move(tracks));
161 221 : }
162 :
163 221 : void GP67DomBuilder::buildGPMasterBars(XmlDomNode* masterBarsNode)
164 : {
165 221 : std::vector<std::unique_ptr<GPMasterBar> > masterBars;
166 :
167 221 : XmlDomNode innerNode = masterBarsNode->firstChild();
168 221 : int masterBarIdx = 0;
169 916 : while (!innerNode.isNull()) {
170 695 : String nodeName = innerNode.nodeName();
171 :
172 695 : if (nodeName == u"MasterBar") {
173 695 : masterBars.push_back(createGPMasterBar(&innerNode));
174 695 : masterBars.back()->setId(masterBarIdx);
175 695 : masterBarIdx++;
176 : }
177 :
178 695 : innerNode = innerNode.nextSibling();
179 695 : }
180 :
181 221 : _gpDom->addGPMasterBars(std::move(masterBars));
182 221 : }
183 :
184 221 : void GP67DomBuilder::buildGPBars(XmlDomNode* barsNode)
185 : {
186 221 : std::unordered_map<int, std::unique_ptr<GPBar> > bars;
187 :
188 221 : XmlDomNode innerNode = barsNode->firstChild();
189 999 : while (!innerNode.isNull()) {
190 778 : String nodeName = innerNode.nodeName();
191 778 : if (nodeName == u"Bar") {
192 778 : bars.insert(createGPBar(&innerNode));
193 : }
194 :
195 778 : innerNode = innerNode.nextSibling();
196 778 : }
197 :
198 221 : _bars.swap(bars);
199 221 : }
200 :
201 221 : void GP67DomBuilder::buildGPVoices(XmlDomNode* voicesNode)
202 : {
203 221 : std::unordered_map<int, std::unique_ptr<GPVoice> > voices;
204 :
205 221 : XmlDomNode innerNode = voicesNode->firstChild();
206 945 : while (!innerNode.isNull()) {
207 724 : String nodeName = innerNode.nodeName();
208 724 : if (nodeName == u"Voice") {
209 724 : voices.insert(createGPVoice(&innerNode));
210 : }
211 :
212 724 : innerNode = innerNode.nextSibling();
213 724 : }
214 :
215 221 : _voices.swap(voices);
216 221 : }
217 :
218 221 : void GP67DomBuilder::buildGPBeats(XmlDomNode* beatsNode)
219 : {
220 221 : std::unordered_map<int, std::shared_ptr<GPBeat> > beats;
221 :
222 221 : XmlDomNode innerNode = beatsNode->firstChild();
223 1718 : while (!innerNode.isNull()) {
224 1497 : String nodeName = innerNode.nodeName();
225 1497 : if (nodeName == u"Beat") {
226 1497 : beats.insert(createGPBeat(&innerNode));
227 : }
228 :
229 1497 : innerNode = innerNode.nextSibling();
230 1497 : }
231 :
232 221 : _beats.swap(beats);
233 221 : }
234 :
235 221 : void GP67DomBuilder::buildGPNotes(XmlDomNode* notesNode)
236 : {
237 221 : std::unordered_map<int, std::shared_ptr<GPNote> > notes;
238 :
239 221 : XmlDomNode innerNode = notesNode->firstChild();
240 :
241 1561 : while (!innerNode.isNull()) {
242 1340 : String nodeName = innerNode.nodeName();
243 1340 : if (nodeName == u"Note") {
244 1340 : notes.insert(createGPNote(&innerNode));
245 : }
246 :
247 1340 : innerNode = innerNode.nextSibling();
248 1340 : }
249 :
250 221 : _notes.swap(notes);
251 221 : }
252 :
253 221 : void GP67DomBuilder::buildGPRhythms(XmlDomNode* rhythmsNode)
254 : {
255 221 : std::unordered_map<int, std::shared_ptr<GPRhythm> > rhythms;
256 :
257 221 : XmlDomNode innerNode = rhythmsNode->firstChild();
258 568 : while (!innerNode.isNull()) {
259 347 : String nodeName = innerNode.nodeName();
260 347 : if (nodeName == u"Rhythm") {
261 347 : rhythms.insert(createGPRhythm(&innerNode));
262 : }
263 :
264 347 : innerNode = innerNode.nextSibling();
265 347 : }
266 :
267 221 : _rhythms.swap(rhythms);
268 221 : }
269 :
270 221 : std::vector<GPMasterTracks::Automation> GP67DomBuilder::readTempoMap(XmlDomNode* currentNode)
271 : {
272 221 : std::vector<GPMasterTracks::Automation> tempoMap;
273 221 : XmlDomNode currentAutomation = currentNode->firstChild();
274 444 : while (!currentAutomation.isNull()) {
275 223 : if (currentAutomation.nodeName() == u"Automation") {
276 223 : XmlDomElement ln = currentAutomation.firstChildElement("Linear");
277 223 : String first_name = currentAutomation.firstChild().nodeName();
278 223 : if (first_name == u"Type") {
279 223 : first_name = currentAutomation.firstChild().toElement().text();
280 : }
281 223 : if (first_name == u"Tempo") {
282 223 : GPMasterTracks::Automation tempo;
283 223 : tempo.type = GPMasterTracks::Automation::Type::tempo;
284 223 : String str = currentAutomation.firstChildElement("Value").toElement().text();
285 223 : StringList tempoValue = str.split(u' ');
286 223 : tempo.value = tempoValue[0].toDouble();
287 223 : tempo.tempoUnit = tempoValue.size() > 1 ? static_cast<int>(tempoValue[1].toDouble()) : 0;
288 223 : tempo.bar = currentAutomation.firstChildElement("Bar").text().toInt();
289 223 : tempo.position = currentAutomation.firstChildElement("Position").text().toFloat();
290 223 : tempo.linear = (ln.toElement().text() == u"true");
291 223 : XmlDomElement labelElem = currentAutomation.firstChildElement("Text");
292 223 : if (labelElem.hasChildNodes()) {
293 28 : tempo.text = labelElem.toElement().text();
294 : }
295 :
296 223 : tempoMap.push_back(tempo);
297 223 : }
298 223 : }
299 223 : currentAutomation = currentAutomation.nextSibling();
300 : }
301 :
302 221 : return tempoMap;
303 221 : }
304 :
305 0 : std::unique_ptr<GPMasterTracks> GP67DomBuilder::createGPMasterTrack(XmlDomNode* metadata)
306 : {
307 : UNUSED(metadata);
308 0 : return std::make_unique<GPMasterTracks>();
309 : }
310 :
311 695 : std::unique_ptr<GPMasterBar> GP67DomBuilder::createGPMasterBar(XmlDomNode* masterBarNode)
312 : {
313 : static const std::set<String> sUnused = {
314 : u"XProperties"
315 695 : };
316 :
317 0 : auto tripletFeelType = [](String&& str) {
318 0 : if (str == u"Triplet8th") {
319 0 : return GPMasterBar::TripletFeelType::Triplet8th;
320 : }
321 0 : if (str == u"Triplet16th") {
322 0 : return GPMasterBar::TripletFeelType::Triplet16th;
323 : }
324 0 : if (str == u"Dotted8th") {
325 0 : return GPMasterBar::TripletFeelType::Dotted8th;
326 : }
327 0 : if (str == u"Dotted16th") {
328 0 : return GPMasterBar::TripletFeelType::Dotted16th;
329 : }
330 0 : if (str == u"Scottish8th") {
331 0 : return GPMasterBar::TripletFeelType::Scottish8th;
332 : }
333 0 : if (str == u"Scottish16th") {
334 0 : return GPMasterBar::TripletFeelType::Scottish16th;
335 : }
336 0 : return GPMasterBar::TripletFeelType::None;
337 : };
338 :
339 695 : std::unique_ptr<GPMasterBar> masterBar = std::make_unique<GPMasterBar>();
340 :
341 695 : auto innerNode = masterBarNode->firstChild();
342 :
343 3014 : while (!innerNode.isNull()) {
344 2319 : String nodeName = innerNode.nodeName();
345 :
346 2319 : if (nodeName == u"Time") {
347 695 : masterBar->setTimeSig(readTimeSig(&innerNode));
348 1624 : } else if (nodeName == u"Repeat") {
349 8 : masterBar->setRepeat(readRepeat(&innerNode));
350 1616 : } else if (nodeName == u"AlternateEndings") {
351 4 : masterBar->setAlternativeEnding(readEnding(&innerNode));
352 1612 : } else if (nodeName == u"Key") {
353 695 : masterBar->setKeySig(readKeySig(&innerNode), readUseFlats(&innerNode));
354 917 : } else if (nodeName == u"Bars") {
355 695 : const String& barsElement = innerNode.toElement().text();
356 695 : const StringList& bars = barsElement.split(u' ');
357 1473 : for (const String& barIdx : bars) {
358 778 : int idx = barIdx.toInt();
359 778 : std::unique_ptr<GPBar> bar;
360 778 : bar = std::move(_bars.at(idx));
361 778 : _bars.erase(idx);
362 778 : masterBar->addGPBar(std::move(bar));
363 778 : }
364 917 : } else if (nodeName == u"TripletFeel") {
365 0 : masterBar->setTripletFeel(tripletFeelType(innerNode.toElement().text()));
366 222 : } else if (nodeName == u"Fermatas") {
367 6 : masterBar->setFermatas(readFermatas(&innerNode));
368 216 : } else if (nodeName == u"Section") {
369 3 : masterBar->setSection(readMasterBarSection(innerNode));
370 213 : } else if (nodeName == u"Directions") {
371 36 : masterBar->setDirections(readRepeatsJumps(&innerNode));
372 177 : } else if (nodeName == u"DoubleBar") {
373 3 : masterBar->setBarlineType(GPMasterBar::BarlineType::DOUBLE);
374 174 : } else if (nodeName == u"FreeTime") {
375 8 : masterBar->setFreeTime(true);
376 166 : } else if (sUnused.find(nodeName) != sUnused.end()) {
377 : // Ignored
378 : }
379 :
380 2319 : innerNode = innerNode.nextSibling();
381 2319 : }
382 :
383 695 : return masterBar;
384 695 : }
385 :
386 778 : std::pair<int, std::unique_ptr<GPBar> > GP67DomBuilder::createGPBar(XmlDomNode* barNode)
387 : {
388 778 : auto clefType = [](const String& clef) {
389 778 : if (clef == u"C4") {
390 2 : return GPBar::ClefType::C4;
391 776 : } else if (clef == u"C3") {
392 2 : return GPBar::ClefType::C3;
393 774 : } else if (clef == u"F4") {
394 15 : return GPBar::ClefType::F4;
395 759 : } else if (clef == u"G2") {
396 727 : return GPBar::ClefType::G2;
397 : } else {
398 32 : return GPBar::ClefType::Neutral;
399 : }
400 : };
401 :
402 6 : auto ottaviaType = [](const String& ott) {
403 6 : if (ott == u"8va") {
404 2 : return GPBar::OttaviaType::va8;
405 4 : } else if (ott == u"15ma") {
406 2 : return GPBar::OttaviaType::ma15;
407 2 : } else if (ott == u"8vb") {
408 2 : return GPBar::OttaviaType::vb8;
409 0 : } else if (ott == u"15mb") {
410 0 : return GPBar::OttaviaType::mb15;
411 : } else {
412 0 : return GPBar::OttaviaType::Regular;
413 : }
414 : };
415 3 : auto simileMarkType = [](const String& str) {
416 3 : if (str == u"Simple") {
417 3 : return GPBar::SimileMark::Simple;
418 0 : } else if (str == u"FirstOfDouble") {
419 0 : return GPBar::SimileMark::FirstOfDouble;
420 0 : } else if (str == u"SecondOfDouble") {
421 0 : return GPBar::SimileMark::SecondOfDouble;
422 : } else {
423 0 : return GPBar::SimileMark::None;
424 : }
425 : };
426 :
427 778 : std::unique_ptr<GPBar> bar = std::make_unique<GPBar>();
428 :
429 778 : auto innerNode = barNode->firstChild();
430 778 : int barIdx = barNode->toElement().attribute("id").value().toInt();
431 778 : bar->setId(barIdx);
432 :
433 2357 : while (!innerNode.isNull()) {
434 1579 : String nodeName = innerNode.nodeName();
435 :
436 1579 : if (nodeName == u"Clef") {
437 778 : bar->setClefType(clefType(innerNode.toElement().text()));
438 801 : } else if (nodeName == u"Ottavia") {
439 6 : bar->setOttaviaType(ottaviaType(innerNode.toElement().text()));
440 795 : } else if (nodeName == u"SimileMark") {
441 3 : bar->setSimileMark(simileMarkType(innerNode.toElement().text()));
442 792 : } else if (nodeName == u"Voices") {
443 778 : String voicesElement = innerNode.toElement().text();
444 778 : StringList voices = voicesElement.split(u' ');
445 778 : int currentPosition = -1;
446 3890 : for (const String& voiceIdx : voices) {
447 3112 : currentPosition++;
448 3112 : int idx = voiceIdx.toInt();
449 3112 : if (idx == -1) {
450 2388 : continue;
451 : }
452 :
453 724 : std::unique_ptr<GPVoice> voice;
454 724 : voice = std::move(_voices.at(idx));
455 724 : voice->setPosition(currentPosition);
456 724 : _voices.erase(idx);
457 724 : bar->addGPVoice(std::move(voice));
458 724 : }
459 778 : }
460 :
461 1579 : innerNode = innerNode.nextSibling();
462 1579 : }
463 :
464 1556 : return std::make_pair(barIdx, std::move(bar));
465 778 : }
466 :
467 724 : std::pair<int, std::unique_ptr<GPVoice> > GP67DomBuilder::createGPVoice(XmlDomNode* voiceNode)
468 : {
469 724 : std::unique_ptr<GPVoice> voice = std::make_unique<GPVoice>();
470 :
471 724 : XmlDomNode innerNode = voiceNode->firstChild();
472 724 : int voiceIdx = voiceNode->toElement().attribute("id").value().toInt();
473 724 : voice->setId(voiceIdx);
474 :
475 1448 : while (!innerNode.isNull()) {
476 724 : auto nodeName = innerNode.nodeName();
477 724 : if (nodeName == u"Beats") {
478 724 : String beatsElement = innerNode.toElement().text();
479 724 : StringList beats = beatsElement.split(u' ');
480 2857 : for (const String& beatIdx : beats) {
481 2133 : int idx = beatIdx.toInt();
482 2133 : std::shared_ptr<GPBeat> beat;
483 2133 : beat = _beats.at(idx);
484 2133 : voice->addGPBeat(beat);
485 2133 : }
486 724 : }
487 :
488 724 : innerNode = innerNode.nextSibling();
489 724 : }
490 :
491 1448 : return std::make_pair(voiceIdx, std::move(voice));
492 724 : }
493 :
494 1497 : std::pair<int, std::shared_ptr<GPBeat> > GP67DomBuilder::createGPBeat(XmlDomNode* beatNode)
495 : {
496 : static const std::set<String> sUnused = {
497 : u"Bank",
498 : u"StemOrientation", u"ConcertPitchStemOrientation",
499 : u"Ottavia"
500 1497 : };
501 :
502 1497 : auto dynamicType = [](String&& str) -> GPBeat::DynamicType {
503 1497 : if (str == u"FFF") {
504 4 : return GPBeat::DynamicType::FFF;
505 1493 : } else if (str == u"FF") {
506 12 : return GPBeat::DynamicType::FF;
507 1481 : } else if (str == u"F") {
508 274 : return GPBeat::DynamicType::F;
509 1207 : } else if (str == u"MF") {
510 1174 : return GPBeat::DynamicType::MF;
511 33 : } else if (str == u"MP") {
512 19 : return GPBeat::DynamicType::MP;
513 14 : } else if (str == u"P") {
514 2 : return GPBeat::DynamicType::P;
515 12 : } else if (str == u"PP") {
516 4 : return GPBeat::DynamicType::PP;
517 : } else {
518 8 : return GPBeat::DynamicType::PPP;
519 : }
520 : };
521 59 : auto legatoType = [](const String& origin, const String& destination) {
522 59 : if (origin == u"true" && destination == u"false") {
523 20 : return GPBeat::LegatoType::Start;
524 39 : } else if (origin == u"true" && destination == u"true") {
525 27 : return GPBeat::LegatoType::Mediate;
526 12 : } else if (origin == u"false" && destination == u"true") {
527 12 : return GPBeat::LegatoType::End;
528 : } else {
529 0 : return GPBeat::LegatoType::None;
530 : }
531 : };
532 4 : auto arpeggioType = [](const String& arp) {
533 4 : if (arp == u"Up") {
534 2 : return GPBeat::Arpeggio::Up;
535 2 : } else if (arp == u"Down") {
536 2 : return GPBeat::Arpeggio::Down;
537 : }
538 0 : return GPBeat::Arpeggio::None;
539 : };
540 24 : auto graceNotes = [](const String& gn) {
541 24 : if (gn == u"OnBeat") {
542 10 : return GPBeat::GraceNotes::OnBeat;
543 14 : } else if (gn == u"BeforeBeat") {
544 14 : return GPBeat::GraceNotes::BeforeBeat;
545 : }
546 0 : return GPBeat::GraceNotes::None;
547 : };
548 7 : auto faddingType = [](const String& str) {
549 7 : if (str == u"FadeIn") {
550 3 : return GPBeat::Fadding::FadeIn;
551 4 : } else if (str == u"FadeOut") {
552 0 : return GPBeat::Fadding::FadeOut;
553 4 : } else if (str == u"VolumeSwell") {
554 4 : return GPBeat::Fadding::VolumeSwell;
555 : }
556 0 : return GPBeat::Fadding::None;
557 : };
558 14 : auto hairpinType = [](const String& str) {
559 14 : if (str == u"Crescendo") {
560 7 : return GPBeat::Hairpin::Crescendo;
561 7 : } else if (str == u"Decrescendo") {
562 7 : return GPBeat::Hairpin::Decrescendo;
563 : }
564 0 : return GPBeat::Hairpin::None;
565 : };
566 4 : auto wahType = [](const String& str) {
567 4 : if (str == u"Open") {
568 2 : return GPBeat::Wah::Open;
569 2 : } else if (str == u"Closed") {
570 2 : return GPBeat::Wah::Closed;
571 : } else {
572 0 : return GPBeat::Wah::None;
573 : }
574 : };
575 :
576 0 : auto golpeType = [](const String& str) {
577 0 : if (str == u"Finger") {
578 0 : return GPBeat::Golpe::Finger;
579 0 : } else if (str == u"Thumb") {
580 0 : return GPBeat::Golpe::Thumb;
581 : }
582 :
583 0 : return GPBeat::Golpe::None;
584 : };
585 :
586 51 : auto ottavaType = [](const String& ott) {
587 51 : if (ott == u"8va") {
588 30 : return GPBeat::OttavaType::va8;
589 21 : } else if (ott == u"15ma") {
590 3 : return GPBeat::OttavaType::ma15;
591 18 : } else if (ott == u"8vb") {
592 14 : return GPBeat::OttavaType::vb8;
593 4 : } else if (ott == u"15mb") {
594 4 : return GPBeat::OttavaType::mb15;
595 : }
596 0 : LOGE() << "wrong ottava type: " << ott;
597 0 : return GPBeat::OttavaType::None;
598 : };
599 :
600 1497 : std::shared_ptr<GPBeat> beat = std::make_shared<GPBeat>();
601 :
602 1497 : auto innerNode = beatNode->firstChild();
603 1497 : int beatIdx = beatNode->toElement().attribute("id").value().toInt();
604 1497 : beat->setId(beatIdx);
605 :
606 9669 : while (!innerNode.isNull()) {
607 8172 : String nodeName = innerNode.nodeName();
608 :
609 8172 : if (nodeName == u"Dynamic") {
610 1497 : GPBeat::DynamicType dynamic = dynamicType(innerNode.toElement().text());
611 1497 : beat->setDynamic(dynamic);
612 6675 : } else if (nodeName == u"Legato") {
613 59 : String origin = innerNode.toElement().attribute("origin").value();
614 59 : String destination = innerNode.toElement().attribute("destination").value();
615 59 : GPBeat::LegatoType legato = legatoType(origin, destination);
616 59 : beat->setLegatoType(legato);
617 6675 : } else if (nodeName == u"Rhythm") {
618 1497 : int rIdx = innerNode.toElement().attribute("ref").value().toInt();
619 1497 : beat->addGPRhythm(_rhythms.at(rIdx));
620 5119 : } else if (nodeName == u"Notes") {
621 1224 : String notesStr = innerNode.toElement().text();
622 1224 : StringList strList = notesStr.split(u' ');
623 2754 : for (const auto& strIdx : strList) {
624 1530 : int idx = strIdx.toInt();
625 1530 : std::shared_ptr<GPNote> note;
626 1530 : note = _notes.at(idx);
627 1530 : beat->addGPNote(note);
628 1530 : }
629 1224 : beat->sortGPNotes();
630 5119 : } else if (nodeName == u"GraceNotes") {
631 24 : beat->setGraceNotes(graceNotes(innerNode.toElement().text()));
632 3871 : } else if (nodeName == u"Arpeggio") {
633 4 : beat->setArpeggio(arpeggioType(innerNode.toElement().text()));
634 3867 : } else if (nodeName == u"Properties") {
635 1480 : readBeatProperties(innerNode, beat.get());
636 2387 : } else if (nodeName == u"Chord") {
637 20 : beat->setDiagramIdx(innerNode.toElement().text().toInt());
638 2367 : } else if (nodeName == u"Timer") {
639 6 : beat->setTime(innerNode.toElement().text().toInt());
640 2361 : } else if (nodeName == u"FreeText") {
641 7 : beat->setFreeText(innerNode.toElement().text());
642 2354 : } else if (nodeName == u"Fadding") {
643 7 : beat->setFadding(faddingType(innerNode.toElement().text()));
644 2347 : } else if (nodeName == u"Hairpin") {
645 14 : beat->setHairpin(hairpinType(innerNode.toElement().text()));
646 2333 : } else if (nodeName == u"Tremolo") {
647 6 : GPBeat::Tremolo tr;
648 6 : String trStr = innerNode.toElement().text();
649 6 : StringList trList = trStr.split(u'/');
650 6 : tr.numerator = trList.at(0).toInt();
651 6 : tr.denominator = trList.at(1).toInt();
652 6 : beat->setTremolo(tr);
653 2333 : } else if (nodeName == u"Wah") {
654 4 : beat->setWah(wahType(innerNode.toElement().text()));
655 2323 : } else if (nodeName == u"Golpe") {
656 0 : beat->setGolpe(golpeType(innerNode.toElement().text()));
657 2323 : } else if (nodeName == u"Lyrics") {
658 : // this code reads lyrics for the beat (only one line).
659 :
660 0 : XmlDomElement lyrNode = innerNode.firstChildElement("Line");
661 0 : String str = lyrNode.toElement().text();
662 0 : beat->setLyrics(str.toStdString());
663 2323 : } else if (nodeName == u"Ottavia") {
664 51 : beat->setOttavaType(ottavaType(innerNode.toElement().text()));
665 2272 : } else if (nodeName == u"Whammy" || nodeName == u"WhammyExtend") {
666 74 : beat->setWhammy(readWhammy(innerNode.toElement()));
667 2198 : } else if (nodeName == u"DeadSlapped") {
668 0 : beat->setDeadSlapped(true);
669 2198 : } else if (nodeName == u"TransposedPitchStemOrientation") {
670 1041 : beat->setStemOrientationUp(innerNode.toElement().text() == u"Upward");
671 1157 : } else if (nodeName == u"TransposedPitchStemOrientationUserDefined") {
672 0 : beat->setStemOrientationUserDefined(true);
673 1157 : } else if (nodeName == u"XProperties") {
674 42 : readBeatXProperties(innerNode, beat.get());
675 1115 : } else if (sUnused.find(nodeName) != sUnused.end()) {
676 : // Ignored nodes
677 : }
678 :
679 8172 : innerNode = innerNode.nextSibling();
680 8172 : }
681 :
682 2994 : return std::make_pair(beatIdx, std::move(beat));
683 1497 : }
684 :
685 1340 : std::pair<int, std::shared_ptr<GPNote> > GP67DomBuilder::createGPNote(XmlDomNode* noteNode)
686 : {
687 131 : auto tieType = [](const String& origin, const String& destination) ->GPNote::TieType {
688 131 : if (origin == u"true" && destination == u"false") {
689 55 : return GPNote::TieType::Start;
690 76 : } else if (origin == u"true" && destination == u"true") {
691 18 : return GPNote::TieType::Mediate;
692 58 : } else if (origin == u"false" && destination == u"true") {
693 58 : return GPNote::TieType::End;
694 : } else {
695 0 : return GPNote::TieType::None;
696 : }
697 : };
698 9 : auto vibratoType = [](const String& str) {
699 9 : if (str == u"Wide") {
700 3 : return GPNote::VibratoType::Wide;
701 6 : } else if (str == u"Slight") {
702 6 : return GPNote::VibratoType::Slight;
703 : } else {
704 0 : return GPNote::VibratoType::None;
705 : }
706 : };
707 8 : auto ornamentType = [](const String& str) {
708 8 : if (str == u"UpperMordent") {
709 2 : return GPNote::Ornament::UpperMordent;
710 6 : } else if (str == u"LowerMordent") {
711 2 : return GPNote::Ornament::LowerMordent;
712 4 : } else if (str == u"InvertedTurn") {
713 2 : return GPNote::Ornament::InvertedTurn;
714 2 : } else if (str == u"Turn") {
715 2 : return GPNote::Ornament::Turn;
716 : } else {
717 0 : return GPNote::Ornament::None;
718 : }
719 : };
720 :
721 1340 : auto note = std::make_shared<GPNote>();
722 1340 : int noteIdx = noteNode->toElement().attribute("id").value().toInt();
723 1340 : note->setId(noteIdx);
724 :
725 1340 : auto innerNode = noteNode->firstChild();
726 3908 : while (!innerNode.isNull()) {
727 2568 : String nodeName = innerNode.nodeName();
728 :
729 2568 : if (nodeName == u"Accidental") {
730 : std::map<String, int> accidentals = {
731 0 : { u"DoubleFlat", -2 },
732 0 : { u"Flat", -1 },
733 0 : { u"Natural", 0 },
734 0 : { u"Sharp", +1 },
735 0 : { u"DoubleSharp", +2 }
736 301 : };
737 :
738 43 : String accidentalName = innerNode.toElement().text();
739 :
740 43 : if (accidentals.find(accidentalName) != accidentals.end()) {
741 43 : note->setAccidental(accidentals[accidentalName]);
742 : }
743 43 : }
744 2568 : if (nodeName == u"InstrumentArticulation") {
745 : }
746 2568 : if (nodeName == u"Properties") {
747 1340 : readNoteProperties(&innerNode, note.get());
748 : }
749 2568 : if (nodeName == u"XProperties") {
750 2 : readNoteXProperties(innerNode, note.get());
751 : }
752 2568 : if (nodeName == u"Tie") {
753 131 : String origin = innerNode.toElement().attribute("origin").value();
754 131 : String destination = innerNode.toElement().attribute("destination").value();
755 131 : GPNote::TieType tie = tieType(origin, destination);
756 131 : note->setTieType(tie);
757 131 : }
758 2568 : if (nodeName == u"LetRing") {
759 8 : note->setLetRing(true);
760 2560 : } else if (nodeName == u"AntiAccent") {
761 2 : note->setGhostNote(innerNode.toElement().text() == u"Normal");
762 2558 : } else if (nodeName == u"Accent") {
763 6 : note->setAccent(innerNode.toElement().text().toUInt());
764 2552 : } else if (nodeName == u"LeftFingering") {
765 12 : String finger = innerNode.toElement().text();
766 12 : if (finger == u"Open") {
767 2 : finger = u"0";
768 10 : } else if (finger == u"P") {
769 2 : finger = u"t";
770 8 : } else if (finger == u"I") {
771 2 : finger = u"1";
772 6 : } else if (finger == u"M") {
773 2 : finger = u"2";
774 4 : } else if (finger == u"A") {
775 2 : finger = u"3";
776 2 : } else if (finger == u"C") {
777 2 : finger = u"4";
778 : }
779 12 : note->setLeftFingering(finger);
780 2552 : } else if (nodeName == "RightFingering") {
781 10 : note->setRightFingering(innerNode.toElement().text().toLower());
782 2530 : } else if (nodeName == "Vibrato") {
783 9 : note->setVibratoType(vibratoType(innerNode.toElement().text()));
784 2521 : } else if (nodeName == "Trill") {
785 2 : note->setTrillFret(innerNode.toElement().text().toInt());
786 2519 : } else if (nodeName == "Ornament") {
787 8 : note->setOrnament(ornamentType(innerNode.toElement().text()));
788 : }
789 :
790 2568 : innerNode = innerNode.nextSibling();
791 2568 : }
792 :
793 2680 : return std::make_pair(noteIdx, std::move(note));
794 1383 : }
795 :
796 347 : std::pair<int, std::shared_ptr<GPRhythm> > GP67DomBuilder::createGPRhythm(XmlDomNode* rhythmNode)
797 : {
798 347 : auto rhythmType = [](const String& str) -> GPRhythm::RhytmType {
799 347 : if (str == u"Whole") {
800 49 : return GPRhythm::RhytmType::Whole;
801 298 : } else if (str == u"Half") {
802 58 : return GPRhythm::RhytmType::Half;
803 240 : } else if (str == u"Quarter") {
804 173 : return GPRhythm::RhytmType::Quarter;
805 67 : } else if (str == u"Eighth") {
806 41 : return GPRhythm::RhytmType::Eighth;
807 26 : } else if (str == u"16th") {
808 17 : return GPRhythm::RhytmType::Sixteenth;
809 9 : } else if (str == u"32nd") {
810 9 : return GPRhythm::RhytmType::ThirtySecond;
811 : } else {
812 0 : return GPRhythm::RhytmType::SixtyFourth;
813 : }
814 : };
815 :
816 347 : std::shared_ptr<GPRhythm> rhythm = std::make_shared<GPRhythm>();
817 :
818 347 : auto innerNode = rhythmNode->firstChild();
819 347 : int rhythmIdx = rhythmNode->toElement().attribute("id").value().toInt();
820 719 : while (!innerNode.isNull()) {
821 372 : String nodeName = innerNode.nodeName();
822 372 : if (nodeName == u"NoteValue") {
823 347 : auto rhType = rhythmType(innerNode.toElement().text());
824 347 : rhythm->setRhytm(rhType);
825 : }
826 372 : if (nodeName == u"AugmentationDot") {
827 11 : rhythm->setDotCount(innerNode.toElement().attribute("count").value().toInt());
828 361 : } else if (nodeName == u"PrimaryTuplet") {
829 14 : int num = innerNode.toElement().attribute("num").value().toInt();
830 14 : int denom = innerNode.toElement().attribute("den").value().toInt();
831 14 : rhythm->setTuplet({ num, denom });
832 : }
833 :
834 372 : innerNode = innerNode.nextSibling();
835 372 : }
836 :
837 694 : return std::make_pair(rhythmIdx, std::move(rhythm));
838 347 : }
839 :
840 238 : GPTrack::RSE GP67DomBuilder::readTrackRSE(XmlDomNode* trackChildNode) const
841 : {
842 238 : XmlDomNode innerNode = trackChildNode->firstChild();
843 238 : String nodeName = innerNode.nodeName();
844 :
845 238 : if (!innerNode.isNull() && nodeName == u"ChannelStrip") {
846 238 : innerNode = innerNode.firstChildElement("Parameters");
847 238 : if (!innerNode.isNull()) {
848 238 : String str = innerNode.toElement().text();
849 238 : StringList strList = str.split(u' ');
850 238 : GPTrack::RSE rse;
851 238 : rse.pan = strList.at(11).toFloat();
852 238 : rse.volume = strList.at(12).toFloat();
853 238 : return rse;
854 238 : }
855 : }
856 :
857 0 : return GPTrack::RSE();
858 238 : }
859 :
860 695 : GPMasterBar::KeySig GP67DomBuilder::readKeySig(XmlDomNode* keyNode) const
861 : {
862 695 : const auto& accidentalCount = keyNode->firstChildElement("AccidentalCount");
863 695 : const auto& modeNode = keyNode->firstChildElement("Mode");
864 :
865 695 : String modeName = modeNode.toElement().text();
866 :
867 695 : GPMasterBar::KeySig::Mode mode = GPMasterBar::KeySig::Mode::Major;
868 695 : if (modeName == "Minor") {
869 56 : mode = GPMasterBar::KeySig::Mode::Minor;
870 : }
871 :
872 695 : int keyCount = accidentalCount.toElement().text().toInt();
873 :
874 695 : return GPMasterBar::KeySig{ GPMasterBar::KeySig::Accidentals(keyCount), mode };
875 695 : }
876 :
877 695 : bool GP67DomBuilder::readUseFlats(XmlDomNode* keyNode) const
878 : {
879 695 : const auto& transposeAs = keyNode->firstChildElement("TransposeAs");
880 695 : if (transposeAs.isNull()) {
881 457 : return false;
882 : }
883 238 : return transposeAs.toElement().text() == "Flats";
884 695 : }
885 :
886 695 : GPMasterBar::TimeSig GP67DomBuilder::readTimeSig(XmlDomNode* timeNode) const
887 : {
888 695 : const String time = timeNode->toElement().text();
889 695 : const StringList timeSig = time.split(u'/');
890 695 : GPMasterBar::TimeSig sig { timeSig.at(0).toInt(), timeSig.at(1).toInt() };
891 695 : return sig;
892 695 : }
893 :
894 2 : void GP67DomBuilder::readNoteXProperties(const XmlDomNode& propertiesNode, GPNote* note)
895 : {
896 2 : auto propertyNode = propertiesNode.firstChild();
897 :
898 4 : while (!propertyNode.isNull()) {
899 2 : int propertyId = propertyNode.toElement().attribute("id").value().toInt();
900 :
901 2 : if (propertyId == 688062467) {
902 2 : note->setTrillSpeed(propertyNode.firstChild().toElement().text().toInt());
903 : }
904 :
905 2 : propertyNode = propertyNode.nextSibling();
906 : }
907 2 : }
908 :
909 1340 : void GP67DomBuilder::readNoteProperties(XmlDomNode* propertiesNode, GPNote* note)
910 : {
911 1340 : std::unordered_set<std::unique_ptr<INoteProperty> > properties;
912 :
913 1340 : auto propertyNode = propertiesNode->firstChild();
914 :
915 7961 : while (!propertyNode.isNull()) {
916 6621 : auto propertyName = propertyNode.toElement().attribute("name").value();
917 :
918 6621 : if (propertyName == u"Midi") {
919 995 : int midi = propertyNode.firstChild().toElement().text().toInt();
920 995 : note->setMidi(midi);
921 : }
922 6621 : if (propertyName == u"Variation") {
923 0 : note->setVariation(propertyNode.firstChild().toElement().text().toInt());
924 : }
925 6621 : if (propertyName == u"Element") {
926 0 : note->setElement(propertyNode.firstChild().toElement().text().toInt());
927 6621 : } else if (propertyName == u"String") {
928 1329 : int string = propertyNode.firstChild().toElement().text().toInt();
929 1329 : note->setString(string);
930 5292 : } else if (propertyName == u"Fret") {
931 1329 : int fret = propertyNode.firstChild().toElement().text().toInt();
932 1329 : note->setFret(fret);
933 3963 : } else if (propertyName == u"Octave") {
934 11 : int octave = propertyNode.firstChild().toElement().text().toInt();
935 11 : note->setOctave(octave);
936 3952 : } else if (propertyName == u"ConcertPitch") {
937 995 : auto pitchNode = propertyNode.firstChild();
938 995 : auto innerNode = pitchNode.firstChild();
939 3980 : while (!innerNode.isNull()) {
940 2985 : String nodeName = innerNode.nodeName();
941 2985 : if (nodeName == u"Accidental") {
942 : std::map<String, int> accidentals = {
943 0 : { u"bb", -2 },
944 0 : { u"b", -1 },
945 0 : { u"#", +1 },
946 0 : { u"x", +2 },
947 5970 : };
948 :
949 995 : String accidentalName = innerNode.toElement().text();
950 995 : if (!accidentalName.isEmpty() && accidentals.find(accidentalName) != accidentals.end()) {
951 96 : note->setAccidental(accidentals[accidentalName]);
952 : } else {
953 899 : note->setAccidental(0);
954 : }
955 995 : }
956 :
957 2985 : innerNode = innerNode.nextSibling();
958 2985 : }
959 3952 : } else if (propertyName == u"Tone") {
960 11 : int tone = propertyNode.firstChild().toElement().text().toInt();
961 11 : note->setTone(tone);
962 2946 : } else if (propertyName == u"Bended") {
963 94 : if (propertyNode.firstChild().nodeName() == "Enable") {
964 94 : note->setBend(createBend(&propertyNode));
965 : }
966 2852 : } else if (propertyName == u"Harmonic"
967 2819 : || propertyName == u"HarmonicFret"
968 5671 : || propertyName == u"HarmonicType") {
969 115 : readHarmonic(&propertyNode, note);
970 2737 : } else if (propertyName == u"PalmMuted") {
971 3 : if (propertyNode.firstChild().nodeName() == "Enable") {
972 3 : note->setPalmMute(true);
973 : }
974 2734 : } else if (propertyName == u"Muted") {
975 : //! property muted in GP means dead note
976 9 : if (propertyNode.firstChild().nodeName() == "Enable") {
977 9 : note->setMute(true);
978 : }
979 2725 : } else if (propertyName == u"Slide") {
980 45 : int slideInfo = propertyNode.firstChild().toElement().text().toUInt();
981 45 : switch (slideInfo) {
982 1 : case 64:
983 1 : note->setPickScrape(GPNote::PickScrape::Down);
984 1 : break;
985 0 : case 128:
986 0 : note->setPickScrape(GPNote::PickScrape::Up);
987 0 : break;
988 44 : default:
989 44 : note->setSlides(slideInfo);
990 44 : break;
991 : }
992 2680 : } else if (propertyName == u"HopoOrigin") {
993 35 : note->setHammerOn(GPNote::HammerOn::Start);
994 2645 : } else if (propertyName == u"Tapped") {
995 2 : if (propertyNode.firstChild().nodeName() == "Enable") {
996 2 : note->setRightHandTapping(true);
997 : }
998 2643 : } else if (propertyName == u"LeftHandTapped") {
999 0 : if (propertyNode.firstChild().nodeName() == "Enable") {
1000 0 : note->setLeftHandTapped(true);
1001 : }
1002 2643 : } else if (propertyName == "ShowStringNumber") {
1003 0 : note->setShowStringNumber(true);
1004 : }
1005 :
1006 6621 : propertyNode = propertyNode.nextSibling();
1007 6621 : }
1008 :
1009 1340 : note->addProperties(std::move(properties));
1010 2680 : return;
1011 2335 : }
1012 :
1013 42 : void GP67DomBuilder::readBeatXProperties(const XmlDomNode& propertiesNode, GPBeat* beat)
1014 : {
1015 42 : auto propertyNode = propertiesNode.firstChild();
1016 :
1017 42 : bool brokenBeams = false;
1018 42 : bool brokenSecondaryBeams = false;
1019 42 : bool joinedBeams = false;
1020 :
1021 92 : while (!propertyNode.isNull()) {
1022 50 : int propertyId = propertyNode.toElement().attribute("id").value().toInt();
1023 :
1024 50 : if (propertyId == 687931393 || propertyId == 687935489) {
1025 : // arpeggio/brush ticks
1026 8 : beat->setArpeggioStretch(propertyNode.firstChild().toElement().text().toDouble() / mu::engraving::Constants::DIVISION);
1027 42 : } else if (propertyId == 1124204546) {
1028 22 : int beamData = propertyNode.firstChild().toElement().text().toInt();
1029 :
1030 22 : if (beamData == 1) {
1031 11 : joinedBeams = true;
1032 11 : } else if (beamData == 2) {
1033 11 : brokenBeams = true;
1034 : }
1035 20 : } else if (propertyId == 1124204552) {
1036 7 : int beamData = propertyNode.firstChild().toElement().text().toInt();
1037 7 : if (beamData == 1) {
1038 7 : brokenSecondaryBeams = true;
1039 : }
1040 : }
1041 :
1042 50 : propertyNode = propertyNode.nextSibling();
1043 : }
1044 :
1045 42 : if (brokenBeams) {
1046 11 : beat->setBeamMode(GPBeat::BeamMode::BROKEN);
1047 31 : } else if (brokenSecondaryBeams) {
1048 7 : beat->setBeamMode(joinedBeams ? GPBeat::BeamMode::BROKEN2_JOINED : GPBeat::BeamMode::BROKEN2);
1049 24 : } else if (joinedBeams) {
1050 11 : beat->setBeamMode(GPBeat::BeamMode::JOINED);
1051 : }
1052 42 : }
1053 :
1054 74 : GPBeat::Whammy GP67DomBuilder::readWhammy(const XmlDomElement& elem) const
1055 : {
1056 74 : GPBeat::Whammy whammy;
1057 74 : whammy.originValue = elem.attribute("originValue").value().toFloat();
1058 74 : whammy.destinationValue = elem.attribute("destinationValue").value().toFloat();
1059 74 : whammy.middleValue = elem.attribute("middleValue").value().toFloat();
1060 74 : whammy.originOffset = elem.attribute("originOffset").value().toFloat();
1061 74 : whammy.middleOffset1 = elem.attribute("middleOffset1").value().toFloat();
1062 74 : whammy.middleOffset2 = elem.attribute("middleOffset2").value().toFloat();
1063 74 : whammy.destinationOffset = elem.attribute("destinationOffset").value().toFloat();
1064 74 : return whammy;
1065 : }
1066 :
1067 94 : std::unique_ptr<GPNote::Bend> GP67DomBuilder::createBend(XmlDomNode* propertyNode)
1068 : {
1069 94 : std::unique_ptr<GPNote::Bend> bend = std::make_unique<GPNote::Bend>();
1070 :
1071 94 : auto currentNode = propertyNode->nextSibling();
1072 1146 : while (!currentNode.isNull()) {
1073 1052 : String propertyName = currentNode.toElement().attribute("name").value();
1074 1052 : if (propertyName == u"BendDestinationOffset") {
1075 86 : bend->destinationOffset = currentNode.firstChild().toElement().text().toFloat();
1076 966 : } else if (propertyName == u"BendDestinationValue") {
1077 94 : bend->destinationValue = currentNode.firstChild().toElement().text().toFloat();
1078 872 : } else if (propertyName == u"BendMiddleOffset1") {
1079 86 : bend->middleOffset1 = currentNode.firstChild().toElement().text().toFloat();
1080 786 : } else if (propertyName == u"BendMiddleOffset2") {
1081 86 : bend->middleOffset2 = currentNode.firstChild().toElement().text().toFloat();
1082 700 : } else if (propertyName == u"BendMiddleValue") {
1083 87 : bend->middleValue = currentNode.firstChild().toElement().text().toFloat();
1084 613 : } else if (propertyName == u"BendOriginOffset") {
1085 86 : bend->originOffset = currentNode.firstChild().toElement().text().toFloat();
1086 527 : } else if (propertyName == u"BendOriginValue") {
1087 94 : bend->originValue = currentNode.firstChild().toElement().text().toFloat();
1088 : }
1089 :
1090 1052 : currentNode = currentNode.nextSibling();
1091 1052 : }
1092 :
1093 94 : return bend;
1094 94 : }
1095 :
1096 115 : void GP67DomBuilder::readHarmonic(XmlDomNode* propertyNode, GPNote* note) const
1097 : {
1098 41 : auto harmonicType = [](const String& str) {
1099 41 : if (str == u"Artificial") {
1100 21 : return GPNote::Harmonic::Type::Artificial;
1101 20 : } else if (str == u"Semi") {
1102 4 : return GPNote::Harmonic::Type::Semi;
1103 16 : } else if (str == u"Pinch") {
1104 2 : return GPNote::Harmonic::Type::Pinch;
1105 14 : } else if (str == u"Feedback") {
1106 8 : return GPNote::Harmonic::Type::FeedBack;
1107 6 : } else if (str == u"Natural") {
1108 3 : return GPNote::Harmonic::Type::Natural;
1109 : } else {
1110 3 : return GPNote::Harmonic::Type::Tap;
1111 : }
1112 : };
1113 :
1114 115 : String propertyName = propertyNode->toElement().attribute("name").value();
1115 115 : if (propertyName == u"HarmonicFret") {
1116 41 : note->setHarmonicFret(propertyNode->firstChild().toElement().text().toFloat());
1117 74 : } else if (propertyName == u"HarmonicType") {
1118 41 : note->setHarmonicType(harmonicType(propertyNode->firstChild().toElement().text()));
1119 : }
1120 115 : }
1121 :
1122 1480 : void GP67DomBuilder::readBeatProperties(const XmlDomNode& propertiesNode, GPBeat* beat) const
1123 : {
1124 4 : auto brushType = [](const String& brush) {
1125 4 : if (brush == u"Down") {
1126 2 : return GPBeat::Brush::Down;
1127 : }
1128 2 : return GPBeat::Brush::Up;
1129 : };
1130 8 : auto vibratoType = [](const String& str) {
1131 8 : if (str == u"Wide") {
1132 3 : return GPBeat::VibratoWTremBar::Wide;
1133 5 : } else if (str == u"Slight") {
1134 5 : return GPBeat::VibratoWTremBar::Slight;
1135 : } else {
1136 0 : return GPBeat::VibratoWTremBar::None;
1137 : }
1138 : };
1139 5 : auto rasgueadoType = [](const String& str) {
1140 5 : if (str == u"ii_1") {
1141 0 : return GPBeat::Rasgueado::II_1;
1142 5 : } else if (str == u"mi_1") {
1143 0 : return GPBeat::Rasgueado::MII_1;
1144 5 : } else if (str == u"mii_1") {
1145 0 : return GPBeat::Rasgueado::MII_1;
1146 5 : } else if (str == u"mii_2") {
1147 0 : return GPBeat::Rasgueado::MII_2;
1148 5 : } else if (str == u"ami_1") {
1149 0 : return GPBeat::Rasgueado::AMI_1;
1150 5 : } else if (str == u"ami_2") {
1151 0 : return GPBeat::Rasgueado::AMI_2;
1152 5 : } else if (str == u"pai_1") {
1153 3 : return GPBeat::Rasgueado::PAI_1;
1154 2 : } else if (str == u"pai_2") {
1155 0 : return GPBeat::Rasgueado::PAI_2;
1156 2 : } else if (str == u"pei_1") {
1157 0 : return GPBeat::Rasgueado::PEI_1;
1158 2 : } else if (str == u"pei_2") {
1159 0 : return GPBeat::Rasgueado::PEI_2;
1160 2 : } else if (str == u"pmp_1") {
1161 0 : return GPBeat::Rasgueado::PMP_1;
1162 2 : } else if (str == u"pmp_2") {
1163 0 : return GPBeat::Rasgueado::PMP_2;
1164 2 : } else if (str == u"ppp_1") {
1165 0 : return GPBeat::Rasgueado::PPP_1;
1166 2 : } else if (str == u"amii_1") {
1167 0 : return GPBeat::Rasgueado::AMII_1;
1168 2 : } else if (str == u"amip_1") {
1169 0 : return GPBeat::Rasgueado::AMIP_1;
1170 2 : } else if (str == u"eami_1") {
1171 2 : return GPBeat::Rasgueado::EAMI_1;
1172 0 : } else if (str == u"eamii_1") {
1173 0 : return GPBeat::Rasgueado::EAMII_1;
1174 0 : } else if (str == u"peami_1") {
1175 0 : return GPBeat::Rasgueado::PEAMI_1;
1176 : } else {
1177 0 : return GPBeat::Rasgueado::None;
1178 : }
1179 : };
1180 4 : auto pickStrokeType = [](const String& str) {
1181 4 : if (str == u"Up") {
1182 2 : return GPBeat::PickStroke::Up;
1183 2 : } else if (str == u"Down") {
1184 2 : return GPBeat::PickStroke::Down;
1185 : }
1186 0 : return GPBeat::PickStroke::None;
1187 : };
1188 :
1189 1480 : GPBeat::Whammy whammyData;
1190 1480 : bool hasWhammyMarker = false;
1191 1480 : auto propertyNode = propertiesNode.firstChild();
1192 :
1193 3816 : while (!propertyNode.isNull()) {
1194 2336 : String propertyName = propertyNode.toElement().attribute("name").value();
1195 :
1196 2336 : if (propertyName == u"Popped") {
1197 6 : if (propertyNode.firstChild().nodeName() == "Enable") {
1198 6 : beat->setPopped(true);
1199 : }
1200 2330 : } else if (propertyName == u"Slapped") {
1201 7 : if (propertyNode.firstChild().nodeName() == "Enable") {
1202 7 : beat->setSlapped(true);
1203 : }
1204 2323 : } else if (propertyName == u"Brush") {
1205 4 : beat->setBrush(brushType(propertyNode.firstChild().toElement().text()));
1206 2319 : } else if (propertyName == u"VibratoWTremBar") {
1207 8 : beat->setVibratoWTremBar(vibratoType(propertyNode.firstChild().toElement().text()));
1208 2311 : } else if (propertyName == u"Rasgueado") {
1209 5 : beat->setRasgueado(rasgueadoType(propertyNode.firstChild().toElement().text()));
1210 2306 : } else if (propertyName == u"PickStroke") {
1211 4 : beat->setPickStroke(pickStrokeType(propertyNode.firstChild().toElement().text()));
1212 2302 : } else if (propertyName == u"BarreFret") {
1213 4 : beat->setBarreFret(propertyNode.firstChild().toElement().text().toInt());
1214 2298 : } else if (propertyName == u"BarreString") {
1215 3 : beat->setBarreString(propertyNode.firstChild().toElement().text().toInt());
1216 2295 : } else if (propertyName == u"WhammyBar") {
1217 12 : hasWhammyMarker = true;
1218 2283 : } else if (propertyName == u"WhammyBarOriginOffset") {
1219 2 : whammyData.originOffset = propertyNode.firstChild().toElement().text().toFloat();
1220 2281 : } else if (propertyName == u"WhammyBarOriginValue") {
1221 62 : whammyData.originValue = propertyNode.firstChild().toElement().text().toFloat();
1222 2219 : } else if (propertyName == u"WhammyBarDestinationValue") {
1223 62 : whammyData.destinationValue = propertyNode.firstChild().toElement().text().toFloat();
1224 2157 : } else if (propertyName == u"WhammyBarMiddleValue") {
1225 60 : whammyData.middleValue = propertyNode.firstChild().toElement().text().toFloat();
1226 2097 : } else if (propertyName == u"WhammyBarDestinationOffset") {
1227 5 : whammyData.destinationOffset = propertyNode.firstChild().toElement().text().toFloat();
1228 2092 : } else if (propertyName == u"WhammyBarMiddleOffset1") {
1229 5 : whammyData.middleOffset1 = propertyNode.firstChild().toElement().text().toFloat();
1230 2087 : } else if (propertyName == u"WhammyBarMiddleOffset2") {
1231 5 : whammyData.middleOffset2 = propertyNode.firstChild().toElement().text().toFloat();
1232 : }
1233 :
1234 2336 : propertyNode = propertyNode.nextSibling();
1235 2336 : }
1236 :
1237 1480 : if (hasWhammyMarker && !whammyData.isEmpty() && !beat->hasWhammy()) {
1238 6 : beat->setWhammy(whammyData);
1239 : }
1240 1480 : }
1241 :
1242 241 : void GP67DomBuilder::readTrackProperties(XmlDomNode* propertiesNode, GPTrack* track, bool ignoreTuningFlats) const
1243 : {
1244 241 : GPTrack::StaffProperties properties;
1245 241 : properties.ignoreFlats = ignoreTuningFlats;
1246 :
1247 241 : auto propertyNode = propertiesNode->firstChild();
1248 :
1249 2075 : while (!propertyNode.isNull()) {
1250 1834 : String propertyName = propertyNode.toElement().attribute("name").value();
1251 :
1252 1834 : if (propertyName == u"CapoFret") {
1253 159 : properties.capoFret = propertyNode.firstChild().toElement().text().toInt();
1254 1675 : } else if (propertyName == u"FretCount") {
1255 159 : properties.fretCount = propertyNode.firstChild().toElement().text().toInt();
1256 1516 : } else if (propertyName == "Tuning") {
1257 239 : String tuningStr = propertyNode.firstChildElement("Pitches").text();
1258 239 : std::vector<int> tunning;
1259 239 : tunning.reserve(6);
1260 1668 : for (const String& val : tuningStr.split(u' ')) {
1261 1429 : tunning.push_back(val.toInt());
1262 239 : }
1263 239 : properties.tuning.swap(tunning);
1264 239 : properties.useFlats = !propertyNode.firstChildElement("Flat").isNull();
1265 1516 : } else if (propertyName == u"TuningFlat") {
1266 57 : properties.useFlats = !propertyNode.firstChildElement("Enable").isNull();
1267 1220 : } else if (propertyName == u"DiagramCollection" || propertyName == u"DiagramWorkingSet") {
1268 476 : readDiagram(propertyNode.firstChild(), track);
1269 : }
1270 :
1271 1834 : propertyNode = propertyNode.nextSibling();
1272 1834 : }
1273 :
1274 241 : track->setStaffProperties(properties);
1275 241 : }
1276 :
1277 476 : void GP67DomBuilder::readDiagram(const XmlDomNode& items, GPTrack* track) const
1278 : {
1279 476 : auto item = items.firstChild();
1280 :
1281 503 : while (!item.isNull()) {
1282 27 : GPTrack::Diagram diagram;
1283 :
1284 27 : diagram.id = item.toElement().attribute("id").value().toInt();
1285 27 : diagram.name = item.toElement().attribute("name").value();
1286 :
1287 27 : auto diagramNode = item.firstChild();
1288 27 : diagram.stringCount = diagramNode.toElement().attribute("stringCount").value().toInt();
1289 27 : diagram.fretCount = diagramNode.toElement().attribute("fretCount").value().toInt();
1290 27 : diagram.baseFret = diagramNode.toElement().attribute("baseFret").value().toInt();
1291 :
1292 27 : auto fretNode = diagramNode.firstChild();
1293 239 : while (!fretNode.isNull()) {
1294 212 : if (fretNode.nodeName() == "Fret") {
1295 132 : int string = fretNode.toElement().attribute("string").value().toInt();
1296 132 : int fret = fretNode.toElement().attribute("fret").value().toInt();
1297 132 : diagram.frets[string] = fret;
1298 : }
1299 :
1300 212 : fretNode = fretNode.nextSibling();
1301 : }
1302 :
1303 27 : track->addDiagram(std::make_pair(diagram.id, diagram));
1304 :
1305 27 : item = item.nextSibling();
1306 27 : }
1307 476 : }
1308 :
1309 238 : void GP67DomBuilder::readLyrics(const XmlDomNode& items, GPTrack* track) const
1310 : {
1311 238 : XmlDomNode lineNode = items.firstChildElement("Line");
1312 :
1313 : // This code doesn't support multiple lines of lyrics.
1314 238 : if (!lineNode.isNull()) {
1315 238 : auto textNode = lineNode.firstChildElement("Text");
1316 238 : if (!textNode.isNull()) {
1317 238 : track->setLyrics(textNode.toElement().text().toStdString());
1318 : }
1319 :
1320 238 : auto offsetNode = lineNode.firstChildElement("Offset");
1321 238 : if (!offsetNode.isNull()) {
1322 238 : track->setLyricsOffset(offsetNode.toElement().text().toInt());
1323 : }
1324 238 : }
1325 238 : }
1326 :
1327 4 : std::vector<int> GP67DomBuilder::readEnding(XmlDomNode* endNode) const
1328 : {
1329 4 : String str = endNode->toElement().text();
1330 4 : StringList strList = str.split(u' ');
1331 4 : std::vector<int> ending;
1332 4 : ending.reserve(strList.size());
1333 10 : for (const String& val : strList) {
1334 6 : ending.push_back(val.toInt());
1335 : }
1336 :
1337 4 : return ending;
1338 4 : }
1339 :
1340 8 : GPMasterBar::Repeat GP67DomBuilder::readRepeat(XmlDomNode* repeatNode) const
1341 : {
1342 8 : auto repeatType = [](const String& start, const String& end) {
1343 8 : if (start == u"true" && end == u"false") {
1344 2 : return GPMasterBar::Repeat::Type::Start;
1345 6 : } else if (start == u"false" && end == u"true") {
1346 6 : return GPMasterBar::Repeat::Type::End;
1347 0 : } else if (start == u"true" && end == u"true") {
1348 0 : return GPMasterBar::Repeat::Type::StartEnd;
1349 : }
1350 0 : return GPMasterBar::Repeat::Type::None;
1351 : };
1352 :
1353 8 : String start = repeatNode->toElement().attribute("start").value();
1354 8 : String end = repeatNode->toElement().attribute("end").value();
1355 8 : int count = repeatNode->toElement().attribute("count").value().toInt();
1356 :
1357 8 : GPMasterBar::Repeat repeat{ repeatType(start, end), count };
1358 8 : return repeat;
1359 8 : }
1360 :
1361 : std::pair<String, String>
1362 3 : GP67DomBuilder::readMasterBarSection(const XmlDomNode& sectionNode) const
1363 : {
1364 3 : std::pair<String, String> section;
1365 :
1366 3 : auto node = sectionNode.firstChild();
1367 :
1368 9 : while (!node.isNull()) {
1369 6 : String nodeName = node.nodeName();
1370 :
1371 6 : if (nodeName == u"Letter") {
1372 3 : section.first = node.toElement().text();
1373 3 : } else if (nodeName == u"Text") {
1374 3 : section.second = node.toElement().text();
1375 : }
1376 :
1377 6 : node = node.nextSibling();
1378 6 : }
1379 :
1380 3 : return section;
1381 3 : }
1382 :
1383 6 : std::vector<GPMasterBar::Fermata> GP67DomBuilder::readFermatas(XmlDomNode* fermatasNode) const
1384 : {
1385 10 : auto fermataType = [](const String& str) {
1386 10 : if (str == u"Short") {
1387 0 : return GPMasterBar::Fermata::Type::Short;
1388 10 : } else if (str == u"Medium") {
1389 10 : return GPMasterBar::Fermata::Type::Medium;
1390 : }
1391 0 : return GPMasterBar::Fermata::Type::Long;
1392 : };
1393 :
1394 6 : std::vector<GPMasterBar::Fermata> fermatas;
1395 :
1396 6 : auto fermataNode = fermatasNode->firstChild();
1397 :
1398 16 : while (!fermataNode.isNull()) {
1399 10 : GPMasterBar::Fermata fermata;
1400 :
1401 10 : auto fermataProperty = fermataNode.firstChild();
1402 40 : while (!fermataProperty.isNull()) {
1403 30 : String nodeName = fermataProperty.nodeName();
1404 30 : if (nodeName == u"Type") {
1405 10 : fermata.type = fermataType(fermataProperty.toElement().text());
1406 20 : } else if (nodeName == u"Offset") {
1407 10 : String str = fermataProperty.toElement().text();
1408 10 : StringList numbers = str.split(u'/');
1409 10 : fermata.offsetNum = numbers.at(0).toInt();
1410 10 : fermata.offsetDenom = numbers.at(1).toInt();
1411 20 : } else if (nodeName == u"Length") {
1412 10 : fermata.length = fermataProperty.toElement().text().toFloat();
1413 : }
1414 :
1415 30 : fermataProperty = fermataProperty.nextSibling();
1416 30 : }
1417 :
1418 10 : fermatas.push_back(fermata);
1419 10 : fermataNode = fermataNode.nextSibling();
1420 10 : }
1421 :
1422 6 : return fermatas;
1423 6 : }
1424 :
1425 36 : std::vector<GPMasterBar::Direction> GP67DomBuilder::readRepeatsJumps(XmlDomNode* repeatsJumpsNode) const
1426 : {
1427 36 : std::vector<GPMasterBar::Direction> repeatsJumps;
1428 :
1429 36 : auto innerNode = repeatsJumpsNode->firstChild();
1430 :
1431 72 : while (!innerNode.isNull()) {
1432 36 : GPMasterBar::Direction repeatJump;
1433 36 : repeatJump.type = (innerNode.nodeName() == "Jump" ? GPMasterBar::Direction::Type::Jump : GPMasterBar::Direction::Type::Repeat);
1434 36 : repeatJump.name = innerNode.toElement().text();
1435 :
1436 : // GP encodes "To Coda" instructions as Jumps, but MuseScore uses Markers for that
1437 36 : if ((repeatJump.name == u"DaCoda") || (repeatJump.name == u"DaDoubleCoda")) {
1438 4 : repeatJump.type = GPMasterBar::Direction::Type::Marker;
1439 : }
1440 :
1441 36 : repeatsJumps.push_back(repeatJump);
1442 36 : innerNode = innerNode.nextSibling();
1443 36 : }
1444 :
1445 36 : return repeatsJumps;
1446 36 : }
1447 : } // namespace mu::iex::guitarpro
|