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