1 /*
2 MusicXML Library
3 Copyright (C) Grame 2006-2013
4
5 This Source Code Form is subject to the terms of the Mozilla Public
6 License, v. 2.0. If a copy of the MPL was not distributed with this
7 file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9 Grame Research Laboratory, 11, cours de Verdun Gensoul 69002 Lyon - France
10 research@grame.fr
11 */
12
13 #include <algorithm>
14 #include <iostream>
15 #include <sstream>
16 #include <string>
17 #include <cmath>
18
19
20 #include "conversions.h"
21 #include "partsummary.h"
22 #include "rational.h"
23 #include "xml2guidovisitor.h"
24 #include "metronomevisitor.h"
25 #include "xmlpart2guido.h"
26 #include "xml_tree_browser.h"
27
28 using namespace std;
29
30 namespace MusicXML2
31 {
32
33 //______________________________________________________________________________
xmlpart2guido(bool generateComments,bool generateStem,bool generateBar)34 xmlpart2guido::xmlpart2guido(bool generateComments, bool generateStem, bool generateBar) :
35 fGenerateComments(generateComments), //fGenerateStem(generateStem),
36 fGenerateBars(generateBar),
37 fNotesOnly(false), fCurrentStaffIndex(0), fCurrentStaff(0),
38 fTargetStaff(0), fTargetVoice(0)
39 {
40 fGeneratePositions = true;
41 fGenerateAutoMeasureNum = true;
42 xmlpart2guido::reset();
43 fHasLyrics = false;
44 fNonStandardNoteHead = false;
45 fLyricsManualSpacing = false;
46 fIgnoreWedgeWithOffset = false;
47 fTupletOpen = 0;
48 fTremoloInProgress = false;
49 }
50
51 //______________________________________________________________________________
reset()52 void xmlpart2guido::reset ()
53 {
54 guidonotestatus::resetall();
55 fCurrentBeamNumber = 0;
56 fMeasNum = 0;
57 fInCue = fInGrace = fInhibitNextBar = fPendingBar = fDoubleBar
58 = fBeamOpened = fCrescPending = fSkipDirection = fWavyTrillOpened = fSingleScopeTrill = fNonStandardNoteHead = false;
59 fCurrentStemDirection = kStemUndefined;
60 fCurrentDivision = 1;
61 fCurrentOffset = 0;
62 fPendingPops = 0;
63 fMeasNum = 0;
64 fLyricsManualSpacing = false;
65 fTextTagOpen = 0;
66 fIgnoreWedgeWithOffset = false;
67 fTupletOpen = 0;
68 fTremoloInProgress = false;
69 }
70
71 //______________________________________________________________________________
initialize(Sguidoelement seq,int staff,int guidostaff,int voice,bool notesonly,rational defaultTimeSign)72 void xmlpart2guido::initialize (Sguidoelement seq, int staff, int guidostaff, int voice,
73 bool notesonly, rational defaultTimeSign)
74 {
75 fCurrentStaff = fTargetStaff = staff; // the current and target staff
76 fTargetVoice = voice; // the target voice
77 fNotesOnly = notesonly; // prevent multiple output for keys, clefs etc...
78 fCurrentTimeSign = defaultTimeSign; // a default time signature
79 fCurrentStaffIndex = guidostaff; // the current guido staff index
80 fHasLyrics = false;
81 fLyricsManualSpacing = false;
82 fIgnoreWedgeWithOffset = false;
83 fTupletOpen = 0;
84 fTremoloInProgress = false;
85 start (seq);
86 }
87
88 //________________________________________________________________________
89 // some code for the delayed elements management
90 // delayed elements are elements enclosed in a <direction> element that
91 // contains a non-null <offset> element. This offset postpone the graphic
92 // appearance of the element in 'offset' time units in the futur.
93 // Time units are <division> time units
94 //________________________________________________________________________
95 // add an element to the list of delayed elements
addDelayed(Sguidoelement elt,long offset)96 void xmlpart2guido::addDelayed (Sguidoelement elt, long offset)
97 {
98 add(elt);
99 return;
100
101 if (offset > 0) {
102 delayedElement de;
103 de.delay = offset;
104 de.element = elt;
105 fDelayed.push_back(de);
106 }
107 else add (elt);
108 }
109
110 //________________________________________________________________________
111 // checks ready elements in the list of delayed elements
112 // 'time' is the time elapsed since the last check, it is expressed in
113 // <division> time units
checkDelayed(long time)114 void xmlpart2guido::checkDelayed (long time)
115 {
116 vector<delayedElement>::iterator it = fDelayed.begin();
117 while (it!=fDelayed.end()) {
118 it->delay -= time;
119 if (it->delay < 0) {
120 add (it->element);
121 it = fDelayed.erase(it);
122 }
123 else it++;
124 }
125 }
126
127 //______________________________________________________________________________
stackClean()128 void xmlpart2guido::stackClean ()
129 {
130 if (fInCue) {
131 pop();
132 fInCue = false;
133
134 // add EMPTY if durationInCue>0 and fCurrentMeasurePosition is not equal to fCurrentMeasureLength
135 durationInCue.rationalise();
136 if (durationInCue.getNumerator() > 0) {
137 guidonoteduration dur (durationInCue.getNumerator(), durationInCue.getDenominator());
138 Sguidoelement note = guidonote::create(fTargetVoice, "empty", 0, dur, "");
139 add (note);
140 fCurrentVoicePosition += durationInCue;
141 fCurrentVoicePosition.rationalise();
142 }
143
144 durationInCue = 0;
145 }
146 if (fInGrace) {
147 pop();
148 fInGrace = false;
149 }
150 if (fTextTagOpen>0) {
151 while (fTextTagOpen>0) {
152 pop();
153 fTextTagOpen--;
154 }
155 }
156 }
157
158 //______________________________________________________________________________
checkStaff(int staff)159 void xmlpart2guido::checkStaff (int staff) {
160 if (staff != fCurrentStaff) {
161 Sguidoelement tag = guidotag::create("staff");
162 int offset = staff - fCurrentStaff;
163 fCurrentStaff = staff;
164 fCurrentStaffIndex += offset;
165 tag->add (guidoparam::create(fCurrentStaffIndex, false));
166 add (tag);
167 }
168 }
169
170 //______________________________________________________________________________
moveMeasureTime(int duration,bool moveVoiceToo,int x_default)171 void xmlpart2guido::moveMeasureTime (int duration, bool moveVoiceToo, int x_default)
172 {
173 rational r(duration, fCurrentDivision*4);
174 r.rationalise();
175 fCurrentMeasurePosition += r;
176 fCurrentMeasurePosition.rationalise();
177 if (fCurrentMeasurePosition > fCurrentMeasureLength)
178 fCurrentMeasureLength = fCurrentMeasurePosition;
179 if (moveVoiceToo) {
180 fCurrentVoicePosition += r;
181 fCurrentVoicePosition.rationalise();
182 }
183 }
184
185 //______________________________________________________________________________
186 // check the current position in the current voice: when it lags behind
187 // the current measure position, it creates the corresponding empty element
188 //______________________________________________________________________________
checkVoiceTime(const rational & currTime,const rational & voiceTime)189 void xmlpart2guido::checkVoiceTime ( const rational& currTime, const rational& voiceTime)
190 {
191 rational diff = currTime - voiceTime;
192 diff.rationalise();
193 if (diff.getNumerator() > 0) {
194 guidonoteduration dur (diff.getNumerator(), diff.getDenominator());
195 Sguidoelement note = guidonote::create(fTargetVoice, "empty", 0, dur, "");
196 add (note);
197 fCurrentVoicePosition += diff;
198 fCurrentVoicePosition.rationalise();
199 }
200 else if (diff.getNumerator() < 0)
201 {
202 if (!fInCue)
203 cerr << "warning! checkVoiceTime: measure time behind voice time " << string(diff) << "(measure "<< fMeasNum<<")" << endl;
204 }
205 }
206
207 //______________________________________________________________________________
visitStart(S_backup & elt)208 void xmlpart2guido::visitStart ( S_backup& elt )
209 {
210 stackClean(); // closes pending chords, cue and grace
211 int duration = elt->getIntValue(k_duration, 0);
212 if (duration) {
213 // backup is supposed to be used only for moving between voices
214 // thus we don't move the voice time (which is supposed to be 0)
215 moveMeasureTime (-duration, false);
216 }
217 }
218
219 //______________________________________________________________________________
visitStart(S_forward & elt)220 void xmlpart2guido::visitStart ( S_forward& elt )
221 {
222 bool scanElement = (elt->getIntValue(k_voice, 0) == fTargetVoice)
223 && (elt->getIntValue(k_staff, 0) == fTargetStaff);
224 int duration = elt->getIntValue(k_duration, 0);
225 moveMeasureTime(duration, scanElement);
226 if (!scanElement) return;
227
228 stackClean(); // closes pending chords, cue and grace
229
230 if (duration) {
231 rational r(duration, fCurrentDivision*4);
232 r.rationalise();
233 guidonoteduration dur (r.getNumerator(), r.getDenominator());
234 Sguidoelement note = guidonote::create(fTargetVoice, "empty", 0, dur, "");
235 add (note);
236 fMeasureEmpty = false;
237 }
238 }
239
240 //______________________________________________________________________________
visitStart(S_part & elt)241 void xmlpart2guido::visitStart ( S_part& elt )
242 {
243 reset();
244 if (!current()) {
245 Sguidoelement seq = guidoseq::create();
246 start (seq);
247 }
248 fCurrentPart = elt;
249 }
250
251
252 //______________________________________________________________________________
visitStart(S_measure & elt)253 void xmlpart2guido::visitStart ( S_measure& elt )
254 {
255 const string& implicit = elt->getAttributeValue ("implicit");
256 if (implicit == "yes") fPendingBar = false;
257 if (fPendingBar) {
258 // before adding a bar, we need to check that there are no repeat begin at this location
259 ctree<xmlelement>::iterator repeat = elt->find(k_repeat);
260 if ((repeat == elt->end()) || (repeat->getAttributeValue("direction") != "forward")) {
261 checkStaff (fTargetStaff);
262
263 Sguidoelement tag;
264
265 // Check bar-style if doubleBar
266 if (fDoubleBar)
267 tag = guidotag::create("doubleBar");
268 else
269 tag = guidotag::create("bar");
270
271 add (tag);
272 }
273 }
274 fCurrentMeasure = elt;
275 fMeasNum++;
276 fCurrentMeasureLength.set (0, 1);
277 fCurrentMeasurePosition.set(0, 1);
278 fCurrentVoicePosition.set (0, 1);
279 fInhibitNextBar = false; // fNotesOnly;
280 fPendingBar = false;
281 fDoubleBar = false;
282 fPendingPops = 0;
283 fMeasureEmpty = true;
284 if (fGenerateComments) {
285 stringstream s;
286 s << " (* meas. " << fMeasNum << " *) ";
287 string comment="\n"+s.str();
288 Sguidoelement elt = guidoelement ::create(comment);
289 add (elt);
290 }
291 }
292
293 //______________________________________________________________________________
visitEnd(S_measure & elt)294 void xmlpart2guido::visitEnd ( S_measure& elt )
295 {
296 stackClean(); // closes pending chords, cue and grace
297 checkVoiceTime (fCurrentMeasureLength, fCurrentVoicePosition);
298
299 if (!fInhibitNextBar) {
300 if (fGenerateBars) fPendingBar = true;
301 else if (!fMeasureEmpty) {
302 if (fCurrentVoicePosition < fCurrentMeasureLength)
303 fPendingBar = true;
304 }
305 }
306
307 // Inhibit barline generation next time if bar-style of barline of this measure is "none"
308 ctree<xmlelement>::iterator barStyle = elt->find(k_bar_style);
309 if (barStyle != elt->end())
310 {
311 if (barStyle->getValue() == "none")
312 fPendingBar = false;
313 else if (barStyle->getValue() == "light-light")
314 fDoubleBar = true;
315
316 }
317 }
318
319 //______________________________________________________________________________
visitStart(S_direction & elt)320 void xmlpart2guido::visitStart ( S_direction& elt )
321 {
322 // Parse Staff and Offset first
323 if (fNotesOnly || (elt->getIntValue(k_staff, 0) != fTargetStaff)) {
324 fSkipDirection = true;
325 }
326 else {
327 fCurrentOffset = elt->getLongValue(k_offset, 0);
328 }
329 }
330
visitStart(S_rehearsal & elt)331 void xmlpart2guido::visitStart ( S_rehearsal& elt )
332 {
333 if (fSkipDirection) return;
334
335 string rehearsalValue = elt->getValue();
336 rehearsalValue = "\""+rehearsalValue+"\"";
337
338 string enclosure = elt->getAttributeValue("enclosure");
339 string font_size = elt->getAttributeValue("font-size");
340 string font_weight = elt->getAttributeValue("font-weight");
341 string font_style = elt->getAttributeValue("font-style");
342
343 if (rehearsalValue.size())
344 {
345 //// Using MARK tag:
346 Sguidoelement tag = guidotag::create("mark");
347 if (enclosure.size())
348 {
349 rehearsalValue += ", enclosure=\""+enclosure+"\"";
350 }else
351 {
352 // declare rectangle by default
353 rehearsalValue += ", enclosure=\"square\"";
354 }
355 if (font_size.size())
356 rehearsalValue += ", fsize="+font_size+"pt";
357
358 tag->add (guidoparam::create(rehearsalValue.c_str(), false));
359 //xml2guidovisitor::addPosition(elt, tag, -4, -4);
360 // FIXME: Researsal is a Direction and its x-pos is from the beginning of measure where in Guido it is from current graphical position!
361 float markDx = xPosFromTimePos(elt->getAttributeFloatValue("default-x", 0), elt->getAttributeFloatValue("relative-x", 0));
362 if (markDx != -999) {
363 stringstream s;
364 s << "dx=" << markDx ;
365 tag->add (guidoparam::create(s.str(), false));
366 }
367 xml2guidovisitor::addPosY(elt, tag, -4, 1);
368
369 add (tag);
370 }
371 }
372
373 //______________________________________________________________________________
visitEnd(S_direction & elt)374 void xmlpart2guido::visitEnd ( S_direction& elt )
375 {
376 // !IMPORTANT: Avoid using default-x since it is measured from the beginning of the measure for S_direction!
377
378 if (fSkipDirection) return;
379
380 /// Skip already visited Direction in case of grace notes (GUID-153)
381 if ((!fDirectionEraserStack.empty())) {
382 //cerr<<"\t stack top="<<fDirectionEraserStack.front()<<endl;
383 if (fDirectionEraserStack.front() == elt->getInputLineNumber()) {
384 fDirectionEraserStack.pop();
385 //cerr<<"\tS_direction Skipping"<<endl;
386 return;
387 }
388 }
389
390 // Browse into all S_direction_type elements and parse, by preserving ordering AND grouped direction positions (if missing in proceedings calls)
391 ctree<xmlelement>::literator iter = elt->lbegin();
392
393 /// IMPORTANT: In case of "metronome", there's a coupling of WORDS and METRONOME which leads to ONE guido element. Take this into account.
394 bool generateTempo = false; // Possible Composite generation of Metronome
395 bool generateCompositeDynamic = false; // true if Dynamic and Word are present all together, resulting into one composite Guido Tag
396 string tempoWording;
397 string tempoTextParameters;
398 ctree<xmlelement>::iterator ito;
399 if (elt->find(k_metronome) != elt->end())
400 {
401 generateTempo = true; // this will allow grouping of S_Word and S_Metronome into one tag
402 }
403 /// An S_Word with Sound element in the direction that has a Tempo attribute is NOT a text but a tempo markup
404 if (elt->find(k_sound) != elt->end())
405 {
406 // If "sound" attribute exists then we are in generateTempo
407 if (elt->find(k_sound)->getAttributeIntValue("tempo", 0) > 0) {
408 generateTempo = true;
409 }
410 }
411
412 /* If we have a combination of k_words and k_dynamics, then the ORDER of appearance is important for Parsing:
413 If text is BEFORE dynamics, then impose textformat="rt", dx=-2
414 If text order is AFTER dynamics, then impose textformat="lt", dx=2
415 */
416 Sguidoelement tag;
417 bool generateAfter= false; // hold on to generation until everything is visited
418 /// Check if there's a combination of k_words and k_dynamics
419 if ((elt->find(k_words) != elt->end()) && (elt->find(k_dynamics) != elt->end()) ) {
420 generateCompositeDynamic = true;
421 // find k_dynamics first and search for k_words after.
422 ito = elt->find(k_dynamics);
423 ito++;
424 if (elt->find(k_words, ito) != elt->end()) {
425 // then there is a WORD after Dynamics
426 generateAfter = true;
427 }
428 }
429
430 float commonDy = 0.0; // This is the inherited group Dy
431 string wordParameterBuffer = ""; // used if generateCompositeDynamic
432
433 auto branches = elt->elements();
434
435 for (iter = elt->lbegin(); iter != elt->lend(); iter++) {
436 // S_Direction can accept direction_type, offset, footnote, level, voice, staff
437
438 if ((*iter)->getType() == k_direction_type) {
439
440 ctree<xmlelement>::literator directionTypeElements;
441 for (directionTypeElements = (*iter)->lbegin(); directionTypeElements != (*iter)->lend(); directionTypeElements++) {
442 int elementType = (*directionTypeElements)->getType();
443 auto element = (*directionTypeElements);
444
445 switch (elementType) {
446 case k_words:
447 {
448 if (generateTempo) {
449 tempoWording = element->getValue();
450 }
451
452 string wordPrefix="";
453 // in case of composite Dynamics, detemrine whether text is Before or After
454 if (generateCompositeDynamic) {
455 auto itol = std::find(branches.begin(), branches.end(), *iter);
456 if (itol != branches.end()) {
457 // found current element!
458 // check if there is a dynamic after, if yes then this word is "before"!
459 wordPrefix="after=";
460 for (auto itol2 = itol; itol2 != branches.end(); itol2++) {
461 if ((*itol2)->find(k_dynamics) != (*itol2)->end()) {
462 wordPrefix="before=";
463 }
464 }
465
466 }else {
467 cerr << "UNABLE to find current element in S_direction. PLEASE REPORT!"<<endl;
468 }
469 }
470
471 std::stringstream wordParameters;
472 std::stringstream parameters;
473
474 string font_family = element->getAttributeValue("font-family");
475 string font_size = element->getAttributeValue("font-size");
476 string font_weight = element->getAttributeValue("font-weight");
477 string font_style = element->getAttributeValue("font-style");
478 if (font_family.size())
479 parameters << ",font=\""+font_family+"\"";
480 if (font_size.size())
481 parameters << ",fsize="+font_size+"pt";
482
483 // Add font styles
484 string fattrib;
485 if (font_weight=="bold")
486 fattrib +="b";
487 if (font_style=="italic")
488 fattrib +="i";
489 if (fattrib.size())
490 parameters << ",fattrib=\""+fattrib+"\"";
491
492 if (generateTempo) {
493 // Convert dy to Guido Tempo Tag origin which is +4hs from top of the staff
494 float tempoDy = xml2guidovisitor::getYposition(element, -4, true);
495 parameters << ", dy="<<tempoDy<<"hs";
496 tempoTextParameters = parameters.str();
497 break;
498 }
499
500 wordParameters << wordPrefix <<"\"" << element->getValue() << "\""<< parameters.str();
501
502 if (generateCompositeDynamic) {
503 if (tag) {
504 // This should happen when WORD is After Dynamic
505 tag->add (guidoparam::create(wordParameters.str(), false));
506 }else {
507 wordParameterBuffer= wordParameters.str();
508 }
509 }else {
510 /// Take into account group positioning
511 float posy = xml2guidovisitor::getYposition(element, 0, true);
512 if (posy != 0.0) {
513 // then apply and save
514 commonDy += xml2guidovisitor::getYposition(element, 11.0, true); // Should this be additive?
515 }
516
517 tag = guidotag::create("text");
518 tag->add (guidoparam::create(wordParameters.str(), false));
519
520 // FIXME: XML x-pos is from beginning of measure, whereas nested Text in Guido from the notehead
521 //xml2guidovisitor::addPosX(element, tag, 0);
522
523 // apply inherited Y-position
524 if (commonDy != 0.0) {
525 stringstream s;
526 s << "dy=" << commonDy << "hs";
527 tag->add (guidoparam::create(s.str(), false));
528 }
529
530 push(tag);
531 fTextTagOpen++;
532 }
533
534 break;
535 }
536
537 case k_dynamics:
538 {
539 ctree<xmlelement>::literator iter2;
540 float dynamicsDx = 0.0;
541 /// Take into account group positioning
542 float posy = xml2guidovisitor::getYposition(element, 0, true);
543 if (posy != 0.0) {
544 // then apply and save
545 commonDy += xml2guidovisitor::getYposition(element, 13, true); // Should this be additive?
546 }
547 for (iter2 = element->lbegin(); iter2 != element->lend(); iter2++) {
548 if ((*iter2)->getType() != k_other_dynamics) {
549 tag = guidotag::create("intens");
550 tag->add (guidoparam::create((*iter2)->getName()));
551 float intensDx = xPosFromTimePos(element->getAttributeFloatValue("default-x", 0), element->getAttributeFloatValue("relative-x", 0));
552
553 // add pending word parameters (for "before")
554 if (!generateAfter) {
555 if (wordParameterBuffer.size()) {
556 tag->add (guidoparam::create(wordParameterBuffer, false));
557 wordParameterBuffer = "";
558 }
559
560 // apply inherited Y-position
561 stringstream s;
562 s << "dy=" << commonDy << "hs";
563 tag->add (guidoparam::create(s.str(), false));
564
565 // Apply dx in case of consecutive dynamics (e.g. "sf ff")
566 if (dynamicsDx != 0.0) {
567 stringstream s;
568 s << "dx=" << dynamicsDx << "hs";
569 tag->add (guidoparam::create(s.str(), false));
570 }else if (intensDx != -999) {
571 stringstream s;
572 s << "dx=" << intensDx ;
573 tag->add (guidoparam::create(s.str(), false));
574 }
575
576 /// Add Tag
577 if (fCurrentOffset)
578 addDelayed(tag, fCurrentOffset);
579 else {
580 add(tag);
581 }
582 }
583 dynamicsDx = 4.0; // heuristic HS to avoid collision between consecutive dynamics
584 }
585 }
586
587 break;
588 }
589
590 case k_metronome:
591 {
592 metronomevisitor mv;
593 xml_tree_browser browser(&mv);
594 browser.browse(*elt);
595
596 std::string tempoMetronome = parseMetronome(mv);
597
598 tag = guidotag::create("tempo");
599 stringstream tempoParams;
600 if (tempoWording.size())
601 {
602 tempoParams << "\""<<tempoWording<<" "<<tempoMetronome<<"\"";
603 }
604 else
605 tempoParams << "\""+tempoMetronome+"\"";
606
607 if (tempoTextParameters.size()) {
608 tempoParams << tempoTextParameters;
609 }
610
611 if (tempoParams.str().size())
612 {
613 tag->add (guidoparam::create(tempoParams.str(), false));
614 }
615
616 /// Take into account group positioning
617 float posy = xml2guidovisitor::getYposition(element, 0, true);
618 if (posy != 0.0) {
619 // then apply and save
620 commonDy += xml2guidovisitor::getYposition(element, -4.0, true); // Should this be additive?
621 }
622
623 // apply inherited Y-position
624 if (commonDy != 0.0) {
625 stringstream s;
626 s << "dy=" << commonDy << "hs";
627 tag->add (guidoparam::create(s.str(), false));
628 }
629
630 /// Add Tag
631 if (fCurrentOffset)
632 addDelayed(tag, fCurrentOffset);
633 else {
634 add(tag);
635 }
636
637 generateTempo = false;
638 }
639
640 default:
641 break;
642 }
643 }
644 }
645 }
646
647 // If composed tag, add here
648 if (generateAfter) {
649 // apply inherited Y-position
650 stringstream s;
651 s << "dy=" << commonDy << "hs";
652 tag->add (guidoparam::create(s.str(), false));
653 /// Add Tag
654 if (fCurrentOffset)
655 addDelayed(tag, fCurrentOffset);
656 else {
657 add(tag);
658 }
659 }
660
661 // We get to this block is we have a combination of Words AND Sound only (without Metronome)
662 if (generateTempo && tempoWording.size()) {
663 Sguidoelement tempotag = guidotag::create("tempo");
664 std::stringstream tempoParameters;
665 tempoParameters <<"\"" << tempoWording << "\""<< tempoTextParameters;
666
667 tempotag->add (guidoparam::create(tempoParameters.str(), false));
668
669 if (fCurrentOffset)
670 addDelayed(tempotag, fCurrentOffset);
671 else {
672 add(tempotag);
673 }
674 }
675
676
677 fSkipDirection = false;
678 fCurrentOffset = 0;
679 }
680
681 /// Closing Guido TEXT tags; to be used once a NOTE/CHORD is embedded.
checkTextEnd()682 void xmlpart2guido::checkTextEnd() {
683 if (fTextTagOpen>0) {
684 while (fTextTagOpen>0) {
685 pop();
686 fTextTagOpen--;
687 }
688 }
689 }
690
691 //______________________________________________________________________________
visitEnd(S_key & elt)692 void xmlpart2guido::visitEnd ( S_key& elt )
693 {
694 if (fNotesOnly) return;
695 Sguidoelement tag = guidotag::create("key");
696 tag->add (guidoparam::create(keysignvisitor::fFifths, false));
697 //add (tag);
698 }
699
700 //______________________________________________________________________________
visitStart(S_coda & elt)701 void xmlpart2guido::visitStart ( S_coda& elt )
702 {
703 if (fSkipDirection) return;
704 Sguidoelement tag = guidotag::create("coda");
705 add(tag);
706 }
707
708 //______________________________________________________________________________
visitStart(S_segno & elt)709 void xmlpart2guido::visitStart ( S_segno& elt )
710 {
711 if (fSkipDirection) return;
712 Sguidoelement tag = guidotag::create("segno");
713 add(tag);
714 }
715
716 //______________________________________________________________________________
visitStart(S_wedge & elt)717 void xmlpart2guido::visitStart ( S_wedge& elt )
718 {
719 if (fSkipDirection) return;
720
721 // bool wedgeStart = false;
722
723 string type = elt->getAttributeValue("type");
724 Sguidoelement tag;
725 if (type == "crescendo") {
726 tag = guidotag::create("crescBegin");
727 fCrescPending = true;
728 // wedgeStart = true;
729 }
730 else if (type == "diminuendo") {
731 tag = guidotag::create("dimBegin");
732 fCrescPending = false;
733 // wedgeStart = true;
734 }
735 else if (type == "stop") {
736 if (fIgnoreWedgeWithOffset) {
737 fIgnoreWedgeWithOffset = false;
738 return; // FIXME: Ignore Offset Wedge à la Verovio
739 }
740
741 tag = guidotag::create(fCrescPending ? "crescEnd" : "dimEnd");
742 }
743
744 if (tag) {
745 //// Also add SPREAD values (in mXML tenths - conversion: (X / 10) * 2)
746 //// Spread is present right away for a diminuendo, it'll be present for crescendo at its STOP type
747 if (type == "diminuendo") {
748 float spreadValue = elt->getAttributeFloatValue("spread", 15.0);
749 if (spreadValue != 15.0) {
750 stringstream s;
751 s << "deltaY=" << (spreadValue/10)*2 << "hs";
752 tag->add (guidoparam::create(s.str(), false));
753 }
754
755 /*stringstream s;
756 s << "autopos=\"on\"";
757 tag->add (guidoparam::create(s.str(), false));*/
758
759 }else if (type == "crescendo")
760 {
761 ctree<xmlelement>::iterator wedgeBegin= find(fCurrentPart->begin(), fCurrentPart->end(), elt);
762 int crescendoNumber = elt->getAttributeIntValue("number", 1); // default is 1 for wedge!
763 ctree<xmlelement>::iterator nextevent = wedgeBegin;
764 nextevent++; // advance one step
765
766 // find next S_direction in measure
767 ctree<xmlelement>::iterator nextWedge = fCurrentMeasure->find(k_wedge, nextevent);
768
769 while ( ( nextWedge->getAttributeIntValue("number", 1) != crescendoNumber)
770 &&
771 (nextWedge->getAttributeValue("type")!="stop") )
772 {
773 nextWedge = fCurrentPart->find(k_wedge, nextevent++);
774 }
775
776 ctree<xmlelement>::iterator wedgeEnd = nextWedge;
777
778 /// GUID-88: Avoid parsing dx2 and dx1 on "single-event wedge"
779 int numberOfNotesInWedge = 0;
780 nextevent = wedgeBegin;nextevent++;
781 while ( nextevent != wedgeEnd ) {
782 if (nextevent->getType() == k_note) {
783 if (nextevent->hasSubElement(k_chord)) {
784 // in chord, do not increment
785 }else {
786 numberOfNotesInWedge++;
787 }
788 }
789 nextevent++;
790 }
791
792 //cerr<< "Measure:"<< fMeasNum <<" Wedge has "<< numberOfNotesInWedge<< " events!"<<endl;
793
794 if (fCurrentOffset) {
795 // FIXME: Impossible for now to handle Wedges with Direction Offset! Ignoring... .
796 fIgnoreWedgeWithOffset = true;
797 }
798 if (fIgnoreWedgeWithOffset)
799 {
800 //cerr <<"\tIgnoring Wedge with Offset on measure "<<fMeasNum<<endl;
801 return; // FIXME: Ignoring Offset wedges à la Verovio
802 }
803
804 if (numberOfNotesInWedge > 1) {
805 /// fetch dx1 and dx2 value based on ending
806 float posx1 = elt->getAttributeFloatValue("relative-x", 0); //elt->getAttributeFloatValue("default-x", 0) +
807
808 //// Add dx1 and dx2 parameters
809 if (posx1!=0.0) {
810 posx1 = (posx1 / 10) * 2; // convert to half spaces
811
812 stringstream s;
813 s << "dx1=" << posx1 << "hs";
814 tag->add (guidoparam::create(s.str(), false));
815 }
816
817 /// !Important: the relative-x on the wedge end CAN NOT be directly translated to GMN as it refers to the x-position at the placement of the Wedge Stop. This should be handled by the GDevice in Guido instead.
818 /*
819 float posx2 = nextWedge->getAttributeFloatValue("relative-x", 0);
820 if (posx2!=0.0) {
821 posx2 = (posx2 / 10) * 2; // convert to half spaces
822
823 stringstream s;
824 s << "dx2=" << posx2 << "hs";
825 tag->add (guidoparam::create(s.str(), false));
826 }*/
827 }
828
829
830 //// Add spreadvalue from the Crescendo Ending
831 float spreadValue = nextWedge->getAttributeFloatValue("spread", 15.0);
832 if (spreadValue != 15.0) {
833 stringstream s;
834 s << "deltaY=" << (spreadValue/10)*2 << "hs";
835 tag->add (guidoparam::create(s.str(), false));
836 }
837
838 // Add new AutoPos="on"
839 /*stringstream s;
840 s << "autopos=\"on\"";
841 tag->add (guidoparam::create(s.str(), false));*/
842 }
843
844 stringstream s;
845 s << "dy=" << xml2guidovisitor::getYposition(elt, 13, true) << "hs";
846 tag->add (guidoparam::create(s.str(), false));
847 //xml2guidovisitor::addPosY(elt, tag, -2, 1.0); // removed negative multiplier. Fixed in GuidoLib 1.6.5
848
849 if (fCurrentOffset) {
850 addDelayed(tag, fCurrentOffset);
851 }
852 else {
853 add (tag);
854 }
855 }
856
857 }
858
859
parseMetronome(metronomevisitor & mv)860 std::string xmlpart2guido::parseMetronome ( metronomevisitor &mv )
861 {
862 if (mv.fBeats.size() != 1) return ""; // support per minute tempo only (for now)
863 if (!mv.fPerMinute) return ""; // support per minute tempo only (for now)
864
865 rational r = NoteType::type2rational(NoteType::xml(mv.fBeats[0].fUnit)), rdot(3,2);
866 while (mv.fBeats[0].fDots-- > 0) {
867 r *= rdot;
868 }
869 r.rationalise();
870
871 stringstream s;
872 s << "[" << (string)r << "] = " << mv.fPerMinute;
873 return s.str();
874 }
875
876
877 //______________________________________________________________________________
visitStart(S_octave_shift & elt)878 void xmlpart2guido::visitStart( S_octave_shift& elt)
879 {
880 if (fSkipDirection) return;
881
882 const string& type = elt->getAttributeValue("type");
883 int size = elt->getAttributeIntValue("size", 0);
884
885 switch (size) {
886 case 8: size = 1; break;
887 case 15: size = 2; break;
888 default: return;
889 }
890
891 if (type == "up")
892 size = -size;
893 else if (type == "stop")
894 size = 0;
895 else if (type != "down") return;
896
897 Sguidoelement tag = guidotag::create("oct");
898 if (tag) {
899 tag->add (guidoparam::create(size, false));
900 add (tag);
901 }
902 }
903
904 //______________________________________________________________________________
visitStart(S_note & elt)905 void xmlpart2guido::visitStart ( S_note& elt )
906 {
907 notevisitor::visitStart ( elt );
908 }
909
910 //______________________________________________________________________________
alter2accident(float alter)911 string xmlpart2guido::alter2accident ( float alter )
912 {
913 stringstream s;
914 while (alter > 0.5) {
915 s << "#";
916 alter -= 1;
917 }
918 while (alter < -0.5) {
919 s << "&";
920 alter += 1;
921 }
922
923 string accident;
924 s >> accident;
925 return accident;
926 }
927
928 //______________________________________________________________________________
visitEnd(S_sound & elt)929 void xmlpart2guido::visitEnd ( S_sound& elt )
930 {
931 if (fNotesOnly) return;
932
933 Sguidoelement tag = 0;
934 Sxmlattribute attribute;
935
936 if ((attribute = elt->getAttribute("dacapo")))
937 tag = guidotag::create("daCapo");
938 else {
939 if ((attribute = elt->getAttribute("dalsegno"))) {
940 tag = guidotag::create("dalSegno");
941 }
942 else if ((attribute = elt->getAttribute("tocoda"))) {
943 tag = guidotag::create("daCoda");
944 }
945 else if ((attribute = elt->getAttribute("fine"))) {
946 tag = guidotag::create("fine");
947 }
948 // if (tag) tag->add(guidoparam::create("id="+attribute->getValue(), false));
949 }
950 if (tag) add (tag);
951 }
952
953 //______________________________________________________________________________
visitEnd(S_ending & elt)954 void xmlpart2guido::visitEnd ( S_ending& elt )
955 {
956 string type = elt->getAttributeValue("type");
957 if (type == "start") {
958 Sguidoelement tag = guidotag::create("volta");
959 string num = elt->getAttributeValue ("number");
960 tag->add(guidoparam::create(num, true));
961 tag->add(guidoparam::create(num + ".", true));
962 push(tag);
963 }
964 else {
965 if (type == "discontinue")
966 current()->add(guidoparam::create("format=\"|-\"", false));
967 pop();
968 }
969 }
970
971 //______________________________________________________________________________
visitEnd(S_repeat & elt)972 void xmlpart2guido::visitEnd ( S_repeat& elt )
973 {
974 Sguidoelement tag;
975 string direction = elt->getAttributeValue("direction");
976 if (direction == "forward")
977 tag = guidotag::create("repeatBegin");
978 else if (direction == "backward") {
979 tag = guidotag::create("repeatEnd");
980 fInhibitNextBar = true;
981 }
982 if (tag) add(tag);
983 }
984
985 //______________________________________________________________________________
visitStart(S_barline & elt)986 void xmlpart2guido::visitStart ( S_barline& elt )
987 {
988 const string& location = elt->getAttributeValue("location");
989 if (location == "middle") {
990 // todo: handling bar-style (not yet supported in guido)
991 Sguidoelement tag = guidotag::create("bar");
992 add(tag);
993 }
994 // todo: support for left and right bars
995 // currently automatically handled at measure boundaries
996 else if (location == "right") {
997 }
998 else if (location == "left") {
999 }
1000 }
1001
1002 //______________________________________________________________________________
visitEnd(S_time & elt)1003 void xmlpart2guido::visitEnd ( S_time& elt )
1004 {
1005 string timesign;
1006 if (!timesignvisitor::fSenzaMisura) {
1007 if (timesignvisitor::fSymbol == "common") {
1008 rational ts = timesignvisitor::timesign(0);
1009 if ((ts.getDenominator() == 2) && (ts.getNumerator() == 2))
1010 timesign = "C/";
1011 else if ((ts.getDenominator() == 4) && (ts.getNumerator() == 4))
1012 timesign = "C";
1013 else
1014 timesign = string(ts);
1015 fCurrentTimeSign = ts;
1016 }
1017 else if (timesignvisitor::fSymbol == "cut") {
1018 timesign = "C/";
1019 fCurrentTimeSign = rational(2,2);
1020 }
1021 else {
1022 stringstream s; string sep ="";
1023 fCurrentTimeSign.set(0,1);
1024 for (unsigned int i = 0; i < timesignvisitor::fTimeSign.size(); i++) {
1025 s << sep << timesignvisitor::fTimeSign[i].first << "/" << timesignvisitor::fTimeSign[i].second;
1026 sep = "+";
1027 // rational ts = timesignvisitor::timesign(i);
1028 fCurrentTimeSign += timesignvisitor::timesign(i);
1029 }
1030 s >> timesign;
1031 }
1032
1033 }
1034 if (fNotesOnly) return;
1035
1036 Sguidoelement tag = guidotag::create("meter");
1037 tag->add (guidoparam::create(timesign));
1038 if (fGenerateBars) tag->add (guidoparam::create("autoBarlines=\"off\"", false));
1039 if (fGenerateAutoMeasureNum) tag->add (guidoparam::create("autoMeasuresNum=\"system\"", false));
1040 //add(tag);
1041 }
1042
1043 //______________________________________________________________________________
visitStart(S_attributes & elt)1044 void xmlpart2guido::visitStart ( S_attributes& elt )
1045 {
1046 // get clef, key, division, staves, time and key in order!
1047 /* Example:
1048 <divisions>2</divisions>
1049 <key>
1050 <fifths>1</fifths>
1051 <mode>major</mode>
1052 </key>
1053 <time>
1054 <beats>3</beats>
1055 <beat-type>2</beat-type>
1056 </time>
1057 <staves>2</staves>
1058 <clef number="1">
1059 <sign>G</sign>
1060 <line>2</line>
1061 </clef>
1062 <clef number="2">
1063 <sign>F</sign>
1064 <line>4</line>
1065 <transpose>
1066 <diatonic>-1</diatonic>
1067 <chromatic>-2</chromatic>
1068 </transpose>
1069 </clef>
1070 *****/
1071
1072 ctree<xmlelement>::iterator iter = elt->begin();
1073
1074 // set division
1075 int divisions = (elt)->getIntValue(k_divisions, -1);
1076 if (divisions != -1)
1077 fCurrentDivision = divisions;
1078
1079 // Generate Clef first
1080 iter = elt->find(k_clef);
1081 while (iter != elt->end())
1082 {
1083 // There is a clef!
1084 string clefsign = iter->getValue(k_sign);
1085 int clefline = iter->getIntValue(k_line, 0);
1086 int clefoctavechange = iter->getIntValue(k_clef_octave_change, 0);
1087
1088 /// Actions:
1089 int staffnum = iter->getAttributeIntValue("number", 0);
1090 if ((staffnum != fTargetStaff) || fNotesOnly)
1091 {
1092 /// Search again for other clefs:
1093 iter++;
1094 iter = elt->find(k_clef, iter);
1095 continue;
1096 }
1097
1098 stringstream s;
1099 if ( clefsign == "G") s << "g";
1100 else if ( clefsign == "F") s << "f";
1101 else if ( clefsign == "C") s << "c";
1102 else if ( clefsign == "percussion") s << "perc";
1103 else if ( clefsign == "TAB") s << "TAB";
1104 else if ( clefsign == "none") s << "none";
1105 else { // unknown clef sign !!
1106 cerr << "warning: unknown clef sign \"" << clefsign << "\"" << endl;
1107 return;
1108 }
1109
1110 string param;
1111 if (clefline != 0) // was clefvisitor::kStandardLine
1112 s << clefline;
1113 s >> param;
1114 if (clefoctavechange == 1)
1115 param += "+8";
1116 else if (clefoctavechange == -1)
1117 param += "-8";
1118 Sguidoelement tag = guidotag::create("clef");
1119 checkStaff (staffnum);
1120 tag->add (guidoparam::create(param));
1121 add(tag);
1122
1123 std::pair<rational, std::string> foo = std::pair<rational, std::string>(fCurrentVoicePosition ,param);
1124 staffClefMap.insert(std::pair<int, std::pair < int , std::pair<rational, std::string> > >(fCurrentStaffIndex, std::pair< int, std::pair< rational, std::string > >(fMeasNum, foo) ) );
1125
1126 /// Search again for other clefs:
1127 iter++;
1128 iter = elt->find(k_clef, iter);
1129 }
1130
1131 // staves are treated at the S_Part level (see xml2guidovisitor) to create accolades
1132
1133 // Generate key
1134 iter = elt->find(k_key);
1135 if ((iter != elt->end())&&(!fNotesOnly))
1136 {
1137 string keymode = iter->getValue(k_mode);
1138 int keyfifths = iter->getIntValue(k_fifths, 0);
1139 Sguidoelement tag = guidotag::create("key");
1140 tag->add (guidoparam::create(keyfifths, false));
1141 add (tag);
1142 }
1143
1144 // Generate Time Signature info and METER info
1145 iter = elt->find(k_time);
1146 if ((iter != elt->end())&&(!fNotesOnly))
1147 {
1148 //int timebeat_type = iter->getIntValue(k_beat_type, 0);
1149 //int timebeats = iter->getIntValue(k_beats, 0);
1150 bool senzamesura = (iter->find(k_senza_misura) != iter->end());
1151 string timesymbol = iter->getAttributeValue("symbol");
1152 std::vector<std::pair<std::string,std::string> > fTimeSignInternal ;
1153
1154 // IOSEPRAC-185: Get all pairs for Composite Time Signatures
1155 ctree<xmlelement>::iterator iter_beat = iter->find(k_beats);
1156 ctree<xmlelement>::iterator iter_beatType = iter->find(k_beat_type);
1157
1158 while (iter_beat != iter->end())
1159 {
1160 // fTimeSignInternal.push_back(make_pair(iter_beat->getValue(k_beats), iter2->getValue(k_beat_type)));
1161 fTimeSignInternal.push_back(make_pair(iter_beat->getValue(),
1162 iter_beatType->getValue()));
1163 iter_beat = iter->find(k_beats, iter_beat++);
1164 iter_beatType = iter->find(k_beat_type, iter_beatType++);
1165 }
1166
1167 //// Actions:
1168 string timesign;
1169 if (!senzamesura) {
1170 if (timesymbol == "common") {
1171 // simulation of timesignvisitor::timesign function
1172 //rational ts = timesignvisitor::timesign(0);
1173 size_t index = 0;
1174 rational ts(0,1);
1175 if (index < fTimeSignInternal.size()) {
1176 const pair<string,string>& tss = fTimeSignInternal[index];
1177 long num = strtol (tss.first.c_str(), 0, 10);
1178 long denum = strtol (tss.second.c_str(), 0, 10);
1179 if (num && denum) ts.set(num, denum);
1180 }
1181 ///
1182 if ((ts.getDenominator() == 2) && (ts.getNumerator() == 2))
1183 timesign = "C/";
1184 else if ((ts.getDenominator() == 4) && (ts.getNumerator() == 4))
1185 timesign = "C";
1186 else
1187 timesign = string(ts);
1188 fCurrentTimeSign = ts;
1189 }
1190 else if (timesymbol == "cut") {
1191 timesign = "C/";
1192 fCurrentTimeSign = rational(2,2);
1193 }
1194 else {
1195
1196 stringstream s; string sep ="";
1197 fCurrentTimeSign.set(0,1);
1198 for (unsigned int i = 0; i < fTimeSignInternal.size(); i++) {
1199 s << sep << fTimeSignInternal[i].first << "/" << fTimeSignInternal[i].second;
1200 sep = "+";
1201 //fCurrentTimeSign += timesignvisitor::timesign(i);
1202 // simulation of timesignvisitor::timesign function
1203 //rational ts = timesignvisitor::timesign(i);
1204 size_t index = i;
1205 rational ts(0,1);
1206 if (index < fTimeSignInternal.size()) {
1207 const pair<string,string>& tss = fTimeSignInternal[index];
1208 long num = strtol (tss.first.c_str(), 0, 10);
1209 long denum = strtol (tss.second.c_str(), 0, 10);
1210 if (num && denum) ts.set(num, denum);
1211 }
1212 ///
1213 fCurrentTimeSign += ts;
1214 }
1215 s >> timesign;
1216 }
1217
1218 }
1219
1220 Sguidoelement tag = guidotag::create("meter");
1221 tag->add (guidoparam::create(timesign));
1222 if (fGenerateBars) tag->add (guidoparam::create("autoBarlines=\"off\"", false));
1223 if (fGenerateAutoMeasureNum) tag->add (guidoparam::create("autoMeasuresNum=\"system\"", false));
1224 if (iter->getAttributeValue("print-object")!="no")
1225 add(tag);
1226 }
1227 }
1228
1229 //______________________________________________________________________________
1230 // tools and methods for converting notes
1231 //______________________________________________________________________________
findTypeValue(const std::vector<S_slur> & slurs,const string & val) const1232 vector<S_slur>::const_iterator xmlpart2guido::findTypeValue ( const std::vector<S_slur>& slurs, const string& val ) const
1233 {
1234 std::vector<S_slur>::const_iterator i;
1235 for (i = slurs.begin(); i != slurs.end(); i++) {
1236 if ((*i)->getAttributeValue("type") == val) break;
1237 }
1238 return i;
1239 }
1240
1241 //______________________________________________________________________________
findTypeValue(const std::vector<S_tied> & tied,const string & val) const1242 vector<S_tied>::const_iterator xmlpart2guido::findTypeValue ( const std::vector<S_tied>& tied, const string& val ) const
1243 {
1244 std::vector<S_tied>::const_iterator i;
1245 for (i = tied.begin(); i != tied.end(); i++) {
1246 if ((*i)->getAttributeValue("type") == val) break;
1247 }
1248 return i;
1249 }
1250
1251 //______________________________________________________________________________
findValue(const std::vector<S_beam> & beams,const string & val) const1252 vector<S_beam>::const_iterator xmlpart2guido::findValue ( const std::vector<S_beam>& beams, const string& val ) const
1253 {
1254 std::vector<S_beam>::const_iterator i;
1255 for (i = beams.begin(); i != beams.end(); i++) {
1256 if ((*i)->getValue() == val) break;
1257 }
1258 return i;
1259 }
1260
1261 //______________________________________________________________________________
checkTiedBegin(const std::vector<S_tied> & tied)1262 void xmlpart2guido::checkTiedBegin ( const std::vector<S_tied>& tied )
1263 {
1264 std::vector<S_tied>::const_iterator i;
1265 for (i = tied.begin(); i != tied.end(); i++) {
1266 if ((*i)->getAttributeValue("type") == "start") {
1267 Sguidoelement tag = guidotag::create("tieBegin");
1268 string num = (*i)->getAttributeValue ("number");
1269 if (num.size())
1270 tag->add (guidoparam::create(num, false));
1271 string placement = (*i)->getAttributeValue("orientation");
1272 if (placement == "under")
1273 tag->add (guidoparam::create("curve=\"down\"", false));
1274 add(tag);
1275 }
1276 }
1277 }
1278
checkTiedEnd(const std::vector<S_tied> & tied)1279 void xmlpart2guido::checkTiedEnd ( const std::vector<S_tied>& tied )
1280 {
1281 std::vector<S_tied>::const_iterator i;
1282 for (i = tied.begin(); i != tied.end(); i++) {
1283 if ((*i)->getAttributeValue("type") == "stop") {
1284 Sguidoelement tag = guidotag::create("tieEnd");
1285 string num = (*i)->getAttributeValue ("number");
1286 if (num.size())
1287 tag->add (guidoparam::create(num, false));
1288 add(tag);
1289 }
1290 }
1291 }
1292
1293 //______________________________________________________________________________
checkSlurBegin(const std::vector<S_slur> & slurs)1294 void xmlpart2guido::checkSlurBegin ( const std::vector<S_slur>& slurs )
1295 {
1296 /*
1297 In Guido, slurBegin should be generated before notename and slurEnd after (or otherwise leading to bad numbering and rendering issues such as big slurs).
1298 Whereas in MusicXML, Slurs are attributes of Notations inside a note event.
1299
1300 There is one special case where slurEnd might appear before slurBegin: for consecutif slurs where notation is as follows:
1301 <notations>
1302 <slur number="1" type="stop"/>
1303 <slur number="1" placement="above" type="start"/>
1304 </notations>
1305
1306 This leads to the conclusion that the numbering in Guido and XML can NOT be the same. So we should track them indefinitely!
1307 */
1308 std::vector<S_slur>::const_iterator i;
1309 int counter = 0;
1310 for (i = slurs.begin(); i != slurs.end(); i++) {
1311 if ((*i)->getAttributeValue("type") == "start") {
1312 // There is a Slur Begin. Creat BeamBegin tag, and add its number to Stack
1313 int lastSlurInternalNumber = 1;
1314 if (!fSlurStack.empty()) {
1315 std::pair<int, int> toto = fSlurStack.back();
1316 lastSlurInternalNumber = toto.first + 1;
1317 }
1318
1319 // Skip Slur creation, if the CLOSING Slur is not on the same voice/staff (Guido limitation)
1320 if (isSlurClosing(*i)==false) {
1321 cerr<< "XML Slur at line:"<< (*i)->getInputLineNumber()<<" measure:"<<fMeasNum << " not closing on same voice! Skipping!"<<endl;
1322
1323 counter++;
1324 continue;
1325 }
1326
1327
1328 stringstream tagName;
1329 tagName << "slurBegin" << ":"<< lastSlurInternalNumber;
1330 Sguidoelement tag = guidotag::create(tagName.str());
1331 string placement = (*i)->getAttributeValue("placement");
1332 string orientation = (*i)->getAttributeValue("placement");
1333 if ((placement == "below")||(orientation=="under"))
1334 tag->add (guidoparam::create("curve=\"down\"", false));
1335 if ((placement == "above")||(orientation=="over"))
1336 tag->add (guidoparam::create("curve=\"up\"", false));
1337 add(tag);
1338
1339 // Add to stack:
1340 std::pair<int,int> toto2(lastSlurInternalNumber, (*i)->getAttributeIntValue("number", 0));
1341 fSlurStack.push_back(toto2);
1342 }
1343 counter++;
1344 }
1345 }
1346
isSlurClosing(S_slur elt)1347 bool xmlpart2guido::isSlurClosing(S_slur elt) {
1348 int internalXMLSlurNumber = elt->getAttributeIntValue("number", 0);
1349
1350 //cerr<< "\tSearching Slur Closing for line:"<<elt->getInputLineNumber() <<" with number "<<internalXMLSlurNumber<< " on Measure:"<<fMeasNum<< " on voice:"<<fTargetVoice<<endl;
1351 ctree<xmlelement>::iterator nextnote = find(fCurrentPart->begin(), fCurrentPart->end(), elt);
1352 if (nextnote != fCurrentPart->end()) {
1353 nextnote++; // advance one step
1354 }
1355
1356 // The first occurence of a slur STOP with the same NUMBER attribute should be considered as the target. Do not go beyond.
1357
1358 while (nextnote != fCurrentPart->end()) {
1359 // looking for the next note on the target voice
1360 if ((nextnote->getType() == k_note)) {
1361 int thisNoteVoice = nextnote->getIntValue(k_voice,0);
1362 ctree<xmlelement>::iterator iter;
1363 iter = nextnote->find(k_notations);
1364 if (iter != nextnote->end())
1365 {
1366 ctree<xmlelement>::iterator iterSlur;
1367 iterSlur = iter->find(k_slur);
1368 if (iterSlur != iter->end())
1369 {
1370 if ((iterSlur->getAttributeValue("type")=="stop") &&
1371 (iterSlur->getAttributeIntValue("number", 0) == internalXMLSlurNumber)
1372 ) {
1373 if (thisNoteVoice == fTargetVoice) {
1374 //cerr<< "\t\t FOUND Slur stop line:"<< iterSlur->getInputLineNumber()<< " voice:"<<thisNoteVoice<<" number:"<<iterSlur->getAttributeIntValue("number", 0)<<endl;
1375
1376 return true;
1377 }else {
1378 return false;
1379 }
1380 }
1381 }
1382 }
1383 }
1384 nextnote++;
1385 }
1386
1387 return false;
1388 }
1389
checkSlurEnd(const std::vector<S_slur> & slurs)1390 void xmlpart2guido::checkSlurEnd ( const std::vector<S_slur>& slurs )
1391 {
1392 std::vector<S_slur>::const_iterator i;
1393 for (i = slurs.begin(); i != slurs.end(); i++) {
1394 if ((*i)->getAttributeValue("type") == "stop") {
1395 int lastSlurInternalNumber = 0;
1396 std::vector< std::pair<int, int> >::const_iterator slurEndToBeErase;
1397 if (!fSlurStack.empty()) {
1398 // find the corresponding open slur in stack that has the same xml-num as this one
1399 int xmlnum = (*i)->getAttributeIntValue("number", 0);
1400 slurEndToBeErase = findSlur(xmlnum);
1401 if (slurEndToBeErase != fSlurStack.end()) {
1402 lastSlurInternalNumber = slurEndToBeErase->first;
1403 }else {
1404 continue;
1405 }
1406 }else {
1407 cerr<< "XML2Guido measure "<<fMeasNum<<" xmlLine "<<(*i)->getInputLineNumber() <<": Got Slur Stop with number:"<< (*i)->getAttributeIntValue("number", 0) <<" without a Slur in Stack. Skipping!"<<endl;
1408 continue;
1409 }
1410
1411 stringstream tagName;
1412 tagName << "slurEnd" << ":"<< lastSlurInternalNumber;
1413 Sguidoelement tag = guidotag::create(tagName.str());
1414 add(tag);
1415
1416 // remove the slur from stack
1417 fSlurStack.erase(slurEndToBeErase);
1418 }
1419 }
1420 }
1421
findSlur(const int xmlnum) const1422 std::vector< std::pair<int, int> >::const_iterator xmlpart2guido::findSlur ( const int xmlnum ) const {
1423 std::vector< std::pair<int, int> >::const_iterator i;
1424
1425 for (i=fSlurStack.begin(); i != fSlurStack.end(); i++) {
1426 if (i->second == xmlnum) {
1427 break;
1428 }
1429 }
1430 return i;
1431 }
1432
1433
1434 //______________________________________________________________________________
checkBeamBegin(const std::vector<S_beam> & beams)1435 void xmlpart2guido::checkBeamBegin ( const std::vector<S_beam>& beams )
1436 {
1437 /// !IMPORTANT NOTE from MXML DOC: Note that the beam number does not distinguish sets of beams that overlap, as it does for slur and other elements.
1438 /// So we need to track them with s Stack
1439 /// ! IMPORTANT NOTE from MXML DOC: "Beaming groups are distinguished by being in different voices and/or the presence or absence of grace and cue elements."
1440 /// This means that we should treate Grace and Cue elements separately.
1441
1442 //std::vector<S_beam>::const_iterator i = findValue(beams, "begin");
1443 //if (i != beams.end()) {
1444 std::vector<S_beam>::const_iterator i ;
1445 for (i = beams.begin(); (i != beams.end()); i++) {
1446 if ((*i)->getValue() != "begin")
1447 continue;
1448 // There is a Beam Begin. Creat BeamBegin tag, and add its number to Stack
1449 int lastBeamInternalNumber = 1;
1450 if (!fBeamStack.empty()) {
1451 std::pair<int, int> toto = fBeamStack.top();
1452 lastBeamInternalNumber = toto.first + 1;
1453 }
1454
1455 //cerr << "Measure "<< fMeasNum << " beam BEGIN "<< lastBeamInternalNumber<< " Beam-level="<<(*i)->getAttributeIntValue("number", 0)<< " fBeamOpened?="<<fBeamOpened<< " Grace?"<<fInGrace<<endl;
1456
1457 /// Using \beamBegin:NUMBER
1458 // GUID-79: Guido Engine does not deal well with nested Beams! Just keep the TOP level and store its number for later closing.
1459 //if ( (fBeamOpened == false) ||(fInGrace) || (fInCue)) { // had for GUID-79
1460 stringstream tagName;
1461 tagName << "beamBegin" << ":"<< lastBeamInternalNumber;
1462 Sguidoelement tag = guidotag::create(tagName.str()); // poor support of the begin end form in guido
1463 add (tag);
1464 if ( (!fInCue)&&(!fInGrace)) {
1465 fBeamOpened = true;
1466 }
1467 fCurrentBeamNumber = lastBeamInternalNumber;
1468
1469 // Add to stack:
1470 std::pair<int,int> toto2(lastBeamInternalNumber, (*i)->getAttributeIntValue("number", 0));
1471 fBeamStack.push(toto2);
1472
1473 //cerr << " Created!"<<endl;
1474 //}
1475 }
1476
1477 if (beams.empty() && fBeamStack.empty() && notevisitor::getType()!=kRest)
1478 {
1479 // Possible candidate for \beamsOff
1480 Sguidoelement tag = guidotag::create("beamsOff");
1481 add (tag);
1482 }
1483 }
1484
checkBeamEnd(const std::vector<S_beam> & beams)1485 void xmlpart2guido::checkBeamEnd ( const std::vector<S_beam>& beams )
1486 {
1487 /// IMPORTANT: Beam Numbering in MusicXML is not the same as in Slurs and are NOT incremental.
1488 /// The only assumption we make here is that the numbers are sorted. So we use a REVERSE iterator to close Beams in Order.
1489 std::vector<S_beam>::const_reverse_iterator i ;
1490 for (i = beams.rbegin(); (i != beams.rend() && (!fBeamStack.empty())); i++)
1491 {
1492 if (((*i)->getValue() == "end") && ((*i)->getAttributeIntValue("number", 0) == fBeamStack.top().second)) {
1493 // There is a Beam End. create tag and pop from stack
1494 int lastBeamInternalNumber = 0;
1495 if (!fBeamStack.empty()) {
1496 lastBeamInternalNumber = fBeamStack.top().first;
1497 }else {
1498 cerr<< "XML2Guido: Got Beam End without a beam in Stack. Skipping!"<<endl;
1499 return;
1500 }
1501
1502 //cerr << "Measure "<< fMeasNum << " beam END "<< lastBeamInternalNumber<< " - Beam-Level="<<(*i)->getAttributeIntValue("number", 1)<< " isBeamOpened? "<< fBeamOpened;
1503
1504 /// using \beamEnd:NUMBER
1505 // GUID-79: Only close the initial Beam
1506 //if ( (fBeamOpened) ||(fInGrace) || (fInCue)) { // && (fCurrentBeamNumber == lastBeamInternalNumber) // for GUID-79
1507 stringstream tagName;
1508 tagName << "beamEnd" << ":"<< lastBeamInternalNumber;
1509 Sguidoelement tag = guidotag::create(tagName.str()); // poor support of the begin end form in guido
1510 add (tag);
1511 if ((fBeamOpened) && (!fInCue) && (!fInGrace)) {
1512 fBeamOpened = false;
1513 }
1514 //cerr<< " ---> CLOSED! fBeamOpened="<<fBeamOpened<< " Grace?="<<fInGrace<<" Cue?="<<fInCue<<endl;
1515 //}
1516
1517 fBeamStack.pop();
1518 }
1519 }
1520
1521 // Experimental
1522 //if (beamStackSizeBeforeClosing > fBeamStack.size())
1523 //{
1524 // checkTextEnd();
1525 //}
1526 }
1527
1528 //_______________________Tuplets___________________________________
checkTupletBegin(const std::vector<S_tuplet> & tuplets,const notevisitor & nv,const S_note & elt)1529 void xmlpart2guido::checkTupletBegin ( const std::vector<S_tuplet>& tuplets,
1530 const notevisitor& nv,
1531 const S_note& elt )
1532 {
1533 std::vector<S_tuplet>::const_iterator i;
1534
1535 for (i = tuplets.begin(); i != tuplets.end(); i++) {
1536 if ( (*i)->getAttributeValue("type") == "start") break;
1537 }
1538
1539 if (i != tuplets.end()) {
1540 /// Determine whether we need Brackets and Numbering
1541 bool withBracket = ((*i)->getAttributeValue("bracket")=="yes");
1542 std::string showNumbering = ((*i)->getAttributeValue("show-number"));
1543 /// Get Tuplet Number
1544 int thisTupletNumber = (*i)->getAttributeIntValue("number", 1);
1545 /// Get Tuplet Placement and graphic type
1546 std::string tupletPlacement = (*i)->getAttributeValue("placement");
1547 std::string tupletGraphicType = nv.fGraphicType;
1548 long numberOfEventsInTuplet = nv.getTimeModification().getDenominator();
1549 long numberOfNormalNotes = nv.getTimeModification().getNumerator();
1550
1551 bool useDispNoteAttribute = false;
1552
1553 ///// if "tuplet-actual" is present override numberOfEventsInTuplet and tupletGraphicType
1554 auto tuplet_actual = (*i)->find(k_tuplet_actual);
1555 if ( tuplet_actual != (*i)->end()) {
1556 numberOfEventsInTuplet = tuplet_actual->getIntValue(k_tuplet_number, 1);
1557 tupletGraphicType = tuplet_actual->getValue(k_tuplet_type);
1558 useDispNoteAttribute = true;
1559 }
1560
1561 ///// if "tuplet-normal" is present override numberOfEventsInTuplet and tupletGraphicType
1562 auto tuplet_normal = (*i)->find(k_tuplet_normal);
1563 if (tuplet_normal != (*i)->end()) {
1564 numberOfNormalNotes = tuplet_normal->getIntValue(k_tuplet_number, 1);
1565 }
1566
1567 //// Rational : If all note durations are equal, then use the dispNote attribute. If not, then don't!
1568 if (useDispNoteAttribute == false) {
1569 /// Get Top Note Guido Duration
1570 rational topNoteDurRational = NoteType::type2rational(NoteType::xml(nv.getGraphicType()));
1571 if (topNoteDurRational.getNumerator() == 0)
1572 topNoteDurRational.set (nv.getDuration(), fCurrentDivision*4);
1573 topNoteDurRational.rationalise();
1574 rational tm = nv.getTimeModification();
1575 topNoteDurRational *= tm;topNoteDurRational.rationalise();
1576
1577 /// Browse through all elements of Tuplet until "stop"!
1578 ctree<xmlelement>::iterator nextnote = find(fCurrentMeasure->begin(), fCurrentMeasure->end(), elt);
1579 if (nextnote != fCurrentMeasure->end()) {
1580 nextnote++; // advance one step
1581 }
1582 useDispNoteAttribute = true; // setback to True.. will become false if check doesn't pass!
1583 while (nextnote != fCurrentMeasure->end()) {
1584 // looking for the next note on the target voice
1585 if ((nextnote->getType() == k_note) && (nextnote->getIntValue(k_voice,0) == fTargetVoice)) {
1586 // Get note's Guido Duration
1587 rational nextNoteDur = NoteType::type2rational(NoteType::xml(nextnote->getValue(k_type)));
1588 if (nextNoteDur.getNumerator()==0)
1589 nextNoteDur.set(nextnote->getIntValue(k_duration, 0), fCurrentDivision*4);
1590 nextNoteDur.rationalise();
1591 rational nextNoteTm;
1592 nextNoteTm.set(nextnote->getIntValue(k_normal_notes, 1), nextnote->getIntValue(k_actual_notes, 1));
1593 nextNoteDur *= nextNoteTm; nextNoteDur.rationalise();
1594
1595 if ( topNoteDurRational != nextNoteDur ) {
1596 useDispNoteAttribute = false;
1597 break;
1598 }
1599
1600 ctree<xmlelement>::iterator iter;
1601 iter = nextnote->find(k_notations);
1602 if (iter != nextnote->end())
1603 {
1604 // There is a notation tag. Now check if there's a tuplet END with the same tuplet number.
1605 // If yes, then increment and break.
1606 ctree<xmlelement>::iterator iterTuplet;
1607 iterTuplet = iter->find(k_tuplet);
1608 if (iterTuplet != iter->end())
1609 {
1610 // There is a tuplet tag!
1611 int newTupletNumber = iterTuplet->getAttributeIntValue("number", 0);
1612 if ((iterTuplet->getAttributeValue("type")=="stop")&&(newTupletNumber==thisTupletNumber))
1613 {
1614 break;
1615 }
1616 }
1617 }
1618 }
1619 nextnote++;
1620 }
1621 }
1622
1623 /// Determine the graphical format inside Tuplet
1624 std::string dispNotePar ;
1625 int dy1offset = 6;
1626 if (tupletGraphicType=="64th")
1627 {
1628 dispNotePar = "\"/64\"";
1629 dy1offset+=4;
1630 }
1631 if (tupletGraphicType=="32nd")
1632 {
1633 dispNotePar = "\"/32\"";
1634 dy1offset+=4;
1635 }
1636 else if (tupletGraphicType=="16th")
1637 {
1638 dispNotePar = "\"/16\"";
1639 dy1offset+=3;
1640 }
1641 else if (tupletGraphicType=="eighth")
1642 {
1643 dispNotePar = "\"/8\"";
1644 dy1offset+=2;
1645 }
1646 else if (tupletGraphicType=="quarter")
1647 {
1648 dispNotePar = "\"/4\"";
1649 dy1offset+=1;
1650 }
1651 else if (tupletGraphicType=="half")
1652 {
1653 dispNotePar = "\"/2\"";
1654 }
1655 else if (tupletGraphicType=="whole")
1656 {
1657 dispNotePar = "\"/1\"";
1658 dy1offset-=5;
1659 }
1660
1661 /// Generate tag and parameters
1662 // Avoid generating parameter for triplets since Guido does this automatically
1663 //if ((numberOfEventsInTuplet!=3) ||(dispNotePar.size()))
1664 //{
1665 Sguidoelement tag = guidotag::create("tuplet");
1666 /// Add number visualiser
1667 stringstream tuplet;
1668 string numbering = std::to_string(numberOfEventsInTuplet); // default is "actual" for "show-numbering)
1669 if (showNumbering=="none") {
1670 numbering = "";
1671 }else if (showNumbering == "both") {
1672 numbering = std::to_string(numberOfEventsInTuplet)+":"+std::to_string(numberOfNormalNotes);
1673 }
1674
1675 if (numberOfEventsInTuplet>1) // workaround for pianistic Tremolos that come out of Finale as Tuplets!
1676 tuplet << (withBracket? "-" : "") << numbering << (withBracket? "-" : "");
1677 tag->add (guidoparam::create(tuplet.str()));
1678
1679 /// set dispNote, Possible values : "/1", "/2" "/4", "/8", "/16"
1680 if (dispNotePar.size() && useDispNoteAttribute)
1681 {
1682 tag->add(guidoparam::create(("dispNote="+dispNotePar),false));
1683 }
1684
1685 //// Add Placement
1686 if (tupletPlacement.size())
1687 {
1688 tag->add(guidoparam::create(("position=\""+tupletPlacement+"\""),false));
1689 }
1690
1691 fTupletOpen++;
1692
1693 push (tag);
1694 //}
1695 }
1696 }
1697
checkTupletEnd(const std::vector<S_tuplet> & tuplets)1698 void xmlpart2guido::checkTupletEnd ( const std::vector<S_tuplet>& tuplets )
1699 {
1700 std::vector<S_tuplet>::const_iterator i;
1701 for (i = tuplets.begin(); (i != tuplets.end()); i++) {
1702 // Do not check for tupletNumber (might cause conflict with nested Tuplets) -- We assume everything is there!
1703 if (((*i)->getAttributeValue("type") == "stop") && (fTupletOpen>0)) {
1704 pop();
1705 fTupletOpen--;
1706 }
1707 }
1708 }
1709
1710
1711
1712
1713 // ------------------- LYRICS Functions
1714
checkLyricBegin(const std::vector<S_lyric> & lyrics)1715 void xmlpart2guido::checkLyricBegin ( const std::vector<S_lyric>& lyrics )
1716 {
1717 if (notevisitor::getSyllabic()== "single")
1718 {
1719 Sguidoelement tag = guidotag::create("lyrics");
1720 /// replaces Spaces in text by '~' to avoid event progression!
1721 std::string newTxt = notevisitor::getLyricText();
1722 std::replace( newTxt.begin(), newTxt.end(), ' ', '~');
1723 tag->add (guidoparam::create(newTxt, true));
1724
1725 /// Adjust Y-Position by infering from XML
1726 //tag->add (guidoparam::create(notevisitor::getLyricDy(), false));
1727 std::string autoPosParam="autopos=\"on\"";
1728 tag->add(guidoparam::create(autoPosParam, false));
1729 push (tag);
1730
1731 fHasLyrics = true;
1732 }
1733
1734 if ((notevisitor::getSyllabic()== "end")
1735 ||(notevisitor::getSyllabic()== "middle")
1736 ||(notevisitor::getSyllabic()== "begin"))
1737 {
1738 Sguidoelement tag = guidotag::create("lyrics");
1739 // replaces Spaces in text by '~' to avoid event progression!
1740 std::string newTxt = notevisitor::getLyricText();
1741 std::replace( newTxt.begin(), newTxt.end(), ' ', '~');
1742 if (!(notevisitor::getSyllabic()== "end"))
1743 {
1744 // Add a '-' only for Begin/Middle and not at the end
1745 newTxt.append("-");
1746 }
1747 tag->add (guidoparam::create(newTxt, true));
1748 //tag->add (guidoparam::create(notevisitor::getLyricDy(), false));
1749 std::string autoPosParam="autopos=\"on\"";
1750 tag->add(guidoparam::create(autoPosParam, false));
1751 push (tag);
1752
1753 fHasLyrics = true;
1754 }
1755 }
1756
checkLyricEnd(const std::vector<S_lyric> & lyrics)1757 void xmlpart2guido::checkLyricEnd ( const std::vector<S_lyric>& lyrics )
1758 {
1759 float minDur4Space = 1;
1760 size_t minStringSize4Space = 2;
1761
1762 float thisDuration = (float)(getDuration()) / (float)(fCurrentDivision*1);
1763
1764 if (notevisitor::getSyllabic()== "single")
1765 {
1766 pop();
1767
1768 if ( fLyricsManualSpacing && (thisDuration< minDur4Space) && (notevisitor::getLyricText().size() > minStringSize4Space))
1769 {
1770 Sguidoelement tag = guidotag::create("space");
1771 size_t additionalSpace = notevisitor::getLyricText().size() - minStringSize4Space;
1772 tag->add (guidoparam::create(8 + additionalSpace, false));
1773 add(tag);
1774 }
1775
1776 }
1777 else if ((notevisitor::getSyllabic()== "end")
1778 ||(notevisitor::getSyllabic()== "middle")
1779 ||(notevisitor::getSyllabic()== "begin"))
1780 {
1781 pop();
1782
1783 if ( fLyricsManualSpacing && (thisDuration< minDur4Space) && (notevisitor::getLyricText().size() > minStringSize4Space))
1784 {
1785 Sguidoelement tag = guidotag::create("space");
1786 size_t lyricStringSize = 0;
1787 if (notevisitor::getSyllabic()=="end")
1788 lyricStringSize = notevisitor::getLyricText().size();
1789 else
1790 lyricStringSize = notevisitor::getLyricText().size() +1;
1791
1792 long additionalSpace = lyricStringSize - minStringSize4Space;
1793 tag->add (guidoparam::create(8 + additionalSpace, false));
1794 add(tag);
1795 }
1796
1797 }
1798 }
1799
findValue(const std::vector<S_lyric> & lyrics,const string & val) const1800 vector<S_lyric>::const_iterator xmlpart2guido::findValue ( const std::vector<S_lyric>& lyrics,
1801 const string& val ) const
1802 {
1803 std::vector<S_lyric>::const_iterator i;
1804 for (i = lyrics.begin(); i != lyrics.end(); i++) {
1805 if ((*i)->getValue() == val) break;
1806 }
1807 return i;
1808 }
1809
1810
1811 //______________________________________________________________________________
checkStem(const S_stem & stem)1812 void xmlpart2guido::checkStem ( const S_stem& stem )
1813 {
1814 // Should regenerate if in a Cue tag
1815 Sguidoelement tag;
1816 if (stem) {
1817 if (stem->getValue() == "down") {
1818 //if (fCurrentStemDirection != kStemDown || fInCue) {
1819 tag = guidotag::create("stemsDown");
1820 fCurrentStemDirection = kStemDown;
1821 //}
1822 }
1823 else if (stem->getValue() == "up") {
1824 //if (fCurrentStemDirection != kStemUp || fInCue) {
1825 tag = guidotag::create("stemsUp");
1826 fCurrentStemDirection = kStemUp;
1827 //}
1828 }
1829 else if (stem->getValue() == "none") {
1830 //if (fCurrentStemDirection != kStemNone || fInCue) {
1831 tag = guidotag::create("stemsOff");
1832 fCurrentStemDirection = kStemNone;
1833 //}
1834 }
1835 else if (stem->getValue() == "double") {
1836 }
1837 }
1838 else if (fCurrentStemDirection != kStemUndefined) {
1839 tag = guidotag::create("stemsAuto");
1840 fCurrentStemDirection = kStemUndefined;
1841 }
1842 if (tag) add(tag);
1843 }
1844
1845 //______________________________________________________________________________
checkArticulation(const notevisitor & note)1846 int xmlpart2guido::checkArticulation ( const notevisitor& note )
1847 {
1848 int n = 0;
1849 Sguidoelement tag;
1850 if (note.fArpeggio) {
1851 tag = guidotag::create("arpeggio");
1852 xml2guidovisitor::addDirection(note.fArpeggio, tag);
1853 push(tag);
1854 n++;
1855 }
1856 if (note.fAccent) {
1857 tag = guidotag::create("accent");
1858 if (fGeneratePositions) xml2guidovisitor::addPlacement(note.fAccent, tag);
1859 push(tag);
1860 n++;
1861 }
1862 if (note.fStrongAccent) {
1863 tag = guidotag::create("marcato");
1864 if (fGeneratePositions) xml2guidovisitor::addPlacement(note.fStrongAccent, tag);
1865 push(tag);
1866 n++;
1867 }
1868 if (note.fStaccato) {
1869 tag = guidotag::create("stacc");
1870 if (fGeneratePositions) xml2guidovisitor::addPlacement(note.fStaccato, tag);
1871 push(tag);
1872 n++;
1873 }
1874 if (note.fTenuto) {
1875 tag = guidotag::create("ten");
1876 if (fGeneratePositions) xml2guidovisitor::addPlacement(note.fTenuto, tag);
1877 push(tag);
1878 n++;
1879 }
1880 if (note.fHarmonic) {
1881 tag = guidotag::create("harmonic");
1882 xml2guidovisitor::addPlacement(note.fHarmonic, tag);
1883 push(tag);
1884 n++;
1885 }
1886 if (note.fSnapPizzicato) {
1887 tag = guidotag::create("pizz");
1888 stringstream s;
1889 s << "type=\"snap\"";
1890 tag->add (guidoparam::create(s.str(), false));
1891 push(tag);
1892 n++;
1893 }
1894
1895 return n;
1896 }
1897
1898 /// Articulations that should be generated AFTER the note creation
checkPostArticulation(const notevisitor & note)1899 void xmlpart2guido::checkPostArticulation ( const notevisitor& note )
1900 {
1901 if (note.fBreathMark) {
1902 Sguidoelement tag = guidotag::create("breathMark");
1903 xml2guidovisitor::addPosY(note.fBreathMark, tag, -3, 1);
1904 add(tag);
1905 }
1906
1907 }
1908
1909 //---------------------
checkWavyTrillBegin(const notevisitor & nv)1910 void xmlpart2guido::checkWavyTrillBegin ( const notevisitor& nv )
1911 {
1912 if (nv.fTrill)
1913 {
1914 Sguidoelement tag;
1915 tag = guidotag::create("trill");
1916
1917 // parse accidental-mark as it'll be used by all
1918 string accidentalMark="";
1919 if (nv.fAccidentalMark)
1920 accidentalMark = nv.fAccidentalMark->getValue();
1921 string guidoAccident="";
1922 if (accidentalMark=="sharp")
1923 {
1924 guidoAccident = "#";
1925 }
1926 if (accidentalMark=="flat")
1927 {
1928 guidoAccident = "&";
1929 }
1930
1931 // Generate ornament note ONLY if there is an AccidentalMark in MusicXML
1932 if (nv.fAccidentalMark) {
1933 string stepString = nv.getStep();
1934 int stepi = ( stepString == "B" ? notevisitor::C : step2i(stepString) + 1);
1935 string newName = i2step(stepi);
1936 if (!newName.empty()) newName[0]=tolower(newName[0]);
1937
1938 stringstream s;
1939 s << newName << guidoAccident;
1940
1941 tag->add (guidoparam::create(s.str(), true));
1942 }
1943
1944 // Check for continuous Trills
1945 if (nv.getWavylines().size()>0)
1946 {
1947 std::vector<S_wavy_line>::const_iterator i;
1948 for (i = nv.getWavylines().begin(); i != nv.getWavylines().end(); i++) {
1949 if ((*i)->getAttributeValue("type") == "start") {
1950 fWavyTrillOpened = true;
1951 }
1952 }
1953 }
1954 else {
1955 // if there is no wavy-line, then the Trill should be closed in this scope!
1956 fSingleScopeTrill = true;
1957 }
1958 push(tag);
1959 }
1960 }
1961
checkWavyTrillEnd(const notevisitor & nv)1962 void xmlpart2guido::checkWavyTrillEnd ( const notevisitor& nv )
1963 {
1964 if (nv.fTrill) pop();
1965
1966 if (nv.getWavylines().size() > 0)
1967 {
1968 std::vector<S_wavy_line>::const_iterator i;
1969 for (i = nv.getWavylines().begin(); i != nv.getWavylines().end(); i++) {
1970 if ((*i)->getAttributeValue("type") == "stop") {
1971 fWavyTrillOpened = false;
1972 }
1973 }
1974 }
1975 else
1976 if (fSingleScopeTrill) {
1977 fSingleScopeTrill = false;
1978 }
1979 }
1980
1981
1982 //---------------------
checkChordOrnaments(const notevisitor & note)1983 int xmlpart2guido::checkChordOrnaments(const notevisitor& note)
1984 {
1985 // We can visit mordent, tremolo, trill, turn, inverted-turn, vertical-turn, and accidental-mark
1986 // See http://usermanuals.musicxml.com/MusicXML/Content/EL-MusicXML-ornaments.htm
1987 /// NOTE: GUIDO 1.6.4+ was upgraded, removing Chord construction by default for ornaments (and thus allowing using ornaments on chords!)
1988 /// This implementation complies to GuidoLib 1.6.4 onwards. -- Arshia Cont
1989
1990 int n = 0;
1991 Sguidoelement tag;
1992
1993 // Inversed mordent in Guido is mordent with inversed chord structure.
1994 if (note.fMordent || note.fInvertedMordent) {
1995 tag = guidotag::create("mord");
1996
1997 // if mordent, then we need to add "inverted" parameter with the "correct" pitch!
1998 if (note.fMordent) {
1999 // Calculating the ornament note to add requires knowing the KEY! CHEAT: put "-"
2000 string stepString = note.getStep();
2001 int stepi = ( step2i(stepString) == 0 ? notevisitor::B : step2i(stepString) - 1);
2002 string newName = i2step(stepi);
2003
2004 if (!newName.empty()) {
2005 newName[0]=tolower(newName[0]);
2006 }
2007 tag->add (guidoparam::create("-", true));
2008 tag->add (guidoparam::create("inverted", true));
2009 }
2010
2011 //if (fGeneratePositions) xml2guidovisitor::addPlacement(note.fMordent, tag);
2012 push(tag);
2013 n++;
2014 }
2015
2016 if (note.fTurn || note.fInvertedTurn) {
2017 tag = guidotag::create("turn");
2018
2019 if (note.fInvertedTurn) {
2020 tag->add (guidoparam::create("type=\"inverted\"", false));
2021 }
2022
2023 //if (fGeneratePositions) xml2guidovisitor::addPlacement(note.fInvertedMordent, tag);
2024 push(tag);
2025 n++;
2026 }
2027
2028 if (note.fTrill) {
2029 tag = guidotag::create("trill");
2030 //n++;
2031 }
2032
2033 if (note.fBowUp || note.fBowDown) {
2034 tag = guidotag::create("bow");
2035 stringstream s;
2036 if (note.fBowUp) {
2037 s << "\"up\"";
2038 }else {
2039 s << "\"down\"";
2040 }
2041 tag->add (guidoparam::create(s.str(), false));
2042 push(tag);
2043 n++;
2044 }
2045
2046 return n;
2047 }
2048
checkTremolo(const notevisitor & note,const S_note & elt)2049 int xmlpart2guido::checkTremolo(const notevisitor& note, const S_note& elt) {
2050 // Starting FINALE 24, there is Tremolo types "Single", "start" and "stop". The "Start" type should search for a "stop" to construct the tremolo
2051 /* Notes from MusicXML Doc:
2052 When using double-note tremolos, the duration of each note in the tremolo should correspond to half of the notated type value.
2053 A time-modification element should also be added with an actual-notes value of 2 and a normal-notes value of 1.
2054 If used within a tuplet, this 2/1 ratio should be multiplied by the existing tuplet ratio.
2055 */
2056
2057 if (note.fTremolo) {
2058 Sguidoelement tag;
2059 stringstream s;
2060
2061 std::string tremType = note.fTremolo->getAttributeValue("type");
2062 if (tremType == "single") {
2063 tag = guidotag::create("trem");
2064 // trem style is the number int value
2065 std::stringstream convert;
2066 convert << note.fTremolo->getValue();
2067 int numDashes = 0;
2068 convert >> numDashes;
2069 s << "style=\"";
2070 for (int id=0; id<numDashes;id++) {
2071 s << "/";
2072 }
2073 s << "\"";
2074 tag->add (guidoparam::create(s.str(), false));
2075
2076 push(tag);
2077 return 1;
2078 }else
2079 if (tremType == "start") {
2080 fTremoloInProgress = true;
2081 tag = guidotag::create("trem");
2082
2083 /// Find "stop" pitch
2084 ctree<xmlelement>::iterator nextnote = find(fCurrentMeasure->begin(), fCurrentMeasure->end(), elt);
2085 if (nextnote != fCurrentMeasure->end()) nextnote++; // advance one step
2086 while (nextnote != fCurrentMeasure->end()) {
2087 // looking for the next note on the target voice
2088 if ((nextnote->getType() == k_note) && (nextnote->getIntValue(k_voice,0) == fTargetVoice)) {
2089 ctree<xmlelement>::iterator iter; // and when there is one
2090 iter = nextnote->find(k_tremolo);
2091 if (iter != nextnote->end() && (iter->getAttributeValue("type")=="stop") ) {
2092 vector<Sxmlelement> chordStop = getChord( (*nextnote) );
2093 s << "pitch=\"";
2094 if (chordStop.size())
2095 s<< "{";
2096
2097 notevisitor nv;xml_tree_browser browser(&nv);
2098 Sxmlelement note = *nextnote;
2099 browser.browse(*note);
2100
2101 // Add main stop note
2102 int octave = nv.getOctave() - 3; // octave offset between MusicXML and GUIDO is -3
2103 string accident = alter2accident(nv.getAlter());
2104 string name = noteName(nv);
2105 s << name<<accident<<(int)octave;
2106
2107 // Add chords if any
2108 if (chordStop.size()) {
2109 for (vector<Sxmlelement>::const_iterator chordIter = chordStop.begin(); chordIter != chordStop.end(); chordIter++) {
2110 s<<",";
2111 notevisitor nv;xml_tree_browser browser(&nv);
2112 Sxmlelement note = *chordIter;
2113 browser.browse(*note);
2114 int octave = nv.getOctave() - 3; // octave offset between MusicXML and GUIDO is -3
2115 string accident = alter2accident(nv.getAlter());
2116 string name = noteName(nv);
2117 s << name<<accident<<(int)octave;
2118 }
2119 s<<"}";
2120 }
2121
2122 s<<"\"";
2123
2124 break;
2125 }
2126 }
2127 nextnote++;
2128 }
2129
2130 // Only add tag if the Stop has been correctly detected
2131 if (s.str().size()) {
2132 tag->add (guidoparam::create(s.str(), false));
2133
2134 push(tag);
2135 // return 0 so that this tag wouldn't be closed by pending Pops. Double-note tremolos are closed using fTremoloInProgress
2136 return 0;
2137 }
2138
2139 }
2140 }
2141
2142 return 0;
2143 }
2144
2145 //______________________________________________________________________________
getChord(const S_note & elt)2146 vector<Sxmlelement> xmlpart2guido::getChord ( const S_note& elt )
2147 {
2148 vector<Sxmlelement> v;
2149 ctree<xmlelement>::iterator nextnote = find(fCurrentMeasure->begin(), fCurrentMeasure->end(), elt);
2150 if (nextnote != fCurrentMeasure->end()) nextnote++; // advance one step
2151 while (nextnote != fCurrentMeasure->end()) {
2152 // looking for the next note on the target voice
2153 if ((nextnote->getType() == k_note) && (nextnote->getIntValue(k_voice,0) == fTargetVoice)) {
2154 ctree<xmlelement>::iterator iter; // and when there is one
2155 iter = nextnote->find(k_chord);
2156 if (iter != nextnote->end())
2157 v.push_back(*nextnote);
2158 else break;
2159 }
2160 nextnote++;
2161 }
2162 return v;
2163 }
2164
getChord(const Sxmlelement & elt)2165 vector<Sxmlelement> xmlpart2guido::getChord ( const Sxmlelement& elt )
2166 {
2167 vector<Sxmlelement> v;
2168 ctree<xmlelement>::iterator nextnote = find(fCurrentMeasure->begin(), fCurrentMeasure->end(), elt);
2169 if (nextnote != fCurrentMeasure->end()) nextnote++; // advance one step
2170 while (nextnote != fCurrentMeasure->end()) {
2171 // looking for the next note on the target voice
2172 if ((nextnote->getType() == k_note) && (nextnote->getIntValue(k_voice,0) == fTargetVoice)) {
2173 ctree<xmlelement>::iterator iter; // and when there is one
2174 iter = nextnote->find(k_chord);
2175 if (iter != nextnote->end())
2176 v.push_back(*nextnote);
2177 else break;
2178 }
2179 nextnote++;
2180 }
2181 return v;
2182 }
2183
2184 //______________________________________________________________________________
checkCue(const notevisitor & nv)2185 void xmlpart2guido::checkCue (const notevisitor& nv)
2186 {
2187 if (nv.isCue()) {
2188 if (!fInCue) {
2189 fInCue = true;
2190 Sguidoelement tag = guidotag::create("cue");
2191 push(tag);
2192 }
2193
2194 // add up duration in Cue
2195 if (!nv.inChord())
2196 {
2197 rational r(nv.getDuration(), fCurrentDivision*4);
2198 r.rationalise();
2199 durationInCue += r;
2200 }
2201 }
2202 else if (fInCue) {
2203 fInCue = false;
2204 pop();
2205
2206 durationInCue.rationalise();
2207 if (durationInCue.getNumerator() > 0) {
2208 guidonoteduration dur (durationInCue.getNumerator(), durationInCue.getDenominator());
2209 Sguidoelement note = guidonote::create(fTargetVoice, "empty", 0, dur, "");
2210 add (note);
2211 fCurrentVoicePosition += durationInCue;
2212 fCurrentVoicePosition.rationalise();
2213 }
2214
2215 durationInCue = 0;
2216 }
2217 }
2218
2219 //______________________________________________________________________________
checkGrace(const notevisitor & nv)2220 void xmlpart2guido::checkGrace (const notevisitor& nv)
2221 {
2222 if (nv.isGrace()) {
2223 if (!fInGrace) {
2224 /// GUID-153: Fetch directions after grace
2225 ctree<xmlelement>::iterator nextnote = find(fCurrentMeasure->begin(), fCurrentMeasure->end(), nv.getSnote());
2226 nextnote.forward_up(); // forward one element
2227 while (nextnote != fCurrentMeasure->end()) {
2228 // break if next element is a non-grace
2229 if (( (nextnote->getType() == k_note) && (nextnote->getIntValue(k_voice,0) == fTargetVoice) )
2230 || (nextnote->getType() == k_direction)){
2231 if (nextnote->getType() == k_note) {
2232 if (nextnote->find(k_grace) != nextnote->end())
2233 {
2234 // Next note has a grace.. continue loop..
2235 nextnote.forward_up(); // forward one element
2236 }else {
2237 // Non-grace, break...
2238 break;
2239 }
2240 }else if (nextnote->getType() == k_direction) {
2241 // should parse direction BEFORE grace occurs: visit the element and put in the eraser stack
2242 nextnote->acceptIn(*this);
2243 nextnote->acceptOut(*this);
2244 fDirectionEraserStack.push(nextnote->getInputLineNumber());
2245 nextnote.forward_up(); // forward one element
2246 }
2247
2248 }else {
2249 break;
2250 }
2251 }
2252 /// End-of Guid-153
2253 fInGrace = true;
2254 Sguidoelement tag = guidotag::create("grace");
2255 push(tag);
2256 }
2257 }
2258 else if (fInGrace) {
2259 fInGrace = false;
2260 pop();
2261 }
2262 }
2263
checkGraceEnd(const notevisitor & nv)2264 void xmlpart2guido::checkGraceEnd(const notevisitor& nv)
2265 {
2266 // End grace BEFORE the next non-grace note to avoid conflict with S_direction
2267 if (fInGrace)
2268 {
2269 ctree<xmlelement>::iterator nextnote = find(fCurrentMeasure->begin(), fCurrentMeasure->end(), nv.getSnote());
2270 nextnote++; // advance one step
2271 while (nextnote != fCurrentMeasure->end()) {
2272 if ((nextnote->getType() == k_note) && (nextnote->getIntValue(k_voice,0) == fTargetVoice)){
2273 ctree<xmlelement>::iterator iter = nextnote->find(k_grace);
2274 if (iter != nextnote->end())
2275 {
2276 // Next note has a grace.. do nothing.. we'll break anyway!
2277 }else {
2278 // Pop grace before disaster! :)
2279 pop();
2280 fInGrace = false;
2281 }
2282
2283 break;
2284 }
2285 nextnote++;
2286 }
2287 }
2288 }
2289
2290 //______________________________________________________________________________
checkFermata(const notevisitor & nv)2291 int xmlpart2guido::checkFermata (const notevisitor& nv)
2292 {
2293 if (nv.inFermata()) {
2294 Sguidoelement tag = guidotag::create("fermata");
2295
2296 // Determine Type ("upright" or "inverted")
2297 std::string fermataType = nv.fFermata->getAttributeValue("type");
2298 if (fermataType == "inverted") {
2299 stringstream s;
2300 s << "position=" << "\"below\"";
2301 tag->add (guidoparam::create(s.str(), false));
2302
2303 // XML reports distance from top of staff, Guido needs from bottom in Inverted mode. This is offset 8
2304 xml2guidovisitor::addPosY(nv.fFermata, tag, 8, 1.0);
2305 }else{
2306 float noteDistanceFromTopStaff = getNoteDistanceFromStaffTop(nv);
2307 // Do not infer fermata position if note is above top of the staff. Let guido do it
2308 if (noteDistanceFromTopStaff < 2) {
2309 xml2guidovisitor::addPosY(nv.fFermata, tag, 0, 1.0);
2310 }
2311 }
2312 push(tag);
2313 return 1;
2314 }
2315 return 0;
2316 }
2317
2318 //______________________________________________________________________________
noteName(const notevisitor & nv)2319 string xmlpart2guido::noteName ( const notevisitor& nv )
2320 {
2321 string accident = alter2accident(nv.getAlter());
2322 string name;
2323 if (nv.getType() == notevisitor::kRest)
2324 name="_";
2325 else {
2326 name = nv.getStep();
2327 if (!name.empty()) name[0]=tolower(name[0]);
2328 else cerr << "warning: empty note name" << endl;
2329 }
2330 return name;
2331 }
2332
2333 //______________________________________________________________________________
noteDuration(const notevisitor & nv)2334 guidonoteduration xmlpart2guido::noteDuration ( const notevisitor& nv)
2335 {
2336 guidonoteduration dur(0,0);
2337 if (nv.getType() == kRest) {
2338 /* OLD WAY
2339 rational r(nv.getDuration(), fCurrentDivision*4);
2340 r.rationalise();
2341 dur.set (r.getNumerator(), r.getDenominator());
2342 */
2343
2344 // Do as in Notes but be aware of TYPE = WHOLE for Rests which should be ignored if we are not 4/4 or similar!
2345
2346 if ( (nv.getGraphicType() == "whole") && (true) ) {
2347 rational r(nv.getDuration(), fCurrentDivision*4);
2348 r.rationalise();
2349 dur.set (r.getNumerator(), r.getDenominator());
2350 } else {
2351 rational r = NoteType::type2rational(NoteType::xml(nv.getGraphicType()));
2352 if (r.getNumerator() == 0) // graphic type missing or unknown
2353 r.set (nv.getDuration(), fCurrentDivision*4);
2354 r.rationalise();
2355 rational tm = nv.getTimeModification();
2356 r *= tm;
2357 r.rationalise();
2358 dur.set (r.getNumerator(), r.getDenominator(), nv.getDots());
2359 }
2360 }
2361 else {
2362 rational r = NoteType::type2rational(NoteType::xml(nv.getGraphicType()));
2363 if (r.getNumerator() == 0) // graphic type missing or unknown
2364 r.set (nv.getDuration(), fCurrentDivision*4);
2365 r.rationalise();
2366 rational tm = nv.getTimeModification();
2367 r *= tm;
2368 if (fTremoloInProgress) {
2369 r *= 2.0;
2370 }
2371 r.rationalise();
2372 dur.set (r.getNumerator(), r.getDenominator(), nv.getDots());
2373 }
2374 return dur;
2375 }
2376
2377 //______________________________________________________________________________
newNote(const notevisitor & nv,rational posInMeasure,const S_note & elt)2378 void xmlpart2guido::newNote ( const notevisitor& nv, rational posInMeasure, const S_note& elt)
2379 {
2380 // Fingering is tied to single notes (in chords)
2381 int hasFingerings = 0; // 0 if none, greater otherwise!
2382 if (nv.getFingerings().size()) {
2383 auto fingerings = nv.getFingerings();
2384 for (int i=0; i < fingerings.size(); i++) {
2385 /// GUID-156: If XML default-x bypasses next note, don't include this fingering
2386 // ctree<xmlelement>::iterator nextnote;
2387 // if (findNextNote(elt, nextnote)) {
2388 // int nextNoteDefaultX = nextnote->getAttributeIntValue("default-x", 0);
2389 // int thisNoteDefaultX = elt->getAttributeIntValue("default-x", 0);
2390 // int fingeringDefaultX = fingerings[i]->getAttributeIntValue("default-x", 0);
2391 // if (fingeringDefaultX+thisNoteDefaultX > nextNoteDefaultX) {
2392 // cerr<<"XML2Guido: Fingering X-position ("<<thisNoteDefaultX<<"->"<<fingeringDefaultX<<") on line:"<<fingerings[i]->getInputLineNumber()<<", measure:"<< fMeasNum<<" bypasses proceeding note("<<nextNoteDefaultX<<")! Skipping... ."<<endl;
2393 // continue;
2394 // }
2395 // }
2396 Sguidoelement tag = guidotag::create("fingering");
2397 // Get text value
2398 std::string fingeringText = fingerings[i]->getValue();
2399 stringstream s;
2400 s << "text=\"" << fingeringText << "\"";
2401 /// Get placement: AVOIDING since rendering is not coherent!
2402 // std::string placement = fingerings[i]->getAttributeValue("placement");
2403 // if (placement.size() > 0) {
2404 // s << ", position=\""<<placement<<"\"";
2405 // }
2406 tag->add (guidoparam::create(s.str(), false));
2407 /// GUID-156: x-pos is highly dependent on Layout. AVOID!
2408 //xml2guidovisitor::addPosX(fingerings[i], tag, 0); // xml x-pos can be safely added
2409 /// In MusicXML, default-y for Fingering is from TOP of the staff. Dy in Guido is from the NOTEHEAD. Therefore the dy is a function of the Note and the Clef!
2410 addPosYforNoteHead(nv, fingerings[i], tag, 2); // FIXME: +2 offset is experimental
2411 push(tag);
2412 hasFingerings++;
2413 }
2414 }
2415
2416 int octave = nv.getOctave() - 3; // octave offset between MusicXML and GUIDO is -3
2417 string accident = alter2accident(nv.getAlter());
2418 string name = noteName(nv);
2419 guidonoteduration dur = noteDuration(nv);
2420
2421 Sguidoelement note = guidonote::create(fTargetVoice, name, octave, dur, accident);
2422
2423 /// Force Accidental if accidental XML tag is present
2424 bool forcedAccidental = false;
2425 if (!nv.fCautionary.empty())
2426 {
2427 Sguidoelement accForce = guidotag::create("acc");
2428 push(accForce);
2429 forcedAccidental = true;
2430 }
2431
2432 /// MusicXML from Finale can skip the Cautionary and just enter Accidental for those inclued in the key!
2433 if ((forcedAccidental==false) && (nv.fAccidental.empty() == false)) {
2434 Sguidoelement accForce = guidotag::create("acc");
2435 push(accForce);
2436 forcedAccidental = true;
2437 }
2438
2439 /// Add Note head of X offset for note if necessary
2440 bool noteFormat = false;
2441 int measureNum = fCurrentMeasure->getAttributeIntValue("number", 0);
2442 auto timePos4measure = timePositions.find(measureNum);
2443 if ( (nv.fNotehead
2444 || ((timePos4measure != timePositions.end()) ) ) // if we need to infer default-x
2445 && fInGrace==false ) // FIXME: Workaround for GUID-74
2446 {
2447 Sguidoelement noteFormatTag = guidotag::create("noteFormat");
2448
2449 if (nv.fNotehead) {
2450 std::string noteFormatType = nv.getNoteheadType();
2451
2452 if (noteFormatType.size())
2453 {
2454 stringstream s;
2455 s << "\"" << noteFormatType << "\"";
2456 noteFormatTag->add (guidoparam::create(s.str(), false));
2457 noteFormat = true;
2458 }
2459 }
2460
2461 /// check for dx inference from default_x but avoid doing this for Chords as Guido handles this automatically!
2462 if (timePos4measure != timePositions.end() && (isProcessingChord==false)) {
2463 auto voiceInTimePosition = timePos4measure->second.find(posInMeasure);
2464 if (voiceInTimePosition != timePos4measure->second.end()) {
2465 auto minXPos = std::min_element(voiceInTimePosition->second.begin(),voiceInTimePosition->second.end() );
2466 if (nv.x_default != *minXPos) {
2467 int noteDx = ( (nv.x_default - *minXPos)/ 10 ) * 2; // convert to half spaces
2468
2469 stringstream s;
2470 s << "dx=" << noteDx ;
2471 noteFormatTag->add (guidoparam::create(s.str(), false));
2472 noteFormat = true;
2473 }
2474 }
2475 }
2476
2477
2478 if (noteFormat == true)
2479 {
2480 push(noteFormatTag);
2481 }
2482 }
2483
2484 add (note);
2485
2486 if (noteFormat)
2487 pop();
2488
2489 if (forcedAccidental)
2490 pop();
2491
2492 if (hasFingerings > 0) {
2493 while (hasFingerings>0) {
2494 pop();
2495 hasFingerings--;
2496 }
2497 }
2498
2499 //checkTiedEnd (nv.getTied());
2500 }
2501
checkNoteFormatDx(const notevisitor & nv,rational posInMeasure)2502 int xmlpart2guido::checkNoteFormatDx ( const notevisitor& nv , rational posInMeasure)
2503 {
2504 int measureNum = fCurrentMeasure->getAttributeIntValue("number", 0);
2505 auto timePos4measure = timePositions.find(measureNum);
2506
2507 if ( timePos4measure != timePositions.end())
2508 {
2509 Sguidoelement noteFormatTag = guidotag::create("noteFormat");
2510
2511 /// check for dx inference from default_x but avoid doing this for Chords as Guido handles this automatically!
2512 if (timePos4measure != timePositions.end() && (isProcessingChord==false)) {
2513 auto voiceInTimePosition = timePos4measure->second.find(posInMeasure);
2514 if (voiceInTimePosition != timePos4measure->second.end()) {
2515 auto minXPos = std::min_element(voiceInTimePosition->second.begin(),voiceInTimePosition->second.end() );
2516 if (nv.x_default != *minXPos) {
2517 int noteDx = ( (nv.x_default - *minXPos)/ 10 ) * 2; // convert to half spaces
2518
2519 stringstream s;
2520 s << "dx=" << noteDx ;
2521 noteFormatTag->add (guidoparam::create(s.str(), false));
2522 push(noteFormatTag);
2523 }else
2524 return 0;
2525 }else
2526 return 0;
2527 }
2528 }
2529
2530 return 1;
2531 }
2532
checkRestFormat(const notevisitor & nv)2533 int xmlpart2guido::checkRestFormat ( const notevisitor& nv )
2534 {
2535 if (nv.getStep().size())
2536 {
2537 // Check out clef for position and voice
2538 std::string thisClef = getClef(fCurrentStaffIndex , fCurrentVoicePosition, fMeasNum);
2539 float noteHeadPos=nv.getNoteHeadDy(thisClef);
2540 float restformatDy = noteHeadPos;
2541 // Rest default position in Guido (dy 0) is the middle line of the staff
2542 // in G-Clef, negative is up and positive is down AND -6 offset if counting from G-Clef C4 which is zero for notehead
2543 // in C-clef, same but +6 offset
2544 if (thisClef[0]=='g') {
2545 restformatDy -= 6;
2546 restformatDy = restformatDy * -1.0;
2547 }else if (thisClef[0]=='f') {
2548 restformatDy += 6;
2549 restformatDy = restformatDy * -1.0;
2550 }else if (thisClef[0]=='c') {
2551 restformatDy -= 6;
2552 restformatDy = restformatDy * -1.0;
2553 }
2554
2555 if (restformatDy!=0.0)
2556 {
2557 Sguidoelement restFormatTag = guidotag::create("restFormat");
2558 stringstream s;
2559 s << "dy=" << restformatDy;
2560 restFormatTag->add (guidoparam::create(s.str(), false));
2561 push(restFormatTag);
2562 return 1;
2563 }
2564 }
2565
2566 return 0;
2567 }
2568
getClef(int staffIndex,rational pos,int measureNum)2569 std::string xmlpart2guido::getClef(int staffIndex, rational pos, int measureNum) {
2570 // std::multimap<int, std::pair< int, std::pair< rational, string > > > staffClefMap;
2571 std::string thisClef = "g";
2572 if (staffClefMap.size()>0) {
2573 auto staffRange = staffClefMap.equal_range(staffIndex);
2574
2575 for (auto i = staffRange.first ; i != staffRange.second; i++ )
2576 {
2577 // Get the measure number
2578 if (((i->second).first <= measureNum) && ((i->second).second.first <= pos )){
2579 thisClef = (i->second).second.second;
2580 }else
2581 break;
2582 }
2583 }
2584 return thisClef;
2585 }
2586
2587 //______________________________________________________________________________
visitEnd(S_note & elt)2588 void xmlpart2guido::visitEnd ( S_note& elt )
2589 {
2590 notevisitor::visitEnd ( elt );
2591
2592 if (inChord()) return; // chord notes have already been handled
2593
2594 isProcessingChord = false;
2595
2596 rational thisNoteHeadPosition = fCurrentVoicePosition;
2597
2598 int pendingPops = 0;
2599
2600 bool scanVoice = (notevisitor::getVoice() == fTargetVoice);
2601 if (!isGrace() ) {
2602 //////// Track all voice default-x parameters, as positions in measures
2603
2604 if (true) { // had fNotesOnly
2605 int measureNum = fCurrentMeasure->getAttributeIntValue("number", 0);
2606 auto timePos4measure = timePositions.find(measureNum);
2607 if (notevisitor::x_default != -1) {
2608 if ( timePos4measure != timePositions.end())
2609 {
2610
2611 if (timePos4measure->second.find(fCurrentVoicePosition) != timePos4measure->second.end())
2612 {
2613 // Exists.. push it to vector
2614 timePos4measure->second.find(fCurrentVoicePosition)->second.push_back(notevisitor::x_default);
2615 }else {
2616 // Doesn't exist.. insert with this element's x_default
2617 timePos4measure->second.insert(std::pair<rational, std::vector<int> >
2618 (fCurrentVoicePosition, std::vector<int>(1, notevisitor::x_default)) );
2619 }
2620 }else {
2621 std::map<rational, std::vector<int> > inner;
2622 inner.insert(std::make_pair(fCurrentVoicePosition, std::vector<int>(1, notevisitor::x_default)));
2623 timePositions.insert(std::make_pair(measureNum, inner));
2624 }
2625 }
2626 }
2627
2628 moveMeasureTime (getDuration(), scanVoice);
2629 checkDelayed (getDuration()); // check for delayed elements (directions with offset)
2630 }
2631 if (!scanVoice) return;
2632
2633 if ((fTremoloInProgress)&&(fTremolo && (fTremolo->getAttributeValue("type")=="stop"))) {
2634 fTremoloInProgress = false;
2635 pop();
2636 checkTupletEnd(notevisitor::getTuplet());
2637 checkSlurEnd (notevisitor::getSlur());
2638 checkBeamEnd (notevisitor::getBeam());
2639 return;
2640 }
2641
2642 checkStaff(notevisitor::getStaff());
2643
2644 checkVoiceTime (fCurrentMeasurePosition, fCurrentVoicePosition);
2645
2646 checkCue(*this);
2647 if (notevisitor::getType() != notevisitor::kRest)
2648 checkStem (notevisitor::fStem);
2649
2650 checkGrace(*this);
2651 checkSlurBegin (notevisitor::getSlur());
2652 checkBeamBegin (notevisitor::getBeam());
2653 checkTupletBegin(notevisitor::getTuplet(), *this, elt);
2654 checkLyricBegin (notevisitor::getLyric());
2655 checkWavyTrillBegin(*this);
2656
2657 pendingPops += checkFermata(*this);
2658 pendingPops += checkArticulation(*this);
2659
2660 int chordOrnaments = checkChordOrnaments(*this);
2661 pendingPops += chordOrnaments;
2662
2663 pendingPops += checkTremolo(*this, elt); // non-measured tremolos will be popped upon "stop" and not counted here
2664
2665 if (notevisitor::getType()==kRest)
2666 pendingPops += checkRestFormat(*this);
2667
2668 // Check for TIES before creating the chord sequence. \tieBeing and \tieEnd should live OUTSIDE chord sequence in Guido
2669 checkTiedBegin((*this).getTied());
2670
2671 vector<Sxmlelement> chord = getChord(elt);
2672 if (chord.size())
2673 {
2674 Sguidoelement chord = guidochord::create();
2675 //// FIXME: The following line removed since it introduced bad behavior on REINE de La NUIT Piano accompaniment!
2676 //pendingPops += checkNoteFormatDx(*this, thisNoteHeadPosition);
2677 push (chord);
2678 pendingPops++;
2679 isProcessingChord = true;
2680 }
2681
2682 newNote (*this, thisNoteHeadPosition, elt);
2683 // Add chord notes (in case of a real chord)
2684 for (vector<Sxmlelement>::const_iterator iter = chord.begin(); iter != chord.end(); iter++) {
2685 isProcessingChord = true;
2686 notevisitor nv;
2687 xml_tree_browser browser(&nv);
2688 Sxmlelement note = *iter;
2689 browser.browse(*note);
2690 checkStaff(nv.getStaff());
2691 newNote (nv, thisNoteHeadPosition, elt);
2692 }
2693
2694 checkTiedEnd((*this).getTied());
2695 isProcessingChord = false;
2696
2697 while (pendingPops--) pop();
2698
2699 checkWavyTrillEnd(*this);
2700 checkLyricEnd (notevisitor::getLyric());
2701
2702 checkTupletEnd(notevisitor::getTuplet());
2703 checkSlurEnd (notevisitor::getSlur());
2704 checkBeamEnd (notevisitor::getBeam());
2705
2706 checkGraceEnd(*this); // This will end GUIDO Grace tag, before any collision with a S_direction
2707
2708 // GUID-126: Do not close texts while a Tuplet is nested inside or expect rendering issues with Tuplet texts.
2709 if (fTupletOpen <= 0)
2710 {
2711 // this is will close any ongoing Guido TEXT tag once a sequence is embedded
2712 // In case of ongoing \tuplet, do it after the \tuplet is closed! (Potential Guido parser issue)
2713 checkTextEnd();
2714 }
2715
2716 /*
2717 if (fBeamStack.size()==0)
2718 {
2719 // this is will close any ongoing Guido TEXT tag once a sequence is embedded
2720 // In case of ongoing \Beam, do it after the \beam is closed! (Potential Guido parser issue)
2721 checkTextEnd();
2722 }*/
2723
2724 checkPostArticulation(*this);
2725
2726
2727 fMeasureEmpty = false;
2728 }
2729
2730 //______________________________________________________________________________
2731 // time management
2732 //______________________________________________________________________________
visitStart(S_divisions & elt)2733 void xmlpart2guido::visitStart ( S_divisions& elt )
2734 {
2735 fCurrentDivision = (long)(*elt);
2736 }
2737
2738 // MARK: Tag Add Methods using element parsing
xPosFromTimePos(float default_x,float relative_x)2739 float xmlpart2guido::xPosFromTimePos(float default_x, float relative_x) {
2740 auto timePos4measure = timePositions.find(fMeasNum);
2741
2742 float xpos = default_x + relative_x;
2743
2744 if ((xpos!=0)&&(timePos4measure != timePositions.end())) {
2745 auto voiceInTimePosition = timePos4measure->second.find(fCurrentVoicePosition);
2746 if (voiceInTimePosition != timePos4measure->second.end()) {
2747 auto minXPos = std::min_element(voiceInTimePosition->second.begin(),voiceInTimePosition->second.end() );
2748 if (xpos != *minXPos) {
2749 int finalDx = (relative_x/10)*2;
2750 // apply default-x ONLY if it exists
2751 if (default_x!=0)
2752 finalDx = ( (xpos - *minXPos)/ 10 ) * 2; // convert to half spaces
2753
2754 /// FIXME: Can't handle OFFSET with Guido! If positive, just add a small value for coherence!
2755 if (fCurrentOffset>0)
2756 finalDx +=3;
2757
2758 return finalDx;
2759 }
2760 }//else {
2761 // cerr<<"ERROR: NO TIME POS FOR VOICE POSITION"<<fCurrentVoicePosition.toString()<<" TO INFER Dx for DYNAMICS!"<<endl;
2762 //}
2763 }
2764 return -999; // This is when the xpos can not be computed
2765 }
2766
findNextNote(const S_note & elt,ctree<xmlelement>::iterator & nextnote)2767 bool xmlpart2guido::findNextNote(const S_note& elt, ctree<xmlelement>::iterator &nextnote) {
2768 ctree<xmlelement>::iterator nextnotetmp = find(fCurrentMeasure->begin(), fCurrentMeasure->end(), elt);
2769 if (nextnotetmp != fCurrentMeasure->end()) nextnotetmp++; // advance one step
2770 while (nextnotetmp != fCurrentMeasure->end()) {
2771 // looking for the next note on the target voice
2772 if ((nextnotetmp->getType() == k_note) && (nextnotetmp->getIntValue(k_voice,0) == fTargetVoice)) {
2773 nextnote = nextnotetmp;
2774 return true;
2775 }
2776 nextnotetmp++;
2777 }
2778 return false;
2779 }
2780
addPosYforNoteHead(const notevisitor & nv,Sxmlelement elt,Sguidoelement & tag,float offset)2781 void xmlpart2guido::addPosYforNoteHead(const notevisitor& nv, Sxmlelement elt, Sguidoelement& tag, float offset) {
2782 std::string thisClef = getClef(fCurrentStaffIndex , fCurrentVoicePosition, fMeasNum);
2783 float noteHeadDy = nv.getNoteHeadDy(thisClef);
2784 float xmlY = xml2guidovisitor::getYposition(elt, 0, true);
2785 /// Notehead placement from top of the staff is (noteheaddy - 10) for G-Clef, and for F-Clef: (2.0 - noteheaddy)
2786 float noteDistanceFromStaffTop = 0.0;
2787 if (thisClef[0]=='g') {
2788 noteDistanceFromStaffTop = (noteHeadDy - 10.0);
2789 }else if (thisClef[0]=='f') {
2790 noteDistanceFromStaffTop = noteHeadDy; //(2.0 - noteHeadDy)
2791 }else if (thisClef[0]=='c') {
2792 noteDistanceFromStaffTop = (noteHeadDy - 10.0);
2793 }
2794 float posy = xmlY - noteDistanceFromStaffTop + offset ;
2795 if (posy) {
2796 stringstream s;
2797 s << "dy=" << posy << "hs";
2798 tag->add (guidoparam::create(s.str(), false));
2799 }
2800
2801 //cerr << "addPosYforNoteHead for "<< elt->getName()<<" line:"<< elt->getInputLineNumber()<<" meas:"<<fMeasNum<< " note:"<<nv.getStep()<<nv.getOctave() <<" xmlY="<<xmlY<<" noteHeadDy="<<noteHeadDy<< " noteDistanceFromStaffTop="<<noteDistanceFromStaffTop <<" ->pos="<<posy<<endl;
2802
2803 }
2804
getNoteDistanceFromStaffTop(const notevisitor & nv)2805 float xmlpart2guido::getNoteDistanceFromStaffTop(const notevisitor& nv) {
2806 std::string thisClef = getClef(fCurrentStaffIndex , fCurrentVoicePosition, fMeasNum);
2807 float noteHeadDy = nv.getNoteHeadDy(thisClef);
2808 /// Notehead placement from top of the staff is (noteheaddy - 10) for G-Clef, and for F-Clef: (2.0 - noteheaddy)
2809 float noteDistanceFromStaffTop = 0.0;
2810 if (thisClef[0]=='g') {
2811 noteDistanceFromStaffTop = (noteHeadDy - 10.0);
2812 }else if (thisClef[0]=='f') {
2813 noteDistanceFromStaffTop = (2.0 - noteHeadDy);
2814 }else if (thisClef[0]=='c') {
2815 noteDistanceFromStaffTop = (noteHeadDy - 10.0);
2816 }
2817
2818 return noteDistanceFromStaffTop;
2819 }
2820
2821 }
2822