1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4   Rosegarden
5   A MIDI and audio sequencer and musical notation editor.
6   Copyright 2000-2021 the Rosegarden development team.
7 
8   This file is Copyright 2002
9   Hans Kieserman      <hkieserman@mail.com>
10   with heavy lifting from csoundio as it was on 13/5/2002.
11 
12   Numerous additions and bug fixes by
13   Michael McIntyre    <dmmcintyr@users.sourceforge.net>
14 
15   Some restructuring by Chris Cannam.
16 
17   Massive brain surgery, fixes, improvements, and additions by
18   Heikki Junes
19 
20   Other copyrights also apply to some parts of this work.  Please
21   see the AUTHORS file and individual file headers for details.
22 
23   This program is free software; you can redistribute it and/or
24   modify it under the terms of the GNU General Public License as
25   published by the Free Software Foundation; either version 2 of the
26   License, or (at your option) any later version.  See the file
27   COPYING included with this distribution for more information.
28 */
29 
30 #define RG_MODULE_STRING "[LilyPondExporter]"
31 #define RG_NO_DEBUG_PRINT 1
32 
33 #include "LilyPondExporter.h"
34 #include "LilyPondSegmentsContext.h"
35 
36 #include "misc/Debug.h"
37 #include "misc/Strings.h"
38 #include "misc/ConfigGroups.h"
39 #include "base/BaseProperties.h"
40 #include "base/Composition.h"
41 #include "base/Configuration.h"
42 #include "base/Event.h"
43 #include "base/Exception.h"
44 #include "base/Instrument.h"
45 #include "base/NotationTypes.h"
46 #include "base/PropertyName.h"
47 #include "base/Segment.h"
48 #include "base/SegmentNotationHelper.h"
49 #include "base/Sets.h"
50 #include "base/Studio.h"
51 #include "base/Track.h"
52 #include "base/NotationQuantizer.h"
53 #include "base/Marker.h"
54 #include "base/StaffExportTypes.h"
55 #include "document/RosegardenDocument.h"
56 #include "gui/application/RosegardenApplication.h"
57 #include "gui/application/RosegardenMainViewWidget.h"
58 #include "gui/editors/notation/NotationProperties.h"
59 #include "gui/editors/notation/NotationView.h"
60 #include "gui/editors/guitar/Chord.h"
61 
62 #include "rosegarden-version.h"
63 
64 #include <QSettings>
65 #include <QMessageBox>
66 #include <QFileInfo>
67 #include <QObject>
68 #include <QProgressDialog>
69 #include <QRegularExpression>
70 #include <QString>
71 #include <QApplication>
72 
73 #include <sstream>
74 #include <algorithm>
75 #include <limits>
76 
77 namespace Rosegarden
78 {
79 
headerDedication()80 const char* headerDedication() { return "dedication"; }
headerTitle()81 const char* headerTitle() { return "title"; }
headerSubtitle()82 const char* headerSubtitle() { return "subtitle"; }
headerSubsubtitle()83 const char* headerSubsubtitle() { return "subsubtitle"; }
headerPoet()84 const char* headerPoet() { return "poet"; }
headerComposer()85 const char* headerComposer() { return "composer"; }
headerMeter()86 const char* headerMeter() { return "meter"; }
headerOpus()87 const char* headerOpus() { return "opus"; }
headerArranger()88 const char* headerArranger() { return "arranger"; }
headerInstrument()89 const char* headerInstrument() { return "instrument"; }
headerPiece()90 const char* headerPiece() { return "piece"; }
headerCopyright()91 const char* headerCopyright() { return "copyright"; }
headerTagline()92 const char* headerTagline() { return "tagline"; }
93 
94 using namespace BaseProperties;
95 
LilyPondExporter(RosegardenDocument * doc,const SegmentSelection & selection,const std::string & fileName,NotationView * parent)96 LilyPondExporter::LilyPondExporter(RosegardenDocument *doc,
97                                    const SegmentSelection &selection,
98                                    const std::string &fileName,
99                                    NotationView *parent) :
100     m_doc(doc),
101     m_fileName(fileName),
102     m_lastClefFound(Clef::Treble),
103     m_selection(selection),
104     SKIP_PROPERTY("LilyPondExportSkipThisEvent")
105 {
106     m_composition = &m_doc->getComposition();
107     m_studio = &m_doc->getStudio();
108     m_notationView = parent;
109 
110     readConfigVariables();
111     m_language = LilyPondLanguage::create(m_exportNoteLanguage);
112 }
113 
114 void
readConfigVariables()115 LilyPondExporter::readConfigVariables()
116 {
117     // grab settings info
118     QSettings settings;
119     settings.beginGroup(LilyPondExportConfigGroup);
120 
121     m_paperSize = settings.value("lilypapersize", PAPER_A4).toUInt();
122     m_paperLandscape = qStrToBool(settings.value("lilypaperlandscape", "false"));
123     m_fontSize = settings.value("lilyfontsize", FONT_20).toUInt();
124     m_raggedBottom = qStrToBool(settings.value("lilyraggedbottom", "false"));
125     m_exportEmptyStaves = qStrToBool(settings.value("lilyexportemptystaves", "false"));
126     m_useShortNames = qStrToBool(settings.value("lilyuseshortnames", "true"));
127     m_exportSelection = settings.value("lilyexportselection", EXPORT_NONMUTED_TRACKS).toUInt();
128     if (settings.value("lilyexporteditedsegments", "false").toBool()) {
129         m_exportSelection = EXPORT_EDITED_SEGMENTS;
130     }
131     m_exportLyrics = settings.value("lilyexportlyrics", EXPORT_LYRICS_LEFT).toUInt();
132     m_exportTempoMarks = settings.value("lilyexporttempomarks", EXPORT_NONE_TEMPO_MARKS).toUInt();
133     m_exportBeams = qStrToBool(settings.value("lilyexportbeamings", "false"));
134     m_exportStaffGroup = qStrToBool(settings.value("lilyexportstaffbrackets", "true"));
135 
136     m_languageLevel = settings.value("lilylanguage", LILYPOND_VERSION_2_12).toUInt();
137     m_exportMarkerMode = settings.value("lilyexportmarkermode", EXPORT_NO_MARKERS).toUInt();
138     m_exportNoteLanguage = settings.value("lilyexportnotelanguage", LilyPondLanguage::NEDERLANDS).toUInt();
139     m_chordNamesMode = qStrToBool(settings.value("lilychordnamesmode", "false"));
140 //    m_repeatMode = settings.value("lilyrepeatmode", REPEAT_BASIC).toUInt();
141     m_repeatMode = settings.value("lilyexportrepeat", "true").toBool() ? REPEAT_VOLTA : REPEAT_UNFOLD;
142     m_voltaBar = settings.value("lilydrawbaratvolta", "true").toBool();
143     m_cancelAccidentals = settings.value("lilycancelaccidentals", "false").toBool();
144     m_fingeringsInStaff = settings.value("lilyfingeringsinstaff", "true").toBool();
145     settings.endGroup();
146 }
147 
148 // Return true if @p event is allowed to start or end a beam group.
149 // Only called for GROUP_TYPE_BEAMED.
canStartOrEndBeam(Event * event)150 static bool canStartOrEndBeam(Event *event)
151 {
152     // Rests cannot start or end beams
153     if (!event->isa(Note::EventType))
154         return false;
155 
156     // Is it really beamed? quarter and longer notes cannot be
157     // (ex: bug #1705430, beaming groups erroneous after merging notes)
158     // HJJ: This should be fixed in notation engine,
159     //      after which the workaround below should be removed.
160     const int noteType = event->get<Int>(NOTE_TYPE);
161     if (noteType >= Note::QuarterNote)
162         return false;
163 
164     return true;
165 }
166 
nextNoteInGroup(Segment * s,Segment::iterator it,const std::string & groupType,int barEnd) const167 Event *LilyPondExporter::nextNoteInGroup(Segment *s, Segment::iterator it, const std::string &groupType, int barEnd) const
168 {
169     Event *event = *it;
170     long currentGroupId = -1;
171     event->get<Int>(BEAMED_GROUP_ID, currentGroupId);
172     Q_ASSERT(currentGroupId != -1);
173     const bool tuplet = groupType == GROUP_TYPE_TUPLED;
174     const bool graceNotesGroup = event->has(IS_GRACE_NOTE) && event->get<Bool>(IS_GRACE_NOTE);
175     timeT currentTime = m_composition->getNotationQuantizer()->getQuantizedAbsoluteTime(event);
176     int subOrdering = event->getSubOrdering();
177 
178     ++it;
179     for ( ; s->isBeforeEndMarker(it) ; ++it ) {
180         event = *it;
181 
182         if (event->getNotationAbsoluteTime() >= barEnd)
183             break;
184 
185         // Grace notes shouldn't break the beaming group of real notes
186         const bool isGrace = (event->has(IS_GRACE_NOTE) && event->get<Bool>(IS_GRACE_NOTE));
187         if (!graceNotesGroup && isGrace)
188             continue;
189 
190         if (event->has(SKIP_PROPERTY))
191             continue;
192 
193         const bool isNote = event->isa(Note::EventType);
194 
195         // Rests at the end of a beam are not included into it.
196         // Rests in the middle of a beam are included, so we keep looking.
197         // Same thing for symbols, etc.
198         if (!tuplet && !isNote)
199             continue;
200         // Tuplets don't get broken by non-notes (e.g. pitchbend)
201         if (tuplet && (!isNote && !event->isa(Note::EventRestType)))
202             continue;
203 
204         // Within a chord, keep moving ahead
205         const timeT eventTime = m_composition->getNotationQuantizer()->getQuantizedAbsoluteTime(event);
206         if (eventTime == currentTime && subOrdering == event->getSubOrdering()) {
207             continue;
208         }
209         currentTime = eventTime;
210         subOrdering = event->getSubOrdering();
211 
212         long newGroupId = -1;
213         event->get<Int>(BEAMED_GROUP_ID, newGroupId);
214 
215         if (!tuplet && !canStartOrEndBeam(event)) {
216             newGroupId = -1;
217         }
218 
219         if (newGroupId == -1 || newGroupId != currentGroupId) {
220             return nullptr;
221         }
222         return event;
223     }
224     return nullptr;
225 }
226 
~LilyPondExporter()227 LilyPondExporter::~LilyPondExporter()
228 {
229     delete(m_language);
230 }
231 
232 bool
isSegmentToPrint(Segment * seg)233 LilyPondExporter::isSegmentToPrint(Segment *seg)
234 {
235     bool currentSegmentSelected = false;
236     if ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && !m_selection.empty()) {
237         //
238         // Check whether the current segment is in the list of selected segments.
239         //
240         for (SegmentSelection::iterator it = m_selection.begin(); it != m_selection.end(); ++it) {
241             if ((*it) == seg) currentSegmentSelected = true;
242         }
243     } else if ((m_exportSelection == EXPORT_EDITED_SEGMENTS) && (m_notationView != nullptr)) {
244         currentSegmentSelected = m_notationView->hasSegment(seg);
245     }
246 
247     // Check whether the track is a non-midi track.
248     Track *track = m_composition->getTrackById(seg->getTrack());
249     InstrumentId instrumentId = track->getInstrument();
250     bool isMidiTrack = instrumentId >= MidiInstrumentBase;
251 
252     // Check for notation flag
253     bool isForNotation = (seg->getForNotation());
254 
255     // Look for various printing selection modes
256     bool ok1 = m_exportSelection == EXPORT_ALL_TRACKS;
257     bool ok2 = (m_exportSelection == EXPORT_NONMUTED_TRACKS) && (!track->isMuted());
258     bool ok3 = (m_exportSelection == EXPORT_SELECTED_TRACK)
259                && (track->getId() == m_composition->getSelectedTrack());
260     bool ok4 = (m_exportSelection == EXPORT_SELECTED_SEGMENTS) && currentSegmentSelected;
261     bool ok5 = (m_exportSelection == EXPORT_EDITED_SEGMENTS) && currentSegmentSelected;
262 
263     // Skip non-midi tracks and return true if segment is selected
264     return isForNotation && isMidiTrack && (ok1 || ok2 || ok3 || ok4 || ok5);
265 }
266 
267 
268 void
handleStartingPreEvents(eventstartlist & preEventsToStart,std::ofstream & str)269 LilyPondExporter::handleStartingPreEvents(eventstartlist &preEventsToStart,
270                                           std::ofstream &str)
271 {
272     eventstartlist::iterator m = preEventsToStart.begin();
273 
274     while (m != preEventsToStart.end()) {
275 
276         try {
277             Indication i(**m);
278 
279             if (i.getIndicationType() == Indication::QuindicesimaUp) {
280                 str << "\\ottava #2 ";
281             } else if (i.getIndicationType() == Indication::OttavaUp) {
282                 str << "\\ottava #1 ";
283             } else if (i.getIndicationType() == Indication::OttavaDown) {
284                 str << "\\ottava #-1 ";
285             } else if (i.getIndicationType() == Indication::QuindicesimaDown) {
286                 str << "\\ottava #-2 ";
287             }
288 
289         } catch (const Event::BadType &) {
290             // Not an indication
291         } catch (const Event::NoData &e) {
292             RG_WARNING << "Bad indication: " << e.getMessage();
293         }
294 
295         eventstartlist::iterator n(m);
296         ++n;
297         preEventsToStart.erase(m);
298         m = n;
299     }
300 }
301 
302 void
handleStartingPostEvents(eventstartlist & postEventsToStart,std::ofstream & str)303 LilyPondExporter::handleStartingPostEvents(eventstartlist &postEventsToStart,
304                                            std::ofstream &str)
305 {
306     eventstartlist::iterator m = postEventsToStart.begin();
307 
308     while (m != postEventsToStart.end()) {
309 
310         try {
311             Indication i(**m);
312 
313             if (i.getIndicationType() == Indication::Slur) {
314                 if ((*m)->get
315                     <Bool>(NotationProperties::SLUR_ABOVE))
316                     str << "^( ";
317                 else
318                     str << "_( ";
319             } else if (i.getIndicationType() == Indication::PhrasingSlur) {
320                 if ((*m)->get
321                     <Bool>(NotationProperties::SLUR_ABOVE))
322                     str << "^\\( ";
323                 else
324                     str << "_\\( ";
325             } else if (i.getIndicationType() == Indication::Crescendo) {
326                 str << "\\< ";
327             } else if (i.getIndicationType() == Indication::Decrescendo) {
328                 str << "\\> ";
329 // Don't seem useful and sometimes are harmful
330 //             } else if (i.getIndicationType() == Indication::QuindicesimaUp) {
331 //                 // #(set-octavation 2) ... #(set-octavation 0)
332 //                 //str << "\\ottava #2 ";
333 //             } else if (i.getIndicationType() == Indication::OttavaUp) {
334 //                 // #(set-octavation 1) ... #(set-octavation 0)
335 //                 str << "\\ottava #1 ";
336 //             } else if (i.getIndicationType() == Indication::OttavaDown) {
337 //                 // #(set-octavation -1) ... #(set-octavation 0)
338 //                 str << "\\ottava #-1 ";
339 //             } else if (i.getIndicationType() == Indication::QuindicesimaDown) {
340 //                 // #(set-octavation -2) ... #(set-octavation 0)
341 //                 str << "\\ottava #-2 ";
342             } else if (i.getIndicationType() == Indication::TrillLine) {
343                 str << "\\startTrillSpan ";
344             }
345 
346         } catch (const Event::BadType &) {
347             // Not an indication
348             // Check for sustainDown or sustainUp events
349             if ((*m)->isa(Controller::EventType) &&
350                 (*m)->has(Controller::NUMBER) &&
351                 (*m)->has(Controller::VALUE)) {
352                 if ((*m)->get <Int>(Controller::NUMBER) == 64) {
353                     //
354                     // As a first approximation, any positive value for
355                     // the pedal event results in a new "Ped." marking.
356                     //
357                     // If the pedals have been entered with a midi piano,
358                     // the pedal may have continuous values from 0 to 127
359                     // and there may appear funny output with plenty of
360                     // "Ped." marks indicating the change of pedal pressure.
361                     //
362                     // One could use the following code to make the pedal
363                     // marks transparent, but the invisible syntax has to
364                     // be put before the note, while the pedal syntax goes
365                     // after the note. Therefore, the following does not work:
366                     //
367                     //   c' \sustainUp \once \overr...#'transparent \sustainDown
368                     //
369                     // If a solution which allows to hide the pedal marks,
370                     // the example code below which shows how to hide the marks
371                     // can be removed.
372                     //
373                     /*
374                      *if ((*m)->has(INVISIBLE) && (*m)->get <Bool>(INVISIBLE)) {
375                      *    str << "\\once \\override Staff.SustainPedal #'transparent = ##t ";
376                      *}
377                      */
378 
379                     // NOTE: sustain syntax changed in LilyPond 2.12
380                     if ((*m)->get <Int>(Controller::VALUE) > 0) {
381                         str << "\\sustain" << (m_languageLevel < LILYPOND_VERSION_2_12 ? "Down " : "On ");
382                     } else {
383                         str << "\\sustain" << (m_languageLevel < LILYPOND_VERSION_2_12 ? "Up " : "Off ");
384                     }
385                 }
386             }
387         } catch (const Event::NoData &e) {
388             RG_WARNING << "Bad indication: " << e.getMessage();
389         }
390 
391         eventstartlist::iterator n(m);
392         ++n;
393         postEventsToStart.erase(m);
394         m = n;
395     }
396 }
397 
398 void
handleEndingPreEvents(eventendlist & preEventsInProgress,const Segment::iterator & j,std::ofstream & str)399 LilyPondExporter::handleEndingPreEvents(eventendlist &preEventsInProgress,
400                                         const Segment::iterator &j,
401                                         std::ofstream &str)
402 {
403     eventendlist::iterator k = preEventsInProgress.begin();
404 
405     while (k != preEventsInProgress.end()) {
406 
407         // Increment before use.  This avoids invalidating k if the element
408         // at l is erased.
409         eventendlist::iterator l(k++);
410 
411         // Handle and remove all the relevant events in value()
412         // This assumes all deferred events are indications
413 
414         try {
415             Indication i(**l);
416 
417             timeT indicationEnd =
418                 (*l)->getNotationAbsoluteTime() + i.getIndicationDuration();
419             timeT eventEnd =
420                 (*j)->getNotationAbsoluteTime() + (*j)->getNotationDuration();
421 
422             if (indicationEnd < eventEnd ||
423                 ((i.getIndicationType() == Indication::Slur ||
424                   i.getIndicationType() == Indication::PhrasingSlur) &&
425                  indicationEnd == eventEnd)) {
426 
427                 if (i.getIndicationType() == Indication::QuindicesimaUp) {
428                     str << "\\ottava #0 ";
429                 } else if (i.getIndicationType() == Indication::OttavaUp) {
430                     str << "\\ottava #0 ";
431                 } else if (i.getIndicationType() == Indication::OttavaDown) {
432                     str << "\\ottava #0 ";
433                 } else if (i.getIndicationType() == Indication::QuindicesimaDown) {
434                     str << "\\ottava #0 ";
435                 }
436 
437                 preEventsInProgress.erase(l);
438             }
439 
440         } catch (const Event::BadType &) {
441             // not an indication
442 
443         } catch (const Event::NoData &e) {
444             RG_WARNING << "Bad indication: " << e.getMessage();
445         }
446     }
447 }
448 
449 void
handleEndingPostEvents(eventendlist & postEventsInProgress,const Segment::iterator & j,std::ofstream & str)450 LilyPondExporter::handleEndingPostEvents(eventendlist &postEventsInProgress,
451                                          const Segment::iterator &j,
452                                          std::ofstream &str)
453 {
454     eventendlist::iterator k = postEventsInProgress.begin();
455 
456     while (k != postEventsInProgress.end()) {
457 
458         // Increment before use.  This avoids invalidating k if the element
459         // at l is erased.
460         eventendlist::iterator l(k++);
461 
462         // Handle and remove all the relevant events in value()
463         // This assumes all deferred events are indications
464 
465         try {
466             Indication i(**l);
467 
468             timeT indicationEnd =
469                 (*l)->getNotationAbsoluteTime() + i.getIndicationDuration();
470             timeT eventEnd =
471                 (*j)->getNotationAbsoluteTime() + (*j)->getNotationDuration();
472 
473             if (indicationEnd < eventEnd ||
474                 ((i.getIndicationType() == Indication::Slur ||
475                   i.getIndicationType() == Indication::PhrasingSlur) &&
476                  indicationEnd == eventEnd)) {
477 
478                 if (i.getIndicationType() == Indication::Slur) {
479                     str << ") ";
480                 } else if (i.getIndicationType() == Indication::PhrasingSlur) {
481                     str << "\\) ";
482                 } else if (i.getIndicationType() == Indication::Crescendo ||
483                            i.getIndicationType() == Indication::Decrescendo) {
484                     str << "\\! ";
485                 } else if (i.getIndicationType() == Indication::TrillLine) {
486                     str << "\\stopTrillSpan ";
487                 }
488 
489                 postEventsInProgress.erase(l);
490             }
491 
492         } catch (const Event::BadType &) {
493             // not an indication
494 
495         } catch (const Event::NoData &e) {
496             RG_WARNING << "Bad indication: " << e.getMessage();
497         }
498     }
499 }
500 
501 std::string
convertPitchToLilyNoteName(int pitch,Accidental accidental,const Rosegarden::Key & key)502 LilyPondExporter::convertPitchToLilyNoteName(int pitch, Accidental accidental,
503                                              const Rosegarden::Key &key)
504 {
505     Pitch p(pitch, accidental);
506     char noteName = (char)tolower(p.getNoteName(key));
507     Accidental acc = p.getAccidental(key);
508     std::string lilyNote = m_language->getLilyNote(noteName, acc);
509     return lilyNote;
510 }
511 
512 std::string
convertPitchToLilyNote(int pitch,Accidental accidental,const Rosegarden::Key & key)513 LilyPondExporter::convertPitchToLilyNote(int pitch, Accidental accidental,
514                                          const Rosegarden::Key &key)
515 {
516     // calculate note name and write note
517     std::string lilyNote = convertPitchToLilyNoteName(pitch, accidental, key);
518 
519     // generate and write octave marks
520     std::string octaveMarks = "";
521     int octave = (int)(pitch / 12);
522 
523     // tweak the octave break for B# / Cb
524     Pitch p(pitch, accidental);
525     char noteName = (char)tolower(p.getNoteName(key));
526     Accidental acc = p.getAccidental(key);
527     if (noteName == 'b' &&
528         (acc == Accidentals::Sharp || acc == Accidentals::DoubleSharp)) {
529         octave--;
530     } else if (noteName == 'c' &&
531                (acc == Accidentals::Flat || acc == Accidentals::DoubleFlat)) {
532         octave++;
533     }
534 
535     if (octave < 4) {
536         for (; octave < 4; octave++)
537             octaveMarks += ",";
538     } else {
539         for (; octave > 4; octave--)
540             octaveMarks += "\'";
541     }
542 
543     lilyNote += octaveMarks;
544 
545     return lilyNote;
546 }
547 
548 std::string
composeLilyMark(std::string eventMark,bool stemUp)549 LilyPondExporter::composeLilyMark(std::string eventMark, bool stemUp)
550 {
551 
552     std::string inStr = "", outStr = "";
553     std::string prefix = (stemUp) ? "_" : "^";
554 
555     // shoot text mark straight through unless it's sf or rf
556     if (Marks::isTextMark(eventMark)) {
557         inStr = protectIllegalChars(Marks::getTextFromMark(eventMark));
558 
559         if (inStr == "sf") {
560             inStr = "\\sf";
561         } else if (inStr == "rf") {
562             inStr = "\\rfz";
563         } else {
564             inStr = "\\markup { \\italic " + inStr + " } ";
565         }
566 
567         outStr = prefix + inStr;
568 
569     } else if (Marks::isFingeringMark(eventMark)) {
570 
571         // fingering marks: use markup syntax only for non-trivial fingerings
572 
573         inStr = protectIllegalChars(Marks::getFingeringFromMark(eventMark));
574 
575         if (inStr != "0" && inStr != "1" && inStr != "2" && inStr != "3" && inStr != "4" && inStr != "5" && inStr != "+") {
576             inStr = "\\markup { \\finger \"" + inStr + "\" } ";
577         }
578 
579         outStr = prefix + inStr;
580 
581     } else {
582         outStr = "-";
583 
584         // use full \accent format for everything, even though some shortcuts
585         // exist, for the sake of consistency
586         if (eventMark == Marks::Accent) {
587             outStr += "\\accent";
588         } else if (eventMark == Marks::Tenuto) {
589             outStr += "\\tenuto";
590         } else if (eventMark == Marks::Staccato) {
591             outStr += "\\staccato";
592         } else if (eventMark == Marks::Staccatissimo) {
593             outStr += "\\staccatissimo";
594         } else if (eventMark == Marks::Marcato) {
595             outStr += "\\marcato";
596         } else if (eventMark == Marks::Open) {
597             outStr += "\\open";
598         } else if (eventMark == Marks::Stopped) {
599             outStr += "\\stopped";
600         } else if (eventMark == Marks::Harmonic) {
601             outStr += "\\flageolet"; // flageolets are violin harmonics, apparently
602         } else if (eventMark == Marks::Trill) {
603             outStr += "\\trill";
604         } else if (eventMark == Marks::LongTrill) {
605             // span trill up to the next note:
606             // tweak the beginning of the next note using an invisible rest having zero length
607             outStr += "\\startTrillSpan s4*0 \\stopTrillSpan";
608         } else if (eventMark == Marks::Turn) {
609             outStr += "\\turn";
610         } else if (eventMark == Marks::Pause) {
611             outStr += "\\fermata";
612         } else if (eventMark == Marks::UpBow) {
613             outStr += "\\upbow";
614         } else if (eventMark == Marks::DownBow) {
615             outStr += "\\downbow";
616         } else if (eventMark == Marks::Mordent) {
617             outStr += "\\mordent";
618         } else if (eventMark == Marks::MordentInverted) {
619             outStr += "\\prall";
620         } else if (eventMark == Marks::MordentLong) {
621             outStr += "\\prallmordent";
622         } else if (eventMark == Marks::MordentLongInverted) {
623             outStr += "\\prallprall";
624         } else {
625             outStr = "";
626             RG_WARNING << "LilyPondExporter::composeLilyMark() - unhandled mark: " << eventMark;
627         }
628     }
629 
630     return outStr;
631 }
632 
633 std::string
indent(const int & column)634 LilyPondExporter::indent(const int &column)
635 {
636     std::string outStr = "";
637     for (int c = 1; c <= column; c++) {
638         outStr += "    ";
639     }
640     return outStr;
641 }
642 
643 std::string
protectIllegalChars(std::string inStr)644 LilyPondExporter::protectIllegalChars(std::string inStr)
645 {
646 
647     QString tmpStr = strtoqstr(inStr);
648 
649     tmpStr.replace(QRegularExpression("&"), "\\&");
650     tmpStr.replace(QRegularExpression("\\^"), "\\^");
651     tmpStr.replace(QRegularExpression("%"), "\\%");
652     tmpStr.replace(QRegularExpression("<"), "\\<");
653     tmpStr.replace(QRegularExpression(">"), "\\>");
654     tmpStr.replace(QRegularExpression("\\["), "");
655     tmpStr.replace(QRegularExpression("\\]"), "");
656     tmpStr.replace(QRegularExpression("\\{"), "");
657     tmpStr.replace(QRegularExpression("\\}"), "");
658     tmpStr.replace(QRegularExpression("\""), "\\\"");
659 
660     //
661     // LilyPond uses utf8 encoding.
662     //
663     return tmpStr.toUtf8().data();
664 }
665 
666 struct MarkerComp {
667     // Sort Markers by time
668     // Perhaps this should be made generic with a template?
operator ()Rosegarden::MarkerComp669     bool operator()(Marker *a, Marker *b) {
670         return a->getTime() < b->getTime();
671     }
672 };
673 
674 bool
write()675 LilyPondExporter::write()
676 {
677     m_warningMessage = "";
678     QString tmpName = strtoqstr(m_fileName);
679 
680     // dmm - modified to act upon the filename itself, rather than the whole
681     // path; fixes bug #855349
682 
683     // split name into parts:
684     QFileInfo nfo(tmpName);
685     QString dirName = nfo.path();
686     QString baseName = nfo.fileName();
687 
688     // sed LilyPond-choking chars out of the filename proper
689     bool illegalFilename = (baseName.contains(' ') || baseName.contains("\\"));
690     baseName.replace(QRegularExpression(" "), "");
691     baseName.replace(QRegularExpression("\\\\"), "");
692     baseName.replace(QRegularExpression("'"), "");
693     baseName.replace(QRegularExpression("\""), "");
694 
695     // cat back together
696     tmpName = dirName + '/' + baseName;
697 
698     if (illegalFilename) {
699         int reply = QMessageBox::question(
700                 dynamic_cast<QWidget*>(qApp),
701                 baseName,
702                 tr("LilyPond does not allow spaces or backslashes in filenames.\n\n"
703                    "Would you like to use\n\n %1\n\n instead?").arg(tmpName),
704                 QMessageBox::Yes |QMessageBox::Cancel,
705                 QMessageBox::Cancel);
706         if (reply != QMessageBox::Yes)
707             return false;
708     }
709 
710     std::ofstream str(qstrtostr(tmpName).c_str(), std::ios::out);
711     if (!str) {
712         RG_WARNING << "LilyPondExporter::write() - can't write file " << tmpName;
713         m_warningMessage = tr("Export failed.  The file could not be opened for writing.");
714         return false;
715     }
716 
717     str << "% This LilyPond file was generated by Rosegarden " << protectIllegalChars(VERSION) << std::endl;
718 
719     str << m_language->getImportStatement();
720 
721     switch (m_languageLevel) {
722 
723     case LILYPOND_VERSION_2_6:
724         str << "\\version \"2.6.0\"" << std::endl;
725         break;
726 
727     case LILYPOND_VERSION_2_8:
728         str << "\\version \"2.8.0\"" << std::endl;
729         break;
730 
731     case LILYPOND_VERSION_2_10:
732         str << "\\version \"2.10.0\"" << std::endl;
733         break;
734 
735     case LILYPOND_VERSION_2_12:
736         str << "\\version \"2.12.0\"" << std::endl;
737         break;
738 
739     case LILYPOND_VERSION_2_14:
740         str << "\\version \"2.14.0\"" << std::endl;
741         break;
742 
743     default:
744         // force the default version if there was an error
745         RG_WARNING << "ERROR: Unknown language level " << m_languageLevel
746                   << ", using \\version \"2.14.0\" instead";
747         str << "\\version \"2.14.0\"" << std::endl;
748         m_languageLevel = LILYPOND_VERSION_2_14;
749     }
750 
751     // LilyPond \header block
752 
753     // set indention level to make future changes to horizontal layout less
754     // tedious, ++col to indent a new level, --col to de-indent
755     int col = 0;
756 
757     // grab user headers from metadata
758     Configuration metadata = m_composition->getMetadata();
759     std::vector<std::string> propertyNames = metadata.getPropertyNames();
760 
761     // open \header section if there's metadata to grab, and if the user
762     // wishes it
763     if (!propertyNames.empty()) {
764         str << "\\header {" << std::endl;
765         col++;  // indent+
766 
767         bool userTagline = false;
768 
769         for (size_t index = 0; index < propertyNames.size(); ++index) {
770             std::string property = propertyNames [index];
771             if (property == headerDedication() || property == headerTitle() ||
772                 property == headerSubtitle() || property == headerSubsubtitle() ||
773                 property == headerPoet() || property == headerComposer() ||
774                 property == headerMeter() || property == headerOpus() ||
775                 property == headerArranger() || property == headerInstrument() ||
776                 property == headerPiece() || property == headerCopyright() ||
777                 property == headerTagline()) {
778                 std::string header = protectIllegalChars(metadata.get<String>(property));
779                 if (property == headerCopyright()) {
780                     // replace a (c) or (C) with a real Copyright symbol
781                     size_t posCpy = header.find("(c)");
782                     if (posCpy == std::string::npos) posCpy = header.find("(C)");
783                     if (posCpy != std::string::npos) {
784                         std::string leftOfCpy = header.substr(0, posCpy);
785                         std::string rightOfCpy = header.substr(posCpy + 3);
786                         str << indent(col) << property << " =  \\markup { \"" << leftOfCpy << "\""
787                             << "\\char ##x00A9" << "\"" << rightOfCpy << "\" }" << std::endl;
788                     } else {
789                         if (header != "") {
790                             str << indent(col) << property << " = \""
791                                 << header << "\"" << std::endl;
792                         }
793                     }
794                 } else if (header != "") {
795                     str << indent(col) << property << " = \"" << header << "\"" << std::endl;
796                     // let users override defaults, but allow for providing
797                     // defaults if they don't:
798                     if (property == headerTagline())
799                         userTagline = true;
800                 }
801             }
802         }
803 
804         // default tagline
805         if (!userTagline) {
806             str << indent(col) << "tagline = \""
807                 << "Created using Rosegarden " << protectIllegalChars(VERSION) << " and LilyPond"
808                 << "\"" << std::endl;
809         }
810 
811         // close \header
812         str << indent(--col) << "}" << std::endl;
813     }
814 
815     // LilyPond \paper block (optional)
816     if (m_raggedBottom) {
817         str << indent(col) << "\\paper {" << std::endl;
818         str << indent(++col) << "ragged-bottom=##t" << std::endl;
819         str << indent(--col) << "}" << std::endl;
820     }
821 
822     // LilyPond music data!   Mapping:
823     // LilyPond Voice = Rosegarden Segment
824     // LilyPond Staff = Rosegarden Track
825     // (not the cleanest output but maybe the most reliable)
826 
827     // paper/font sizes
828     int font = m_fontSize + FONT_OFFSET;
829     str << indent(col) << "#(set-global-staff-size " << font << ")" << std::endl;
830 
831     // write user-specified paper type as default paper size
832     std::string paper = "";
833     switch (m_paperSize) {
834     case PAPER_A3 :
835         paper += "a3";
836         break;
837     case PAPER_A4 :
838         paper += "a4";
839         break;
840     case PAPER_A5 :
841         paper += "a5";
842         break;
843     case PAPER_A6 :
844         paper += "a6";
845         break;
846     case PAPER_LEGAL :
847         paper += "legal";
848         break;
849     case PAPER_LETTER :
850         paper += "letter";
851         break;
852     case PAPER_TABLOID :
853         paper += "tabloid";
854         break;
855     case PAPER_NONE :
856         paper = "";
857         break; // "do not specify"
858     }
859     if (paper != "") {
860         str << indent(col) << "#(set-default-paper-size \"" << paper << "\""
861             << (m_paperLandscape ? " 'landscape" : "") << ")"
862             << std::endl;
863     }
864 
865     // Define exceptions for ChordNames context: c:3
866     if (m_chordNamesMode) {
867         str << "chExceptionMusic = { <c e>-\\markup { \\super \"3\"} }" << std::endl;
868         str << "chExceptions = #(append (sequential-music-to-chord-exceptions chExceptionMusic #t) ignatzekExceptions)" << std::endl;
869     }
870 
871     // Find out the printed length of the composition
872     Composition::iterator i = m_composition->begin();
873     if ((*i) == nullptr) {
874         // The composition is empty!
875         str << indent(col) << "\\score {" << std::endl;
876         str << indent(++col) << "% no segments found" << std::endl;
877         // bind staffs with or without staff group bracket
878         str << indent(col) // indent
879             << "<<" << " s4 " << ">>" << std::endl;
880         str << indent(col) << "\\layout { }" << std::endl;
881         str << indent(--col) << "}" << std::endl;
882         m_warningMessage = tr("Export succeeded, but the composition was empty.");
883         return false;
884     }
885     timeT compositionStartTime = (*i)->getStartTime();
886     timeT compositionEndTime = (*i)->getEndMarkerTime();
887     for (; i != m_composition->end(); ++i) {
888 
889         // Allow some oportunities for user to cancel
890         if (m_progressDialog  &&  m_progressDialog->wasCanceled()) {
891             return false;
892         }
893 
894         if (compositionStartTime > (*i)->getStartTime()) {
895             compositionStartTime = (*i)->getStartTime();
896         }
897         if (compositionEndTime < (*i)->getEndMarkerTime()) {
898             compositionEndTime = (*i)->getEndMarkerTime();
899         }
900     }
901 
902     // Gather all segments in a place where it will be possible
903     // to see the repetitions in a global context and to compute
904     // the place of the different voices in the Lilypond score.
905     LilyPondSegmentsContext lsc(m_composition);
906     for (Composition::iterator i = m_composition->begin();
907             i != m_composition->end(); ++i) {
908         if (isSegmentToPrint(*i)) {
909             lsc.addSegment(*i);
910         }
911     }
912 
913     // Don't continue if lsc is empty
914     if (lsc.containsNoSegment()) {
915         switch (m_exportSelection) {
916 
917             case EXPORT_ALL_TRACKS :
918                 // We should have already exited this method if the composition is empty
919                 m_warningMessage = "No segments found while exporting all the"
920                                    " tracks : THIS IS A BUG.";
921                 break;
922 
923             case EXPORT_NONMUTED_TRACKS :
924                 m_warningMessage = tr("Export of unmuted tracks failed.  There"
925                                       " are no unmuted tracks or no segments on"
926                                       " them.");
927                 break;
928 
929             case EXPORT_SELECTED_TRACK :
930                 m_warningMessage = tr("Export of selected track failed.  There"
931                                       " are no segments on the selected track.");
932                 break;
933 
934             case EXPORT_SELECTED_SEGMENTS :
935                 m_warningMessage = tr("Export of selected segments failed.  No"
936                                       " segments are selected.");
937                 break;
938 
939             case EXPORT_EDITED_SEGMENTS :
940                 // Notation editor can't be open without any segment inside
941                 m_warningMessage = "No segments found while exporting the"
942                                    " edited segments : THIS IS A BUG.";
943                 break;
944 
945             default :
946                 m_warningMessage = "Abnormal m_exportSelection value :"
947                                    " THIS IS A BUG.";
948         }
949 
950         return false;
951     }
952 
953     // Look for repeating segments
954     lsc.precompute();
955 
956     // If needed, compute offsets of segments following a repeating one
957     // in LilyPond score
958     if (m_repeatMode == REPEAT_VOLTA) {
959         lsc.fixRepeatStartTimes();
960     }
961 
962     // If needed, compute offsets in LilyPond score of segments following
963     // a repeat with volta coming from linked segments.
964     //!!! TODO : Use an other switch than m_repeatMode
965     if (m_repeatMode == REPEAT_VOLTA) {
966         lsc.fixVoltaStartTimes();
967     }
968 
969     // If any segment is not starting at a bar boundary, adapted
970     // \partial or \skip keywords must be added to the output file.
971     // We have to know what segment is starting first to compute
972     // such \partial and \skip parameters.
973     // We can't rely on compositionStartTime to compute such data
974     // because the first segment of the composition may be being not printed.
975     // Following code is finding out the start time of the first segment
976     // being printed.
977     timeT firstSegmentStartTime = lsc.getFirstSegmentStartTime();
978 
979 
980     // define global context which is common for all staffs
981     str << indent(col++) << "global = { " << std::endl;
982     TimeSignature timeSignature = m_composition->
983         getTimeSignatureAt(m_composition->getStartMarker());
984 
985     int leftBar = 0;
986     int rightBar = leftBar;
987     if (m_repeatMode != REPEAT_VOLTA) {   ///!!! Quick hack to remove the last blank measure
988         /// The old way : look all bars successively to find time signature and
989         /// write it in a LilyPond comment except for the very first one.
990         /// The time is computed from the composition start time. This is wrong as
991         /// the composition start time may be outside the exported time range.
992         /// Nevertheless I have no time to fix it now. So sometimes it may work and
993         /// sometimes not work...
994         /// When using this code with repeats, the writeskip() to the end of the
995         /// composition is writing a blank measure at the end of the score.
996         do {
997             // Allow some opportunities for user to cancel
998             if (m_progressDialog  &&  m_progressDialog->wasCanceled()) {
999                 return false;
1000             }
1001 
1002             bool isNew = false;
1003             m_composition->getTimeSignatureInBar(rightBar + 1, isNew);
1004 
1005             if (isNew || (m_composition->getBarStart(rightBar + 1) >= compositionEndTime)) {
1006                 //  - set initial time signature; further time signature changes
1007                 //    are defined within the segments, because they may be hidden
1008                 str << indent(col) << (leftBar == 0 ? "" : "% ") << "\\time "
1009                     << timeSignature.getNumerator() << "/"
1010                     << timeSignature.getDenominator() << std::endl;
1011                 //  - place skips upto the end of the composition;
1012                 //    this justifies the printed staffs
1013                 str << indent(col);
1014                 timeT leftTime = m_composition->getBarStart(leftBar);
1015                 timeT rightTime = m_composition->getBarStart(rightBar + 1);
1016                 // Check for a partial measure in the beginning of the composition
1017                 if (leftTime < compositionStartTime) {
1018                     leftTime = compositionStartTime;
1019                 }
1020                 // Check for a partial measure in the end of the composition
1021                 if (rightTime > compositionEndTime) {
1022                     rightTime = compositionEndTime;
1023                 };
1024                 writeSkip(timeSignature, leftTime, rightTime - leftTime, false, str);
1025                 str << " %% " << (leftBar + 1) << "-" << (rightBar + 1) << std::endl;
1026 
1027                 timeSignature = m_composition->getTimeSignatureInBar(rightBar + 1, isNew);
1028                 leftBar = rightBar + 1;
1029             }
1030         } while (m_composition->getBarStart(++rightBar) < compositionEndTime);
1031     } else {    /// Quick hack to remove the last blank measure
1032         /// The preliminary new way: The timesignature are all ignored except
1033         /// the first one and the writeSkip() is computed to the end of the
1034         /// score taking into acount the repeats rather than to the end of
1035         /// the composition.
1036         timeSignature = m_composition->
1037             getTimeSignatureAt(lsc.getFirstSegmentStartTime());
1038         //  - set initial time signature; further time signature changes
1039         //    are defined within the segments, because they may be hidden
1040         str << indent(col) << "\\time "
1041             << timeSignature.getNumerator() << "/"
1042             << timeSignature.getDenominator() << std::endl;
1043         //  - place skips upto the end of the composition;
1044         //    this justifies the printed staffs
1045         str << indent(col);
1046         writeSkip(timeSignature, lsc.getFirstSegmentStartTime(),
1047                   lsc.getLastSegmentEndTime() - lsc.getFirstSegmentStartTime(),
1048                   false, str);
1049         str << std::endl;
1050     }   /// Quick hack to remove the last blank measure
1051 
1052     str << indent(--col) << "}" << std::endl;
1053 
1054     // time signatures changes are in segments, reset initial value
1055     timeSignature = m_composition->
1056         getTimeSignatureAt(m_composition->getStartMarker());
1057 
1058     // All the tempo changes are included in "globalTempo" context.
1059     // This context contains only skip notes between the tempo changes.
1060     // First tempo marking should still be include in \midi{ } block (prior to 2.10).
1061     // If tempo marks are printed in future, they should probably be
1062     // included in this context and the note duration in the tempo
1063     // mark should be according to the time signature. (hjj)
1064     int tempoCount = m_composition->getTempoChangeCount();
1065 
1066     if (tempoCount > 0) {
1067 
1068         timeT prevTempoChangeTime = m_composition->getStartMarker();
1069         int tempo = int(Composition::getTempoQpm(m_composition->getTempoAtTime(prevTempoChangeTime)));
1070         bool tempoMarksInvisible = false;
1071 
1072         str << indent(col++) << "globalTempo = {" << std::endl;
1073         if (m_exportTempoMarks == EXPORT_NONE_TEMPO_MARKS && tempoMarksInvisible == false) {
1074             str << indent(col) << "\\override Score.MetronomeMark #'transparent = ##t" << std::endl;
1075             tempoMarksInvisible = true;
1076         }
1077         str << indent(col) << "\\tempo 4 = " << tempo << "  ";
1078         int prevTempo = tempo;
1079 
1080         for (int i = 0; i < tempoCount; ++i) {
1081 
1082             // Allow some oportunities for user to cancel
1083             if (m_progressDialog  &&  m_progressDialog->wasCanceled()) {
1084                 return false;
1085             }
1086 
1087             std::pair<timeT, tempoT> tempoChange =
1088                 m_composition->getTempoChange(i);
1089 
1090             timeT tempoChangeTime = tempoChange.first;
1091 
1092             tempo = int(Composition::getTempoQpm(tempoChange.second));
1093 
1094             // First tempo change may be before the first segment.
1095             // Do not apply it before the first segment appears.
1096             if (tempoChangeTime < compositionStartTime) {
1097                 tempoChangeTime = compositionStartTime;
1098             } else if (tempoChangeTime >= compositionEndTime) {
1099                 tempoChangeTime = compositionEndTime;
1100             }
1101             if (prevTempoChangeTime < compositionStartTime) {
1102                 prevTempoChangeTime = compositionStartTime;
1103             } else if (prevTempoChangeTime >= compositionEndTime) {
1104                 prevTempoChangeTime = compositionEndTime;
1105             }
1106             writeSkip(m_composition->getTimeSignatureAt(tempoChangeTime),
1107                       tempoChangeTime, tempoChangeTime - prevTempoChangeTime, false, str);
1108             // add new \tempo only if tempo was changed
1109             if (tempo != prevTempo) {
1110                 if (m_exportTempoMarks == EXPORT_FIRST_TEMPO_MARK && tempoMarksInvisible == false) {
1111                     str << std::endl << indent(col) << "\\override Score.MetronomeMark #'transparent = ##t";
1112                     tempoMarksInvisible = true;
1113                 }
1114                 str << std::endl << indent(col) << "\\tempo 4 = " << tempo << "  ";
1115             }
1116 
1117             prevTempo = tempo;
1118             prevTempoChangeTime = tempoChangeTime;
1119             if (prevTempoChangeTime == compositionEndTime)
1120                 break;
1121         }
1122         // First tempo change may be before the first segment.
1123         // Do not apply it before the first segment appears.
1124         if (prevTempoChangeTime < compositionStartTime) {
1125             prevTempoChangeTime = compositionStartTime;
1126         }
1127         if (m_repeatMode != REPEAT_VOLTA) {   ///!!! Quick hack bis to remove the last blank measure
1128             /// The writeSkip() is just not called when exporting repeats
1129             writeSkip(m_composition->getTimeSignatureAt(prevTempoChangeTime),
1130                       prevTempoChangeTime, compositionEndTime - prevTempoChangeTime, false, str);
1131         }   /// Quick hack bis to remove the last blank measure
1132         str << std::endl;
1133         str << indent(--col) << "}" << std::endl;
1134     }
1135     // Markers
1136     // Skip until marker, make sure there's only one marker per measure
1137     if (m_exportMarkerMode != EXPORT_NO_MARKERS) {
1138         str << indent(col++) << "markers = {" << std::endl;
1139         timeT prevMarkerTime = 0;
1140 
1141         // Need the markers sorted by time
1142         Composition::markercontainer markers(m_composition->getMarkers()); // copy
1143         std::sort(markers.begin(), markers.end(), MarkerComp());
1144         Composition::markerconstiterator i_marker = markers.begin();
1145 
1146         while  (i_marker != markers.end()) {
1147             // Allow some oportunities for user to cancel
1148             if (m_progressDialog  &&  m_progressDialog->wasCanceled()) {
1149                 return false;
1150             }
1151 
1152             timeT markerTime = m_composition->getBarStartForTime((*i_marker)->getTime());
1153             RG_DEBUG << "Marker: " << (*i_marker)->getTime() << " previous: " << prevMarkerTime;
1154             // how to cope with time signature changes?
1155             if (markerTime > prevMarkerTime) {
1156                 str << indent(col);
1157                 writeSkip(m_composition->getTimeSignatureAt(markerTime),
1158                           markerTime, markerTime - prevMarkerTime, false, str);
1159                 str << "\\mark ";
1160                 switch (m_exportMarkerMode) {
1161                 case EXPORT_DEFAULT_MARKERS:
1162                     // Use the marker name for text
1163                     str << "\\default %% " << (*i_marker)->getName() << std::endl;
1164                     break;
1165                 case EXPORT_TEXT_MARKERS:
1166                     // Raise the text above the staff as not to clash with the other stuff
1167                     str << "\\markup { \\hspace #0 \\raise #1.5 \"" << (*i_marker)->getName() << "\" }" << std::endl;
1168                     break;
1169                 default:
1170                     break;
1171                 }
1172                 prevMarkerTime = markerTime;
1173             }
1174             ++i_marker;
1175         }
1176         str << indent(--col) << "}" << std::endl;
1177     }
1178 
1179 
1180     // open \score section
1181     str << "\\score {" << std::endl;
1182 
1183     int lastTrackIndex = -1;
1184     int voiceCounter = 0;
1185     bool firstTrack = true;
1186     int staffGroupCounter = 0;
1187     int pianoStaffCounter = 0;
1188     int bracket = 0;
1189     int prevBracket = -1;
1190     bool hasInstrumentNames = false;
1191 
1192     // Write out all segments for each Track, in track order.
1193     // This involves a hell of a lot of loops through all tracks
1194     // and segments, but the time spent doing that should still
1195     // be relatively small in the greater scheme.
1196 
1197     Track *track = nullptr;
1198     int trackPos = 0;
1199 
1200     for (track = lsc.useFirstTrack(); track; track = lsc.useNextTrack()) {
1201         trackPos = lsc.getTrackPos();
1202         // Allow some opportunities for user to cancel
1203         if (m_progressDialog  &&  m_progressDialog->wasCanceled()) {
1204             return false;
1205         }
1206 
1207         int voiceIndex;
1208         for (voiceIndex = lsc.useFirstVoice();
1209                           voiceIndex != -1; voiceIndex = lsc.useNextVoice()) {
1210 
1211             /* timeT repeatOffset = 0; */
1212 
1213             Segment *seg;
1214             for (seg = lsc.useFirstSegment(); seg; seg = lsc.useNextSegment()) {
1215                 RG_DEBUG << "lsc iterate segment" << seg;
1216                 if (!lsc.isVolta()) {
1217                     // handle the bracket(s) for the first track, and if no brackets
1218                     // present, open with a <<
1219                     prevBracket = bracket;
1220                     bracket = track->getStaffBracket();
1221 
1222                     //!!! how will all these indentions work out?  Probably not well,
1223                     // but maybe if users always provide sensible input, this will work
1224                     // out sensibly.  Maybe.  If not, we'll need some tracking gizmos to
1225                     // figure out the indention, or just skip the indention for these or
1226                     // something.  TBA.
1227                     if (firstTrack) {
1228                         // seems to be common to every case now
1229                         str << indent(++col) << "<< % common" << std::endl;
1230                     }
1231 
1232                     if (firstTrack && m_exportStaffGroup) {
1233 
1234                         if (bracket == Brackets::SquareOn) {
1235                             str << indent(++col) << "\\context StaffGroup = \"" << staffGroupCounter++
1236                                 << "\" << " << std::endl; //indent+
1237                         } else if (bracket == Brackets::CurlyOn) {
1238                             str << indent(++col) << "\\context GrandStaff = \"" << pianoStaffCounter++
1239                                 << "\" << " << std::endl; //indent+
1240                         } else if (bracket == Brackets::CurlySquareOn) {
1241                             str << indent(++col) << "\\context StaffGroup = \"" << staffGroupCounter++
1242                                 << "\" << " << std::endl; //indent+
1243                             str << indent(++col) << "\\context GrandStaff = \"" << pianoStaffCounter++
1244                                 << "\" << " << std::endl; //indent+
1245                         }
1246 
1247                         // Make chords offset colliding notes by default (only write for
1248                         // first track)
1249                         str << indent(++col) << "% Force offset of colliding notes in chords:"
1250                             << std::endl;
1251                         str << indent(col)   << "\\override Score.NoteColumn #\'force-hshift = #1.0"
1252                             << std::endl;
1253                         if (m_fingeringsInStaff) {
1254                             str << indent(col) << "% Allow fingerings inside the staff (configured from export options):"
1255                                 << std::endl;
1256                             str << indent(col)   << "\\override Score.Fingering #\'staff-padding = #\'()"
1257                                 << std::endl;
1258                         }
1259                     }
1260 
1261                     if (m_progressDialog)
1262                         m_progressDialog->setValue(
1263                                 trackPos * 100 / m_composition->getNbTracks());
1264 
1265                     qApp->processEvents();
1266 
1267                     if ((int) seg->getTrack() != lastTrackIndex) {
1268                         if (lastTrackIndex != -1) {
1269                             // close the old track (Staff context)
1270                             str << indent(--col) << ">> % Staff ends" << std::endl; //indent-
1271                         }
1272 
1273                         // handle any necessary bracket closures with a rude
1274                         // hack, because bracket closures need to be handled
1275                         // right under staff closures, but at this point in the
1276                         // loop we are one track too early for closing, so we use
1277                         // the bracket setting for the previous track for closing
1278                         // purposes (I'm not quite sure why this works, but it does)
1279                         if (m_exportStaffGroup) {
1280                             if (prevBracket == Brackets::SquareOff ||
1281                                 prevBracket == Brackets::SquareOnOff) {
1282                                 str << indent(--col) << ">> % StaffGroup " << staffGroupCounter
1283                                     << std::endl; //indent-
1284                             } else if (prevBracket == Brackets::CurlyOff) {
1285                                 str << indent(--col) << ">> % GrandStaff " << pianoStaffCounter
1286                                     << std::endl; //indent-
1287                             } else if (prevBracket == Brackets::CurlySquareOff) {
1288                                 str << indent(--col) << ">> % GrandStaff " << pianoStaffCounter
1289                                     << std::endl; //indent-
1290                                 str << indent(--col) << ">> % StaffGroup " << staffGroupCounter
1291                                     << std::endl; //indent-
1292                             }
1293                         }
1294                     }
1295 
1296                     //
1297                     // Write the chord text events into a lead sheet format.
1298                     // The chords are placed into ChordName context above the staff,
1299                     // which is between the previous ending staff and next starting
1300                     // staff.
1301                     //
1302                     if (m_chordNamesMode) {
1303                         int numberOfChords = -1;
1304 
1305                         timeT lastTime = compositionStartTime;
1306                         timeT segLength = seg->getEndTime() -
1307                             seg->getStartTime();
1308 
1309                         int nRepeats = lsc.getNumberOfRepeats();
1310                         RG_DEBUG << "chordNamesMode repeats:" << nRepeats;
1311                         if (nRepeats <= 0) nRepeats = 1;
1312                         // with REPEAT_VOLTA the segment is only rendered once
1313                         if (m_repeatMode == REPEAT_VOLTA) nRepeats = 1;
1314                         RG_DEBUG << "chordNamesMode repeats adj:" << nRepeats;
1315                         timeT myTime;
1316                         for (int iRepeat = 0; iRepeat < nRepeats; ++iRepeat) {
1317                             for (Segment::iterator j = seg->begin();
1318                                  seg->isBeforeEndMarker(j); ++j) {
1319 
1320                                 bool isNote = (*j)->isa(Note::EventType);
1321                                 bool isChord = false;
1322 
1323                                 if (!isNote) {
1324                                     if ((*j)->isa(Text::EventType)) {
1325                                         std::string textType;
1326                                         if ((*j)->get
1327                                             <String>(Text::TextTypePropertyName, textType) &&
1328                                             textType == Text::Chord) {
1329                                             isChord = true;
1330                                         }
1331                                     }
1332                                 }
1333 
1334                                 if (!isNote && !isChord) continue;
1335 
1336                                 myTime = (*j)->getNotationAbsoluteTime() +
1337                                     segLength * iRepeat;
1338                                 RG_DEBUG << "myTime1" << myTime;
1339                                 // adjust for repeats note the
1340                                 // lsc.getSegmentStartTime is relative to
1341                                 // lsc.getFirstSegmentStartTime
1342                                 timeT lscStart =
1343                                     lsc.getFirstSegmentStartTime() +
1344                                     lsc.getSegmentStartTime();
1345                                 timeT segStartDelta = seg->getStartTime() -
1346                                     lscStart;
1347                                 myTime -= segStartDelta;
1348                                 RG_DEBUG << "myTime2" << myTime;
1349 
1350                                 if (isChord) {
1351                                     std::string schord;
1352                                     (*j)->get<String>(Text::TextPropertyName, schord);
1353                                     QString chord(strtoqstr(schord));
1354                                     chord.replace(QRegularExpression("\\s+"), "");
1355                                     chord.replace(QRegularExpression("h"), "b");
1356 
1357                                     // DEBUG: str << " %{ '" << chord.toUtf8() << "' %} ";
1358                                     QRegularExpression rx("^([a-g]([ei]s)?)([:](m|dim|aug|maj|sus|\\d+|[.^]|[+-])*)?(/[+]?[a-g]([ei]s)?)?$");
1359                                     if (rx.match(chord).hasMatch()) {
1360                                         // The chord duration is zero, but the chord
1361                                         // intervals is given with skips (see below).
1362                                         QRegularExpression rxStart("^([a-g]([ei]s)?)");
1363                                         chord.replace(QRegularExpression(rxStart), QString("\\1") + QString("4*0"));
1364                                     } else {
1365                                         // Skip improper chords.
1366                                         str << (" %{ improper chord: '") << qStrToStrUtf8(chord) << ("' %} ");
1367                                         continue;
1368                                     }
1369 
1370                                     if (numberOfChords == -1) {
1371                                         str << indent(col++) << "\\new ChordNames " << "\\with {alignAboveContext=\"track " <<
1372                                             (trackPos + 1) << "\"}" << "\\chordmode {" << std::endl;
1373                                         str << indent(col) << "\\set chordNameExceptions = #chExceptions" << std::endl;
1374                                         str << indent(col);
1375                                         numberOfChords++;
1376                                     }
1377                                     if (numberOfChords >= 0) {
1378                                         // The chord intervals are specified with skips.
1379                                         RG_DEBUG << "writing chord" << myTime <<
1380                                             lastTime;
1381                                         writeSkip(m_composition->getTimeSignatureAt(myTime), lastTime, myTime - lastTime, false, str);
1382                                         str << qStrToStrUtf8(chord) << " ";
1383                                         numberOfChords++;
1384                                     }
1385                                     lastTime = myTime;
1386                                 }
1387                             }
1388                             // skip to end of segment (start of next segment)
1389                             myTime = lsc.getSegmentStartTime() +
1390                                 (iRepeat + 1.0) * segLength;
1391                                 RG_DEBUG << "myTime3" << myTime;
1392                             writeSkip(m_composition->getTimeSignatureAt(myTime), lastTime, myTime - lastTime, false, str);
1393                             lastTime = myTime;
1394                             str << std::endl << indent(col);
1395                         } // for iRepeat
1396                         if (numberOfChords >= 0) {
1397                             writeSkip(m_composition->getTimeSignatureAt(lastTime), lastTime, lsc.getLastSegmentEndTime() - lastTime, false, str);
1398                             if (numberOfChords == 1) str << "s8 ";
1399                             str << std::endl;
1400                             str << indent(--col) << "} % ChordNames " << std::endl;
1401                         }
1402                     } // if (m_exportChords....
1403 
1404                     if ((int) seg->getTrack() != lastTrackIndex) {
1405                         lastTrackIndex = seg->getTrack();
1406 
1407                         // handle any bracket start events (unless track staff
1408                         // brackets are being ignored, as when printing single parts
1409                         // out of a bigger score one by one)
1410                         if (!firstTrack && m_exportStaffGroup) {
1411                             if (bracket == Brackets::SquareOn ||
1412                                 bracket == Brackets::SquareOnOff) {
1413                                 str << indent(col++) << "\\context StaffGroup = \""
1414                                     << ++staffGroupCounter << "\" <<" << std::endl;
1415                             } else if (bracket == Brackets::CurlyOn) {
1416                                 str << indent(col++) << "\\context GrandStaff = \""
1417                                     << ++pianoStaffCounter << "\" <<" << std::endl;
1418                             } else if (bracket == Brackets::CurlySquareOn) {
1419                                 str << indent(col++) << "\\context StaffGroup = \""
1420                                     << ++staffGroupCounter << "\" <<" << std::endl;
1421                                 str << indent(col++) << "\\context GrandStaff = \""
1422                                     << ++pianoStaffCounter << "\" <<" << std::endl;
1423                             }
1424                         }
1425 
1426                         // avoid problem with <untitled> tracks yielding a
1427                         // .ly file that jumbles all notes together on a
1428                         // single staff...  every Staff context has to
1429                         // have a unique name, even if the
1430                         // Staff.instrument property is the same for
1431                         // multiple staffs...
1432                         // Added an option to merge staffs with the same, non-empty
1433                         // name. This option makes it possible to produce staffs
1434                         // with polyphonic, and polyrhytmic, music. Polyrhytmic
1435                         // music in a single staff is typical in piano, or
1436                         // guitar music. (hjj)
1437                         // In the case of colliding note heads, user may define
1438                         //  - DISPLACED_X -- for a note/chord
1439                         //  - INVISIBLE -- for a rest
1440                         const std::string staffName = protectIllegalChars(m_composition->
1441                                                         getTrackById(lastTrackIndex)->getLabel());
1442 
1443                         std::string shortStaffName = protectIllegalChars(m_composition->
1444                                 getTrackById(lastTrackIndex)->getShortLabel());
1445 
1446                         /*
1447                         * The context name is unique to a single track.
1448                         */
1449                         str << std::endl << indent(col)
1450                             << "\\context Staff = \"track "
1451                             << (trackPos + 1) << (staffName == "" ? "" : ", ")
1452                             << staffName << "\" ";
1453 
1454                         str << "<< " << std::endl;
1455                         ++col;
1456 
1457                         if (staffName.size()) {
1458                             hasInstrumentNames = true;
1459                             // The octavation is omitted in the instrument name.
1460                             // HJJ: Should it be automatically added to the clef: G^8 ?
1461                             // What if two segments have different transpose in a track?
1462                             std::ostringstream staffNameWithTranspose;
1463                             staffNameWithTranspose << "\\markup { \\center-column { \"" << staffName << " \"";
1464                             if ((seg->getTranspose() % 12) != 0) {
1465                                 staffNameWithTranspose << " \\line { ";
1466                                 int t = seg->getTranspose();
1467                                 t %= 12;
1468                                 if (t < 0) t+= 12;
1469                                 switch (t) {
1470                                 case 1 : staffNameWithTranspose << "\"in D\" \\smaller \\flat"; break;
1471                                 case 2 : staffNameWithTranspose << "\"in D\""; break;
1472                                 case 3 : staffNameWithTranspose << "\"in E\" \\smaller \\flat"; break;
1473                                 case 4 : staffNameWithTranspose << "\"in E\""; break;
1474                                 case 5 : staffNameWithTranspose << "\"in F\""; break;
1475                                 case 6 : staffNameWithTranspose << "\"in G\" \\smaller \\flat"; break;
1476                                 case 7 : staffNameWithTranspose << "\"in G\""; break;
1477                                 case 8 : staffNameWithTranspose << "\"in A\" \\smaller \\flat"; break;
1478                                 case 9 : staffNameWithTranspose << "\"in A\""; break;
1479                                 case 10 : staffNameWithTranspose << "\"in B\" \\smaller \\flat"; break;
1480                                 case 11 : staffNameWithTranspose << "\"in B\""; break;
1481                                 }
1482                                 staffNameWithTranspose << " }";
1483                             }
1484                             staffNameWithTranspose << " } }";
1485                             if (m_languageLevel < LILYPOND_VERSION_2_10) {
1486                                 str << indent(col) << "\\set Staff.instrument = " << staffNameWithTranspose.str()
1487                                     << std::endl;
1488                             } else {
1489                                 // always write long staff name
1490                                 str << indent(col) << "\\set Staff.instrumentName = "
1491                                     << staffNameWithTranspose.str() << std::endl;
1492 
1493                                 // write short staff name if user desires, and if
1494                                 // non-empty
1495                                 if (m_useShortNames && shortStaffName.size()) {
1496                                     str << indent(col) << "\\set Staff.shortInstrumentName = \""
1497                                         << shortStaffName << "\"" << std::endl;
1498                                 }
1499                             }
1500                         }
1501 
1502                         // Set midi instrument for the Staff when possible
1503                         Instrument *instr = m_studio->getInstrumentById(
1504                             m_composition->getTrackById(lastTrackIndex)
1505                                                             ->getInstrument());
1506                         if (instr) {
1507                             str << indent(col)
1508                                 << "\\set Staff.midiInstrument = \""
1509                                 << instr->getProgramName().c_str()
1510                                 << "\"" << std::endl;
1511                         }
1512 
1513                         // multi measure rests are used by default
1514                         str << indent(col) << "\\set Score.skipBars = ##t" << std::endl;
1515 
1516                         // turn off the stupid accidental cancelling business,
1517                         // because we don't do that ourselves, and because my 11
1518                         // year old son pointed out to me that it "Looks really
1519                         // stupid.  Why is it cancelling out four flats and then
1520                         // adding five flats back?  That's brain damaged."
1521                         //
1522                         // New option to turn it back on, per user request.  There
1523                         // doesn't seem to be any way to get LilyPond's behavior to
1524                         // quite mimic our own, so we just offer it to them as an
1525                         // either/or choice.
1526                         if (m_cancelAccidentals) {
1527                             str << indent(col) << "\\set Staff.printKeyCancellation = ##t" << std::endl;
1528                         } else {
1529                             str << indent(col) << "\\set Staff.printKeyCancellation = ##f" << std::endl;
1530                         }
1531                         str << indent(col) << "\\new Voice \\global" << std::endl;
1532                         if (tempoCount > 0) {
1533                             str << indent(col) << "\\new Voice \\globalTempo" << std::endl;
1534                         }
1535                         if (m_exportMarkerMode != EXPORT_NO_MARKERS) {
1536                             str << indent(col) << "\\new Voice \\markers" << std::endl;
1537                         }
1538 
1539                         if (m_exportBeams) {
1540                             str << indent(col) << "\\set Staff.autoBeaming = ##f % turns off all autobeaming" << std::endl;
1541                         }
1542                     }
1543                 } /// if (!lsc.isVolta())
1544 
1545                 // Temporary storage for non-atomic events (!BOOM)
1546                 // ex. LilyPond expects signals when a decrescendo starts
1547                 // as well as when it ends
1548                 eventendlist preEventsInProgress;
1549                 eventendlist postEventsInProgress;
1550 
1551                 // If the segment doesn't start at 0, add a "skip" to the start
1552                 // No worries about overlapping segments, because Voices can overlap
1553                 // voiceCounter is a hack because LilyPond does not by default make
1554                 // them unique
1555                 std::ostringstream voiceNumber;
1556 
1557                 voiceNumber << "voice " << ++voiceCounter;
1558                 if (!lsc.isVolta()) {
1559                     str << std::endl << indent(col++) << "\\context Voice = \"" << voiceNumber.str()
1560                         << "\" {"; // indent+
1561 
1562                     str << std::endl << indent(col) << "% Segment: " << seg->getLabel();
1563 
1564                     str << std::endl << indent(col) << "\\override Voice.TextScript #'padding = #2.0";
1565                     str << std::endl << indent(col) << "\\override MultiMeasureRest #'expand-limit = 1" << std::endl;
1566 
1567                     // staff notation size
1568                     int staffSize = track->getStaffSize();
1569                     if (staffSize == StaffTypes::Small) str << indent(col) << "\\small" << std::endl;
1570                     else if (staffSize == StaffTypes::Tiny) str << indent(col) << "\\tiny" << std::endl;
1571                 } /// if (!lsc.isVolta())
1572                 SegmentNotationHelper helper(*seg);
1573                 helper.setNotationProperties();
1574 
1575                 int firstBar = m_composition->getBarNumber(seg->getStartTime());
1576 
1577                 if (!lsc.isVolta()) {        // Don't write any skip in a volta
1578                     if (firstBar > 0) {
1579                         // Add a skip for the duration until the start of the first
1580                         // bar in the segment.  If the segment doesn't start on a
1581                         // bar line, an additional skip will be written at the start
1582                         // of writeBar, below.
1583                         //!!! This doesn't cope correctly yet with time signature changes
1584                         // during this skipped section.
1585                         // dmm - changed this to call writeSkip with false, to avoid
1586                         // writing actual rests, and write a skip instead, so
1587                         // visible rests do not appear before the start of short
1588                         // bars
1589                         str << std::endl << indent(col);
1590                         writeSkip(timeSignature, compositionStartTime,
1591                                 lsc.getSegmentStartTime(), false, str);
1592                     }
1593 
1594                     // If segment is not starting on a bar, but is starting at barTime + offset,
1595                     // we have to do :
1596                     //     if segment is the first one : add partial (barDuration - offset)
1597                     //     else  add skip (offset)
1598                     if (seg->getStartTime() - m_composition->getBarStart(firstBar) > 0) {
1599                         if (seg->getStartTime() == firstSegmentStartTime) {
1600                             timeT partialDuration = m_composition->getBarStart(firstBar + 1)
1601                                                     - seg->getStartTime();
1602                             str << indent(col) << "\\partial ";
1603                             // Arbitrary partial durations are handled by the following
1604                             // way: split the partial duration to 64th notes: instead
1605                             // of "4" write "64*16". (hjj)
1606                             Note partialNote = Note::getNearestNote(1, MAX_DOTS);
1607                             writeDuration(1, str);
1608                             str << "*" << ((int)(partialDuration / partialNote.getDuration()))
1609                                 << std::endl;
1610 
1611                         } else {
1612                             if (m_repeatMode == REPEAT_BASIC) {
1613                                 timeT partialOffset = seg->getStartTime()
1614                                                     - m_composition->getBarStart(firstBar);
1615                                 str << indent(col) << "\\skip ";
1616                                 // Arbitrary partial durations are handled by the following
1617                                 // way: split the partial duration to 64th notes: instead
1618                                 // of "4" write "64*16". (hjj)
1619                                 Note partialNote = Note::getNearestNote(1, MAX_DOTS);
1620                                 writeDuration(1, str);
1621                                 str << "*" << ((int)(partialOffset / partialNote.getDuration()))
1622                                     << std::endl;
1623                             }
1624                         }
1625                     }
1626                 } /// if (!lsc.isVolta())
1627 
1628 
1629                 std::string lilyText = "";      // text events
1630                 std::string prevStyle = "";     // track note styles
1631 
1632                 Rosegarden::Key key = lsc.getPreviousKey();
1633 
1634                 bool haveRepeating = false;
1635                 bool haveAlternates = false;
1636 
1637                 bool haveRepeatingWithVolta = false;
1638                 bool haveVolta = false;
1639 
1640                 bool nextBarIsAlt1 = false;
1641                 bool nextBarIsAlt2 = false;
1642                 bool prevBarWasAlt2 = false;
1643 
1644                 int MultiMeasureRestCount = 0;
1645 
1646                 bool nextBarIsDouble = false;
1647                 bool nextBarIsEnd = false;
1648                 bool nextBarIsDot = false;
1649 
1650                 for (int barNo = m_composition->getBarNumber(seg->getStartTime());
1651                     barNo <= m_composition->getBarNumber(seg->getEndMarkerTime());
1652                     ++barNo) {
1653                     qApp->processEvents();
1654 
1655                     timeT barStart = m_composition->getBarStart(barNo);
1656                     timeT barEnd = m_composition->getBarEnd(barNo);
1657                     timeT currentSegmentStartTime = seg->getStartTime();
1658                     timeT currentSegmentEndTime = seg->getEndMarkerTime();
1659                     // Check for a partial measure in the beginning of the composition
1660                     if (barStart < compositionStartTime) {
1661                         barStart = compositionStartTime;
1662                     }
1663                     // Check for a partial measure in the end of the composition
1664                     if (barEnd > compositionEndTime) {
1665                         barEnd = compositionEndTime;
1666                     }
1667                     // Check for a partial measure beginning in the middle of a
1668                     // theoretical bar
1669                     if (barStart < currentSegmentStartTime) {
1670                         barStart = currentSegmentStartTime;
1671                     }
1672                     // Check for a partial measure ending in the middle of a
1673                     // theoretical bar
1674                     if (barEnd > currentSegmentEndTime) {
1675                         barEnd = currentSegmentEndTime;
1676                     }
1677 
1678                     // Check for a time signature in the first bar of the segment
1679                     bool timeSigInFirstBar = false;
1680                     TimeSignature firstTimeSig =
1681                         m_composition->getTimeSignatureInBar(firstBar,
1682                                                              timeSigInFirstBar);
1683                     // and write it here (to avoid multiple time signatures when
1684                     // a repeating segment is unfolded)
1685                     if (timeSigInFirstBar && (barNo == firstBar)) {
1686                         writeTimeSignature(firstTimeSig, col, str);
1687                     }
1688 
1689                     // open \repeat section if this is the first bar in the
1690                     // repeat
1691                     if ( (lsc.isRepeatingSegment()
1692                            || (lsc.isSimpleRepeatedLinks()
1693                                   && (m_repeatMode == REPEAT_VOLTA)
1694                               )
1695                          ) && !haveRepeating) {
1696 
1697                         haveRepeating = true;
1698                         int numRepeats = 2;
1699 
1700                         if (m_repeatMode == REPEAT_BASIC) {
1701                             // The old unfinished way
1702                             str << std::endl << indent(col++)
1703                                 << "\\repeat volta " << numRepeats << " {";
1704                         } else {
1705                             numRepeats = lsc.getNumberOfRepeats();
1706                             if ((m_repeatMode == REPEAT_VOLTA) && lsc.isSynchronous()) {
1707                                 str << std::endl << indent(col++)
1708                                     << "\\repeat volta " << numRepeats << " {";
1709                             } else {
1710                                 // m_repeatMode == REPEAT_UNFOLD
1711                                 str << std::endl << indent(col++)
1712                                     << "\\repeat unfold "
1713                                     << numRepeats << " {";
1714                             }
1715                         }
1716                     } else if (lsc.isRepeatWithVolta() &&
1717                             !haveRepeatingWithVolta &&
1718                             !haveVolta) {
1719                         if (!lsc.isVolta()) {
1720                             str << std::endl << indent(col++);
1721                             if (lsc.isAutomaticVoltaUsable()) {
1722                                 str << "\\repeat volta "
1723                                     << lsc.getNumberOfRepeats() << " ";
1724                             }
1725                             // Opening of main repeating segment
1726                             str << "{   % Repeating stegment start here";
1727                             str << std::endl << indent(col)
1728                                 << "% Segment: " << seg->getLabel();
1729                             haveRepeatingWithVolta = true;
1730                             if (!lsc.isAutomaticVoltaUsable()) {
1731                                 if (lsc.wasRepeatingWithoutVolta()) {
1732                                     // When automatic volta is not usable, the
1733                                     // "start-repeat" bar hides the "end-repeat"
1734                                     // bar issued by the previous automatic
1735                                     // volta. In such a case, a "double-repeat"
1736                                     // bar has to be writed. As #'(double-repeat)
1737                                     // is currently not defined in
1738                                     // LilyPond, the ":..:" string is used.
1739                                     str << std::endl << indent(col)
1740                                         << "\\bar \":..:\"";
1741                                 } else {
1742                                     str << std::endl << indent(col)
1743                                         << "\\set Score.repeatCommands = #'(start-repeat)";
1744                                 }
1745                             }
1746                         } else {
1747                             str << std::endl << indent(col)
1748                                 << "{   % Alternative start here";
1749                             str << std::endl << indent(col++)
1750                                 << "    % Segment: " << seg->getLabel();
1751                             if (!lsc.isAutomaticVoltaUsable()) {
1752                                 str << std::endl << indent(col)
1753                                     << "\\set Score.repeatCommands = ";
1754                                 if (lsc.isFirstVolta()) {
1755                                     str << "#'((volta \""
1756                                         << lsc.getVoltaText() << "\"))";
1757                                 } else {
1758                                     str << "#'((volta #f) (volta \""
1759                                         << lsc.getVoltaText() << "\") end-repeat)";
1760                                 }
1761                             }
1762                             if (m_voltaBar) {
1763                                 str << std::endl << indent(col)
1764                                     << "\\bar \"|\" ";
1765                             }
1766                             haveVolta = true;
1767                         }
1768                     }
1769 
1770                     // open the \alternative section if this bar is alternative ending 1
1771                     // ending (because there was an "Alt1" flag in the
1772                     // previous bar to the left of where we are right now)
1773                     //
1774                     // Alt1 remains in effect until we run into Alt2, which
1775                     // runs to the end of the segment
1776                     if (nextBarIsAlt1 && haveRepeating) {
1777                         str << std::endl << indent(--col) << "} \% repeat close (before alternatives) ";
1778                         str << std::endl << indent(col++) << "\\alternative {";
1779                         str << std::endl << indent(col++) << "{  \% open alternative 1 ";
1780                         nextBarIsAlt1 = false;
1781                         haveAlternates = true;
1782                     } else if (nextBarIsAlt2 && haveRepeating) {
1783                         if (!prevBarWasAlt2) {
1784                             col--;
1785                             // add an extra str to the following to shut up
1786                             // compiler warning from --ing and ++ing it in the
1787                             // same statement
1788                             str << std::endl << indent(--col) << "} \% close alternative 1 ";
1789                             str << std::endl << indent(col++) << "{  \% open alternative 2";
1790                             col++;
1791                         }
1792                         prevBarWasAlt2 = true;
1793                     }
1794 
1795                     // should a time signature be writed in the current bar ?
1796                     bool noTimeSig;
1797                     if (timeSigInFirstBar) {
1798                         noTimeSig = barNo == firstBar;
1799                     } else {
1800                         noTimeSig = barNo != firstBar;
1801                     }
1802 
1803                     // write out a bar's worth of events
1804                     writeBar(seg, barNo, barStart, barEnd, col, key,
1805                             lilyText,
1806                             prevStyle, preEventsInProgress, postEventsInProgress, str,
1807                             MultiMeasureRestCount,
1808                             nextBarIsAlt1, nextBarIsAlt2, nextBarIsDouble,
1809                             nextBarIsEnd, nextBarIsDot,
1810                             noTimeSig);
1811 
1812                 }
1813 
1814                 // close \repeat
1815                 if (haveRepeating) {
1816 
1817                     // close \alternative section if present
1818                     if (haveAlternates) {
1819                         str << std::endl << indent(--col) << "} \% close alternative 2 ";
1820                     }
1821 
1822                     // close \repeat section in either case
1823                     str << std::endl << indent(--col) << "} \% close "
1824                         << (haveAlternates ? "alternatives" : "repeat");
1825                 }
1826 
1827                 // Open alternate parts if repeat with volta from linked segments
1828                 if (haveRepeatingWithVolta) {
1829                     if (!lsc.isVolta()) {
1830                         str << std::endl << indent(--col) << "} \% close main repeat";
1831                         if (lsc.isAutomaticVoltaUsable()) {
1832                             str << std::endl << indent (col++) << "\\alternative  {";
1833                         }
1834                         str <<  std::endl;
1835                     } else {
1836                         // Close alternative segment
1837                         str << std::endl << indent(--col) << "}";
1838                     }
1839                 }
1840 
1841                 // closing bar
1842                 if ((seg->getEndMarkerTime() == compositionEndTime) && !haveRepeating) {
1843                     str << std::endl << indent(col) << "\\bar \"|.\"";
1844                 }
1845 
1846                 if (!haveRepeatingWithVolta && !haveVolta) {
1847                     // close Voice context
1848                     str << std::endl << indent(--col) << "} % Voice" << std::endl;  // indent-
1849                 }
1850 
1851                 if (lsc.isVolta()) {
1852                     // close volta
1853                     if (!lsc.isAutomaticVoltaUsable() && lsc.isLastVolta()) {
1854                         str << std::endl << indent (col)
1855                             << "\\set Score.repeatCommands = ";
1856                         if (lsc.getVoltaRepeatCount() > 1) {
1857                             str << "#'((volta #f) end-repeat)";
1858                         } else {
1859                             str << "#'((volta #f))";
1860                         }
1861                         if (lsc.getVoltaRepeatCount() < 1) {
1862                             RG_WARNING << "BUG in LilyPondExporter : "
1863                                     << "lsc.getVoltaRepeatCount() = "
1864                                     << lsc.getVoltaRepeatCount();
1865                         }
1866                     }
1867                     str << std::endl << indent(--col) << "}" << std::endl;  // indent-
1868 
1869                     if (lsc.isLastVolta()) {
1870                         if (lsc.isAutomaticVoltaUsable()) {
1871                             // close alternative section
1872                             str << std::endl << indent(--col) << "}" << std::endl;  // indent-
1873                         }
1874 
1875                     // close Voice context
1876                         str << std::endl << indent(--col) << "} % Voice" << std::endl;  // indent-
1877                     }
1878                 }
1879 
1880                 //
1881                 // Write accumulated lyric events to the Lyric context, if desired.
1882                 //
1883                 // Sync the code below with LyricEditDialog::unparse() !!
1884                 //
1885                 if (m_exportLyrics != EXPORT_NO_LYRICS) {
1886                     // To force correct ordering of verses must track when first verse is printed.
1887                     bool isFirstPrintedVerse = true;
1888                     for (long currentVerse = 0, lastVerse = 0;
1889                         currentVerse <= lastVerse;
1890                         currentVerse++) {
1891                         bool haveLyric = false;
1892                         bool firstNote = true;
1893                         QString text = "";
1894 
1895                         timeT lastTime = seg->getStartTime();
1896                         for (Segment::iterator j = seg->begin();
1897                             seg->isBeforeEndMarker(j); ++j) {
1898 
1899                             bool isNote = (*j)->isa(Note::EventType);
1900                             bool isLyric = false;
1901 
1902                             if (!isNote) {
1903                                 if ((*j)->isa(Text::EventType)) {
1904                                     std::string textType;
1905                                     if ((*j)->get
1906                                         <String>(Text::TextTypePropertyName, textType) &&
1907                                         textType == Text::Lyric) {
1908                                         isLyric = true;
1909                                     }
1910                                 }
1911                             }
1912 
1913                             if (!isNote && !isLyric) continue;
1914 
1915                             timeT myTime = (*j)->getNotationAbsoluteTime();
1916 
1917                             if (isNote) {
1918                                 if ((myTime > lastTime) || firstNote) {
1919                                     if (!haveLyric)
1920                                         text += " _";
1921                                     lastTime = myTime;
1922                                     haveLyric = false;
1923                                     firstNote = false;
1924                                 }
1925                             }
1926 
1927                             if (isLyric) {
1928                                 // Very old .rg files may not have the verse property.
1929                                 // In such a case there is only one verse which
1930                                 // is numbered 0.
1931                                 long verse;
1932                                 if (! (*j)->get<Int>(Text::LyricVersePropertyName,
1933                                                      verse)) verse = 0;
1934 
1935                                 if (verse == currentVerse) {
1936                                     std::string ssyllable;
1937                                     (*j)->get<String>(Text::TextPropertyName, ssyllable);
1938                                     text += " ";
1939 
1940                                     QString syllable(strtoqstr(ssyllable));
1941                                     syllable.replace(QRegularExpression("^\\s+"), "");
1942                                     syllable.replace(QRegularExpression("\\s+$"), "");
1943                                     syllable.replace(QRegularExpression("\""), "\\\"");
1944                                     text += "\"" + syllable + "\"";
1945                                     haveLyric = true;
1946                                 } else if (verse > lastVerse) {
1947                                     lastVerse = verse;
1948                                 }
1949                             }
1950                         }
1951 
1952                         text.replace(QRegularExpression(" _+([^ ])") , " \\1");
1953                         text.replace("\"_\"" , " ");
1954 
1955                         // Do not create empty context for lyrics.
1956                         // Does this save some vertical space, as was written
1957                         // in earlier comment?
1958                         QRegularExpression rx("\"");
1959                         if (rx.match(text).hasMatch()) {
1960 
1961                             if (m_languageLevel <= LILYPOND_VERSION_2_10) {
1962                                 str << indent(col) << "\\lyricsto \"" << voiceNumber.str() << "\""
1963                                     << " \\new Lyrics \\lyricmode {" << std::endl;
1964                             } else {
1965                                 str << indent(col)
1966                                     << "\\new Lyrics ";
1967                                 // Put special alignment info for first printed verse only.
1968                                 // Otherwise, verses print in reverse order.
1969                                 if (isFirstPrintedVerse) {
1970                                     str << "\\with {alignBelowContext=\"track " << (trackPos + 1) << "\"} ";
1971                                     isFirstPrintedVerse = false;
1972                                 }
1973                                 str << "\\lyricsto \"" << voiceNumber.str() << "\"" << " \\lyricmode {" << std::endl;
1974                             }
1975                             if (m_exportLyrics == EXPORT_LYRICS_RIGHT) {
1976                                 str << indent(++col) << "\\override LyricText #'self-alignment-X = #RIGHT"
1977                                     << std::endl;
1978                             } else if (m_exportLyrics == EXPORT_LYRICS_CENTER) {
1979                                 str << indent(++col) << "\\override LyricText #'self-alignment-X = #CENTER"
1980                                     << std::endl;
1981                             } else {
1982                                 str << indent(++col) << "\\override LyricText #'self-alignment-X = #LEFT"
1983                                     << std::endl;
1984                             }
1985                             str << indent(col) << qStrToStrUtf8("\\set ignoreMelismata = ##t") << std::endl;
1986                             str << indent(col) << qStrToStrUtf8(text) << " " << std::endl;
1987                             str << indent(col) << qStrToStrUtf8("\\unset ignoreMelismata") << std::endl;
1988                             str << indent(--col) << qStrToStrUtf8("} % Lyrics ") << (currentVerse+1) << std::endl;
1989                             // close the Lyrics context
1990                         } // if (rx.search(text....
1991                     } // for (long currentVerse = 0....
1992                 } // if (m_exportLyrics....
1993                 firstTrack = false;
1994             } // for (seg = lsc.useFirstSegment(); seg; seg = ....
1995         } // for (voiceIndex = lsc.useFirstVoice(); voiceIndex != -1; ....
1996     } // for (track = lsc.useFirstTrack(); track; track = ....
1997 
1998     // close the last track (Staff context)
1999     if (voiceCounter > 0) {
2000         str << indent(--col) << ">> % Staff (final) ends" << std::endl;  // indent-
2001 
2002         // handle any necessary final bracket closures (if brackets are being
2003         // exported)
2004         if (m_exportStaffGroup) {
2005             if (bracket == Brackets::SquareOff ||
2006                 bracket == Brackets::SquareOnOff) {
2007                 str << indent(--col) << ">> % StaffGroup " << staffGroupCounter
2008                     << std::endl; //indent-
2009             } else        if (bracket == Brackets::CurlyOff) {
2010                 str << indent(--col) << ">> % GrandStaff (final) " << pianoStaffCounter
2011                     << std::endl; //indent-
2012             } else if (bracket == Brackets::CurlySquareOff) {
2013                 str << indent(--col) << ">> % GrandStaff (final) " << pianoStaffCounter
2014                     << std::endl; //indent-
2015                 str << indent(--col) << ">> % StaffGroup (final) " << staffGroupCounter
2016                     << std::endl; //indent-
2017             }
2018         }
2019     } else {
2020         str << indent(--col) << "% (All staffs were muted.)" << std::endl;
2021     }
2022 
2023     // close \notes section
2024     str << std::endl << indent(--col) << ">> % notes" << std::endl << std::endl; // indent-
2025     //    str << std::endl << indent(col) << ">> % global wrapper" << std::endl;
2026 
2027     // write \layout block
2028     str << indent(col++) << "\\layout {" << std::endl;
2029 
2030     // indent instrument names
2031     if (hasInstrumentNames) {
2032         str << indent(col) << "indent = 3.0\\cm" << std::endl
2033             << indent(col) << "short-indent = 1.5\\cm" << std::endl;
2034     }
2035 
2036     if (!m_exportEmptyStaves) {
2037         str << indent(col) << "\\context { \\Staff \\RemoveEmptyStaves }" << std::endl;
2038     }
2039     if (m_chordNamesMode) {
2040         str << indent(col) << "\\context { \\GrandStaff \\accepts \"ChordNames\" }" << std::endl;
2041     }
2042     if (m_exportLyrics != EXPORT_NO_LYRICS) {
2043         str << indent(col) << "\\context { \\GrandStaff \\accepts \"Lyrics\" }" << std::endl;
2044     }
2045     str << indent(--col) << "}" << std::endl;
2046 
2047     // write initial tempo in Midi block, always, but commented out
2048     // makes debugging the .ly file easier because fewer "noisy" errors are
2049     // produced during the process of rendering MIDI...)
2050     int tempo = int(Composition::getTempoQpm(m_composition->getTempoAtTime(m_composition->getStartMarker())));
2051     // Incomplete?  Can I get away without converting tempo relative to the time
2052     // signature for this purpose?  we'll see...
2053     str << "% " << indent(col++) << "uncomment to enable generating midi file from the lilypond source" << std::endl;
2054     str << "% " << indent(col++) << "\\midi {" << std::endl;
2055     if (m_languageLevel < LILYPOND_VERSION_2_10) {
2056         str << "% " << indent(col) << "\\tempo 4 = " << tempo << std::endl;
2057     }
2058     str << "% " << indent(--col) << "} " << std::endl;
2059 
2060     // close \score section and close out the file
2061     str << "} % score" << std::endl;
2062     str.close();
2063     return true;
2064 }
2065 
2066 timeT
calculateDuration(Segment * s,const Segment::iterator & i,timeT barEnd,timeT & soundingDuration,const std::pair<int,int> & tupletRatio,bool & overlong)2067 LilyPondExporter::calculateDuration(Segment *s,
2068                                     const Segment::iterator &i,
2069                                     timeT barEnd,
2070                                     timeT &soundingDuration,
2071                                     const std::pair<int, int> &tupletRatio,
2072                                     bool &overlong)
2073 {
2074     timeT duration = (*i)->getNotationDuration();
2075     timeT absTime = (*i)->getNotationAbsoluteTime();
2076 
2077     //RG_DEBUG << "calculateDuration: first duration, absTime:" << duration << "," << absTime;
2078 
2079     timeT durationCorrection = 0;
2080 
2081     if ((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType)) {
2082         try {
2083             // tuplet compensation, etc
2084             Note::Type type = (*i)->get<Int>(NOTE_TYPE);
2085             int dots = (*i)->get<Int>(NOTE_DOTS);
2086             durationCorrection = Note(type, dots).getDuration() - duration;
2087         } catch (const Exception &e) { // no properties
2088         }
2089     }
2090 
2091     duration += durationCorrection;
2092 
2093     //RG_DEBUG << "calculateDuration: now duration is" << duration << "after correction of" << durationCorrection;
2094 
2095     soundingDuration = duration * tupletRatio.first/ tupletRatio.second;
2096 
2097     timeT toNext = barEnd - absTime;
2098     if (soundingDuration > toNext) {
2099         soundingDuration = toNext;
2100         duration = soundingDuration * tupletRatio.second/ tupletRatio.first;
2101         overlong = true;
2102     }
2103 
2104     //RG_DEBUG << "calculateDuration: time to barEnd is " << toNext;
2105 
2106     // Examine the following event, and truncate our duration
2107     // if we overlap it.
2108     Segment::iterator nextElt = s->end();
2109     toNext = soundingDuration;
2110 
2111     if ((*i)->isa(Note::EventType)) {
2112 
2113         Chord chord(*s, i, m_composition->getNotationQuantizer());
2114         Segment::iterator nextElt = chord.getFinalElement();
2115         ++nextElt;
2116 
2117         if (s->isBeforeEndMarker(nextElt)) {
2118             // The quantizer sometimes sticks a rest at the same time
2119             // as this note -- don't use that one here, and mark it as
2120             // not to be exported -- it's just a heavy-handed way of
2121             // rendering counterpoint in RG
2122             if ((*nextElt)->isa(Note::EventRestType) &&
2123                 (*nextElt)->getNotationAbsoluteTime() == absTime) {
2124                 (*nextElt)->set<Bool>(SKIP_PROPERTY, true);
2125                 ++nextElt;
2126             }
2127         }
2128 
2129     } else {
2130         nextElt = i;
2131         ++nextElt;
2132         while (s->isBeforeEndMarker(nextElt)) {
2133             if ((*nextElt)->isa(Controller::EventType) ||
2134                 (*nextElt)->isa(ProgramChange::EventType) ||
2135                 (*nextElt)->isa(SystemExclusive::EventType) ||
2136                 (*nextElt)->isa(ChannelPressure::EventType) ||
2137                 (*nextElt)->isa(KeyPressure::EventType) ||
2138                 (*nextElt)->isa(PitchBend::EventType)) {
2139                 ++nextElt;
2140             } else {
2141                 break;
2142             }
2143         }
2144     }
2145 
2146     if (s->isBeforeEndMarker(nextElt)) {
2147         //RG_DEBUG << "calculateDuration: inside conditional";
2148         toNext = (*nextElt)->getNotationAbsoluteTime() - absTime;
2149         // if the note was lengthened, assume it was lengthened to the left
2150         // when truncating to the beginning of the next note
2151         if (durationCorrection > 0) {
2152             toNext += durationCorrection;
2153         }
2154         if (soundingDuration > toNext) {
2155             soundingDuration = toNext;
2156             duration = soundingDuration * tupletRatio.second/ tupletRatio.first;
2157         }
2158     }
2159 
2160     //RG_DEBUG << "calculateDuration: second toNext is" << toNext;
2161     //RG_DEBUG << "calculateDuration: final duration, soundingDuration:" << duration << "," << soundingDuration;
2162 
2163     return duration;
2164 }
2165 
lilyClefType(const std::string & clefType)2166 static std::string lilyClefType(const std::string &clefType)
2167 {
2168     if (clefType == Clef::Treble) {
2169         return "treble";
2170     } else if (clefType == Clef::French) {
2171         return "french";
2172     } else if (clefType == Clef::Soprano) {
2173         return "soprano";
2174     } else if (clefType == Clef::Mezzosoprano) {
2175         return "mezzosoprano";
2176     } else if (clefType == Clef::Alto) {
2177         return "alto";
2178     } else if (clefType == Clef::Tenor) {
2179         return "tenor";
2180     } else if (clefType == Clef::Baritone) {
2181         return "baritone";
2182     } else if (clefType == Clef::Varbaritone) {
2183         return "varbaritone";
2184     } else if (clefType == Clef::Bass) {
2185         return "bass";
2186     } else if (clefType == Clef::Subbass) {
2187         return "subbass";
2188     }
2189     return std::string();
2190 }
2191 
handleGuitarChord(Segment::iterator i,std::ofstream & str)2192 void LilyPondExporter::handleGuitarChord(Segment::iterator i, std::ofstream &str)
2193 {
2194     try {
2195         Guitar::Chord chord = Guitar::Chord(**i);
2196         const Guitar::Fingering& fingering = chord.getFingering();
2197 
2198         int barreStart = 0, barreEnd = 0, barreFret = 0;
2199 
2200         //
2201         // Check if there is a barre.
2202         //
2203         if (fingering.hasBarre()) {
2204             Guitar::Fingering::Barre barre = fingering.getBarre();
2205             barreStart = barre.start;
2206             barreEnd = barre.end;
2207             barreFret = barre.fret;
2208         }
2209 
2210         if (barreStart == 0) {
2211             str << " s4*0^\\markup \\fret-diagram #\"";
2212         } else {
2213             str << " s4*0^\\markup \\override #'(barre-type . straight) \\fret-diagram #\"";
2214         }
2215         //
2216         // Check each string individually.
2217         // Note: LilyPond numbers strings differently.
2218         //
2219         for (int stringNum = 6; stringNum >= 1; --stringNum) {
2220             if (barreStart == stringNum) {
2221                 str << "c:" << barreStart << "-" << barreEnd << "-" << barreFret << ";";
2222             }
2223 
2224             if (fingering.getStringStatus(6-stringNum) == Guitar::Fingering::MUTED) {
2225                 str << stringNum << "-x;";
2226             } else if (fingering.getStringStatus(6-stringNum) == Guitar::Fingering::OPEN) {
2227                 str << stringNum << "-o;";
2228             } else {
2229                 int stringStatus = fingering.getStringStatus(6-stringNum);
2230                 if ((stringNum <= barreStart) && (stringNum >= barreEnd)) {
2231                     str << stringNum << "-" << barreFret << ";";
2232                 } else {
2233                     str << stringNum << "-" << stringStatus << ";";
2234                 }
2235             }
2236         }
2237         str << "\" ";
2238 
2239     } catch (const Exception &e) { // GuitarChord ctor failed
2240         RG_DEBUG << "Bad GuitarChord event in LilyPond export";
2241     }
2242 }
2243 
2244 void
writeBar(Segment * s,int barNo,timeT barStart,timeT barEnd,int col,Rosegarden::Key & key,std::string & lilyText,std::string & prevStyle,eventendlist & preEventsInProgress,eventendlist & postEventsInProgress,std::ofstream & str,int & MultiMeasureRestCount,bool & nextBarIsAlt1,bool & nextBarIsAlt2,bool & nextBarIsDouble,bool & nextBarIsEnd,bool & nextBarIsDot,bool noTimeSignature)2245 LilyPondExporter::writeBar(Segment *s,
2246                            int barNo, timeT barStart, timeT barEnd, int col,
2247                            Rosegarden::Key &key,
2248                            std::string &lilyText,
2249                            std::string &prevStyle,
2250                            eventendlist &preEventsInProgress,
2251                            eventendlist &postEventsInProgress,
2252                            std::ofstream &str,
2253                            int &MultiMeasureRestCount,
2254                            bool &nextBarIsAlt1, bool &nextBarIsAlt2,
2255                            bool &nextBarIsDouble, bool &nextBarIsEnd,
2256                            bool &nextBarIsDot,  bool noTimeSignature)
2257 {
2258     int lastStem = 0; // 0 => unset, -1 => down, 1 => up
2259     int isGrace = 0;
2260 
2261     Segment::iterator i =
2262         SegmentNotationHelper(*s).findNotationAbsoluteTime(barStart);
2263     if (!s->isBeforeEndMarker(i))
2264         return ;
2265 
2266     //RG_DEBUG << "===== Writing bar" << barNo;
2267 
2268     if (MultiMeasureRestCount == 0) {
2269         str << std::endl;
2270 
2271         if ((barNo + 1) % 5 == 0) {
2272             str << "%% " << barNo + 1 << std::endl << indent(col);
2273         } else {
2274             str << indent(col);
2275         }
2276     }
2277 
2278     bool isNew = false;
2279     TimeSignature timeSignature = m_composition->getTimeSignatureInBar(barNo, isNew);
2280     if (isNew && !noTimeSignature) {
2281         writeTimeSignature(timeSignature, col, str);
2282     }
2283 
2284     timeT absTime = (*i)->getNotationAbsoluteTime();
2285     timeT writtenDuration = 0;
2286     std::pair<int,int> barDurationRatio(timeSignature.getNumerator(),timeSignature.getDenominator());
2287     std::pair<int,int> durationRatioSum(0,1);
2288     static std::pair<int,int> durationRatio(0,1);
2289 
2290     if (absTime > barStart) {
2291         Note note(Note::getNearestNote(absTime - barStart, MAX_DOTS));
2292         writtenDuration += note.getDuration();
2293         durationRatio = writeSkip(timeSignature, 0, note.getDuration(), false, str);
2294         durationRatioSum = fractionSum(durationRatioSum,durationRatio);
2295         // str << qstrtostr(QString(" %{ %1/%2 %} ").arg(durationRatio.first).arg(durationRatio.second)); // DEBUG
2296     }
2297 
2298     timeT prevDuration = -1;
2299     eventstartlist preEventsToStart;
2300     eventstartlist postEventsToStart;
2301 
2302     long groupId = -1;
2303     std::string groupType = "";
2304     std::pair<int, int> tupletRatio(1, 1);
2305 
2306     bool overlong = false;
2307 
2308     bool inBeamedGroup = false;
2309     bool startingBeamedGroup = false;
2310     Event *nextBeamedNoteInGroup = nullptr;
2311     Event *nextNoteInTuplet = nullptr;
2312 
2313     while (s->isBeforeEndMarker(i)) {
2314 
2315         Event *event = *i;
2316 
2317         if (event->getNotationAbsoluteTime() >= barEnd)
2318             break;
2319 
2320         // First test whether we're entering or leaving a group,
2321         // before we consider how to write the event itself (at least
2322         // for tuplets)
2323         QString startTupledStr;
2324 
2325         const bool isNote = event->isa(Note::EventType);
2326         const bool isRest = event->isa(Note::EventRestType);
2327 
2328         if (isNote || isRest ||
2329             event->isa(Clef::EventType) || event->isa(Key::EventType) ||
2330             event->isa(Symbol::EventType)) {
2331 
2332             // skip everything until the next beamed note in the current group
2333             if (!nextBeamedNoteInGroup || event == nextBeamedNoteInGroup) {
2334                 nextBeamedNoteInGroup = nullptr;
2335 
2336                 groupType = "";
2337                 event->get<String>(BEAMED_GROUP_TYPE, groupType); // might fail
2338                 const bool tuplet = groupType == GROUP_TYPE_TUPLED;
2339 
2340                 long newGroupId = -1;
2341                 if (!groupType.empty() && (isNote || isRest)) {
2342                     event->get<Int>(BEAMED_GROUP_ID, newGroupId);
2343 
2344                     if (newGroupId != -1) {
2345                         if (tuplet) {
2346                             nextNoteInTuplet = nextNoteInGroup(s, i, groupType, barEnd);
2347                         }
2348                         nextBeamedNoteInGroup = nextNoteInGroup(s, i, GROUP_TYPE_BEAMED, barEnd);
2349                     }
2350                 }
2351 
2352                 if (newGroupId != -1 && newGroupId != groupId) {
2353                     // entering a new beamed group
2354                     groupId = newGroupId;
2355 
2356                     startingBeamedGroup = true;
2357 
2358                     //RG_DEBUG << "Entering group" << groupId << "type" << groupType;
2359                     if (tuplet) {
2360                         long numerator = 0;
2361                         long denominator = 0;
2362                         event->get<Int>(BEAMED_GROUP_TUPLED_COUNT, numerator);
2363                         event->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT, denominator);
2364                         if (numerator == 0 || denominator == 0) {
2365                             RG_WARNING << "WARNING: LilyPondExporter::writeBar: "
2366                                       << "tupled event without tupled/untupled counts";
2367                             groupId = -1;
2368                             groupType = "";
2369                         } else {
2370                             startTupledStr += QString("\\times %1/%2 { ").arg(numerator).arg(denominator);
2371                             tupletRatio = std::pair<int, int>(numerator, denominator);
2372                         }
2373                     } else if (groupType == GROUP_TYPE_BEAMED) {
2374                         // there can currently be only on group type, reset tuplet ratio
2375                         tupletRatio = std::pair<int, int>(1,1);
2376                     }
2377                 }
2378             }
2379         } else if (event->isa(Controller::EventType) &&
2380                    event->has(Controller::NUMBER) &&
2381                    event->has(Controller::VALUE)) {
2382             if (event->get <Int>(Controller::NUMBER) == 64) {
2383                 postEventsToStart.insert(event);
2384                 postEventsInProgress.insert(event);
2385             }
2386         }
2387 
2388         // Test whether the next note is grace note or not.
2389         // The start or end of beamed grouping should be put in proper places.
2390         if (event->has(IS_GRACE_NOTE) && event->get<Bool>(IS_GRACE_NOTE)) {
2391             if (isGrace == 0) {
2392                 isGrace = 1;
2393 
2394                 // LilyPond export hack:  If a grace note has one or more
2395                 // tremolo slashes, we export it as a slashed grace note instead
2396                 // of a plain one.
2397                 long slashes;
2398                 slashes = event->get<Int>(NotationProperties::SLASHES, slashes);
2399                 if (slashes > 0) str << "\\slashedGrace { ";
2400                 else str << "\\grace { ";
2401 
2402                 // str << "%{ grace starts %} "; // DEBUG
2403             }
2404         } else if (isGrace == 1) {
2405             isGrace = 0;
2406             // str << "%{ grace ends %} "; // DEBUG
2407             str << "} ";
2408         }
2409         str << qStrToStrUtf8(startTupledStr);
2410 
2411         timeT soundingDuration = -1;
2412         timeT duration = calculateDuration
2413             (s, i, barEnd, soundingDuration, tupletRatio, overlong);
2414 
2415         if (soundingDuration == -1) {
2416             soundingDuration = duration * tupletRatio.first / tupletRatio.second;
2417         }
2418 
2419         if (event->has(SKIP_PROPERTY)) {
2420             event->unset(SKIP_PROPERTY);
2421             ++i;
2422             continue;
2423         }
2424 
2425         bool needsSlashRest = false;
2426 
2427         // symbols have no duration, so handle these ahead of anything else
2428         if (event->isa(Symbol::EventType)) {
2429 
2430             Symbol symbol(*event);
2431 
2432             if (symbol.getSymbolType() == Symbol::Segno) {
2433                 str << "\\mark \\markup { \\musicglyph #\"scripts.segno\" } ";
2434             } else if (symbol.getSymbolType() == Symbol::Coda) {
2435                 str << "\\mark \\markup { \\musicglyph #\"scripts.coda\" } ";
2436             } else if (symbol.getSymbolType() == Symbol::Breath) {
2437                 str << "\\breathe ";
2438             }
2439 
2440         } else if (isNote) {
2441 
2442             Chord chord(*s, i, m_composition->getNotationQuantizer());
2443             Event *e = *chord.getInitialNote();
2444             bool tiedForward = false;
2445             bool tiedUp = false;
2446 
2447             // Examine the following event, and truncate our duration
2448             // if we overlap it.
2449 
2450             if (e->has(DISPLACED_X)) {
2451                 double xDisplacement = 1 + ((double) e->get
2452                                             <Int>(DISPLACED_X)) / 1000;
2453                 str << "\\once \\override NoteColumn #'force-hshift = #"
2454                     << xDisplacement << " ";
2455             }
2456 
2457             const bool hiddenNote = e->has(INVISIBLE) && e->get<Bool>(INVISIBLE);
2458             if (hiddenNote) {
2459                 str << "\\hideNotes ";
2460             }
2461 
2462             // Export stem direction - but don't change it in the middle of a beamed group
2463             if (!m_exportBeams || !inBeamedGroup || startingBeamedGroup) {
2464                 if (e->has(NotationProperties::STEM_UP)) {
2465                     if (e->get<Bool>(NotationProperties::STEM_UP)) {
2466                         if (lastStem != 1) {
2467                             str << "\\stemUp ";
2468                             lastStem = 1;
2469                         }
2470                     } else {
2471                         if (lastStem != -1) {
2472                             str << "\\stemDown ";
2473                             lastStem = -1;
2474                         }
2475                     }
2476                 } else {
2477                     if (lastStem != 0) {
2478                         str << "\\stemNeutral ";
2479                         lastStem = 0;
2480                     }
2481                 }
2482             }
2483 
2484             handleEndingPreEvents(preEventsInProgress, i, str);
2485             handleStartingPreEvents(preEventsToStart, str);
2486 
2487             if (chord.size() > 1)
2488                 str << "< ";
2489 
2490             Segment::iterator stylei = s->end();
2491 
2492             for (i = chord.getInitialElement(); s->isBeforeEndMarker(i); ++i) {
2493 
2494                 event = *i;
2495                 if (event->isa(Text::EventType)) {
2496                     if (!handleDirective(event, lilyText, nextBarIsAlt1, nextBarIsAlt2,
2497                                          nextBarIsDouble, nextBarIsEnd, nextBarIsDot)) {
2498 
2499                         handleText(event, lilyText);
2500                     }
2501 
2502                 } else if (event->isa(Note::EventType)) {
2503 
2504                     if (m_languageLevel >= LILYPOND_VERSION_2_8) {
2505                         // one \tweak per each chord note
2506                         if (chord.size() > 1)
2507                             writeStyle(event, prevStyle, col, str, true);
2508                         else
2509                             writeStyle(event, prevStyle, col, str, false);
2510                     } else {
2511                         // only one override per chord, and that outside the <>
2512                         stylei = i;
2513                     }
2514 
2515                     writePitch(event, key, str);
2516 
2517                     bool noteHasCautionaryAccidental = false;
2518                     event->get
2519                         <Bool>(NotationProperties::USE_CAUTIONARY_ACCIDENTAL, noteHasCautionaryAccidental);
2520                     if (noteHasCautionaryAccidental)
2521                         str << "?";
2522 
2523                     // get TIED_FORWARD and TIE_IS_ABOVE for later
2524                     event->get<Bool>(TIED_FORWARD, tiedForward);
2525                     event->get<Bool>(TIE_IS_ABOVE, tiedUp);
2526 
2527                     str << " ";
2528                 } else if (event->isa(Indication::EventType)) {
2529                     preEventsToStart.insert(event);
2530                     preEventsInProgress.insert(event);
2531                     postEventsToStart.insert(event);
2532                     postEventsInProgress.insert(event);
2533                 }
2534 
2535                 if (i == chord.getFinalElement())
2536                     break;
2537             }
2538 
2539             if (chord.size() > 1)
2540                 str << "> ";
2541 
2542             if (duration != prevDuration) {
2543                 durationRatio = writeDuration(duration, str);
2544                 str << " ";
2545                 prevDuration = duration;
2546             }
2547 
2548             if (m_languageLevel == LILYPOND_VERSION_2_6) {
2549                 // only one override per chord, and that outside the <>
2550                 if (stylei != s->end()) {
2551                     writeStyle(*stylei, prevStyle, col, str, false);
2552                     stylei = s->end();
2553                 }
2554             }
2555 
2556             if (lilyText != "") {
2557                 str << lilyText;
2558                 lilyText = "";
2559             }
2560             writeSlashes(event, str);
2561 
2562             writtenDuration += soundingDuration;
2563             std::pair<int,int> ratio = fractionProduct(durationRatio,tupletRatio);
2564             durationRatioSum = fractionSum(durationRatioSum, ratio);
2565             // str << qstrtostr(QString(" %{ %1/%2 * %3/%4 = %5/%6 %} ").arg(durationRatio.first).arg(durationRatio.second).arg(tupletRatio.first).arg(tupletRatio.second).arg(ratio.first).arg(ratio.second)); // DEBUG
2566 
2567             std::vector<Mark> marks(chord.getMarksForChord());
2568             // problem here: stem direction unavailable (it's a view-local property)
2569             bool stemUp = true;
2570             e->get<Bool>(NotationProperties::STEM_UP, stemUp);
2571             for (std::vector<Mark>::iterator j = marks.begin(); j != marks.end(); ++j) {
2572                 str << composeLilyMark(*j, stemUp);
2573             }
2574             if (!marks.empty())
2575                 str << " ";
2576 
2577             handleEndingPostEvents(postEventsInProgress, i, str);
2578             handleStartingPostEvents(postEventsToStart, str);
2579 
2580             if (tiedForward) {
2581                 if (tiedUp) {
2582                     str << "^~ ";
2583                 } else {
2584                     str << "_~ ";
2585                 }
2586             }
2587 
2588             if (hiddenNote) {
2589                 str << "\\unHideNotes ";
2590             }
2591 
2592             if (m_exportBeams && startingBeamedGroup && nextBeamedNoteInGroup && canStartOrEndBeam(event)) {
2593                 // starting a beamed group
2594                 str << "[ ";
2595                 startingBeamedGroup = false;
2596                 inBeamedGroup = true;
2597                 //RG_DEBUG << "BEGIN GROUP" << groupId;
2598             }
2599         } else if (isRest) {
2600 
2601             const bool hiddenRest = event->has(INVISIBLE) && event->get<Bool>(INVISIBLE);
2602 
2603             // If the rest has a manually repositioned Y coordinate, we try to
2604             // create a letter note of an appropriate height, and bind a \rest
2605             // to it, in order to make repositioned rests exportable.  This is
2606             // necessary because LilyPond's automatic rest collision avoidance
2607             // frequently chokes to death on our untidy machine-generated files,
2608             // and yields terrible results, so we have to offer some manual
2609             // mechanism for adjusting the rests unless we want users to have to
2610             // edit .ly files by hand to correct for this, which we do not.
2611             bool offsetRest = event->has(DISPLACED_Y);
2612             int restOffset  = 0;
2613             if (offsetRest) {
2614                 restOffset  = event->get<Int>(DISPLACED_Y);
2615             }
2616 
2617             //if (offsetRest) {
2618             //    RG_DEBUG << "REST OFFSET: " << restOffset;
2619             //} else {
2620             //    RG_DEBUG << "NO REST OFFSET";
2621             //}
2622 
2623             if (MultiMeasureRestCount == 0) {
2624                 if (hiddenRest) {
2625                     RG_DEBUG << "HIDDEN REST.  Using duration " << duration;
2626                     str << "s";
2627                 } else if (duration == timeSignature.getBarDuration()) {
2628                     // Look ahead the segment in order to detect
2629                     // the number of measures in the multi measure rest.
2630                     RG_DEBUG << "INCREMENTING MULTI-MEASURE COUNTER (offset rest height will be ignored)";
2631                     Segment::iterator mm_i = i;
2632                     while (s->isBeforeEndMarker(++mm_i)) {
2633                         if ((*mm_i)->isa(Note::EventRestType) &&
2634                             (*mm_i)->getNotationDuration() == event->getNotationDuration() &&
2635                             timeSignature == m_composition->getTimeSignatureAt((*mm_i)->getNotationAbsoluteTime())) {
2636                             MultiMeasureRestCount++;
2637                         } else {
2638                             break;
2639                         }
2640                     }
2641                     str << "R";
2642                 } else {
2643                     handleEndingPreEvents(preEventsInProgress, i, str);
2644                     handleStartingPreEvents(preEventsToStart, str);
2645 
2646                     if (offsetRest) {
2647                         // translate the fine tuning of steps into steps
2648                         int offset = -(restOffset / 500);
2649 
2650                         // accept only even steps to imitate Rosegarden's behaviour
2651                         if (offset % 2 != 0) {
2652                             offset += (offset > 0 ? -1 : 1);
2653                         }
2654                         // move the default position of the rest
2655                         int heightOnStaff = 4 + offset;
2656 
2657                         // find out the pitch corresponding to the rest position
2658                         Clef m_lastClefFound((*s).getClefAtTime(event->getAbsoluteTime()));
2659                         Pitch helper(heightOnStaff, m_lastClefFound, Rosegarden::Key::DefaultKey);
2660 
2661                         // use MIDI pitch to get a named note with octavation
2662                         int p = helper.getPerformancePitch();
2663                         std::string n = convertPitchToLilyNote(p, Accidentals::NoAccidental,
2664                                                                Rosegarden::Key::DefaultKey);
2665 
2666                         // write named note
2667                         str << n;
2668 
2669                         RG_DEBUG << "Offsetting rest: "
2670                                   << "offset = " << offset << ", "
2671                                   << "heightOnStaff = " << heightOnStaff << ", "
2672                                   << "pitch = " << p << ", "
2673                                   << "note = " << n;
2674 
2675                         // defer the \rest until after any duration, because it
2676                         // can't come before a duration
2677                         // necessary, which is all determined a bit further on
2678                         needsSlashRest = true;
2679 
2680                     } else {
2681                         str << "r";
2682 
2683                         // a rest CAN have a fermata, and it could have a text
2684                         // mark conceivably, so we need to export these
2685                         std::vector<Mark> marks(Marks::getMarks(*event));
2686                         for (std::vector<Mark>::iterator j = marks.begin(); j != marks.end(); ++j) {
2687                             // even at the most optimistic, I doubt we'd ever
2688                             // find a way to get a stemUp from a rest, so just
2689                             // set this true, because a fermata is the main
2690                             // thing we're after, and it will want to go up top
2691                             // more often than not
2692                             str << composeLilyMark(*j, true);
2693                         }
2694                         if (!marks.empty())
2695                             str << " ";
2696                     }
2697                 }
2698 
2699                 if (duration != prevDuration) {
2700                     durationRatio = writeDuration(duration, str);
2701                     if (MultiMeasureRestCount > 0) {
2702                         str << "*" << (1 + MultiMeasureRestCount);
2703                     }
2704                     prevDuration = duration;
2705                 }
2706 
2707                 // have to add \rest to a fake rest note after any required
2708                 // duration change
2709                 if (needsSlashRest) {
2710                     str << "\\rest";
2711                     needsSlashRest = false;
2712                 }
2713 
2714                 if (lilyText != "") {
2715                     str << lilyText;
2716                     lilyText = "";
2717                 }
2718 
2719                 str << " ";
2720 
2721                 handleEndingPostEvents(postEventsInProgress, i, str);
2722                 handleStartingPostEvents(postEventsToStart, str);
2723             } else {
2724                 MultiMeasureRestCount--;
2725             }
2726             writtenDuration += soundingDuration;
2727             std::pair<int,int> ratio = fractionProduct(durationRatio,tupletRatio);
2728             durationRatioSum = fractionSum(durationRatioSum, ratio);
2729             // str << qstrtostr(QString(" %{ %1/%2 * %3/%4 = %5/%6 %} ").arg(durationRatio.first).arg(durationRatio.second).arg(tupletRatio.first).arg(tupletRatio.second).arg(ratio.first).arg(ratio.second)); // DEBUG
2730         } else if (event->isa(Clef::EventType)) {
2731 
2732             try {
2733                 // Incomplete: Set which note the clef should center on  (DMM - why?)
2734                 // To allow octavation of the clef, enclose the clefname always with quotes.
2735                 str << "\\clef \"";
2736 
2737                 Clef clef(*event);
2738                 const std::string clefType = clef.getClefType();
2739                 str << lilyClefType(clefType);
2740 
2741                 // save clef for later use by rests that need repositioned
2742                 m_lastClefFound = clef;
2743                 RG_DEBUG << "clef:" << clefType << "lastClefFound:" << m_lastClefFound.getClefType();
2744 
2745                 // Transpose the clef one or two octaves up or down, if specified.
2746                 int octaveOffset = clef.getOctaveOffset();
2747                 if (octaveOffset > 0) {
2748                     str << "^" << 1 + 7*octaveOffset;
2749                 } else if (octaveOffset < 0) {
2750                     str << "_" << 1 + 7*(-octaveOffset);
2751                 }
2752 
2753                 str << "\"" << std::endl << indent(col);
2754 
2755             } catch (const Exception &e) {
2756                 RG_WARNING << "Bad clef: " << e.getMessage();
2757             }
2758 
2759         } else if (event->isa(Rosegarden::Key::EventType)) {
2760             // don't export invisible key signatures
2761             const bool hiddenKey = event->has(INVISIBLE) && event->get<Bool>(INVISIBLE);
2762 
2763             try {
2764                 // Remember current key to write key signature only when
2765                 // there is a key change
2766                 Rosegarden::Key previousKey = key;
2767 
2768                 // grab the value of the key anyway, so we know what it was for
2769                 // future calls to writePitch() (fixes #2039048)
2770                 key = Rosegarden::Key(*event);
2771                 // then we only write a \key change to the export stream if the
2772                 // key signature was meant to be visible
2773                 if ((key != previousKey) && !hiddenKey) {
2774                     str << "\\key ";
2775 
2776                     Accidental accidental = Accidentals::NoAccidental;
2777 
2778                     str << convertPitchToLilyNoteName(key.getTonicPitch(), accidental, key);
2779 
2780                     if (key.isMinor()) {
2781                         str << " \\minor";
2782                     } else {
2783                         str << " \\major";
2784                     }
2785                     str << std::endl << indent(col);
2786                 }
2787 
2788             } catch (const Exception &e) {
2789                 RG_WARNING << "Bad key: " << e.getMessage();
2790             }
2791 
2792         } else if (event->isa(Text::EventType)) {
2793 
2794             if (!handleDirective(event, lilyText, nextBarIsAlt1, nextBarIsAlt2,
2795                                  nextBarIsDouble, nextBarIsEnd, nextBarIsDot)) {
2796                 handleText(event, lilyText);
2797             }
2798 
2799         } else if (event->isa(Guitar::Chord::EventType)) {
2800             handleGuitarChord(i, str);
2801         }
2802 
2803         if (event->isa(Indication::EventType)) {
2804             preEventsToStart.insert(event);
2805             preEventsInProgress.insert(event);
2806             postEventsToStart.insert(event);
2807             postEventsInProgress.insert(event);
2808         }
2809 
2810         if ((isNote || isRest)) {
2811             bool endGroup = false;
2812             if (!nextBeamedNoteInGroup) {
2813                 //RG_DEBUG << "Leaving beamed group" << groupId;
2814                 // ending a beamed group
2815                 if (m_exportBeams && inBeamedGroup) {
2816                     str << "] ";
2817                 }
2818                 inBeamedGroup = false;
2819                 if (groupType == GROUP_TYPE_BEAMED)
2820                     endGroup = true;
2821             }
2822             if (groupType == GROUP_TYPE_TUPLED && !nextNoteInTuplet) {
2823                 //RG_DEBUG << "Leaving tuplet group" << groupId;
2824                 str << "} ";
2825                 tupletRatio = std::pair<int, int>(1, 1);
2826                 endGroup = true;
2827             }
2828             if (endGroup) {
2829                 groupId = -1;
2830                 groupType = "";
2831             }
2832         }
2833 
2834         ++i;
2835     } // end of the gigantic while loop, I think
2836 
2837     if (isGrace == 1) {
2838         isGrace = 0;
2839         // str << "%{ grace ends %} "; // DEBUG
2840         str << "} ";
2841     }
2842 
2843     if (lastStem != 0) {
2844         str << "\\stemNeutral ";
2845     }
2846 
2847     if (overlong) {
2848         str << std::endl << indent(col) <<
2849             qstrtostr(QString("% %1").
2850                       arg(tr("warning: overlong bar truncated here")));
2851     }
2852 
2853     //
2854     // Pad bars whose notes do not add up to the length of the bar.
2855     // This may happen if the note quantization fails somehow.
2856     //
2857     if ((barStart + writtenDuration < barEnd) &&
2858         fractionSmaller(durationRatioSum, barDurationRatio)) {
2859         str << std::endl << indent(col) <<
2860             qstrtostr(QString("% %1").
2861                       arg(tr("warning: bar too short, padding with rests")));
2862         str << std::endl << indent(col) <<
2863             qstrtostr(QString("% %1 + %2 < %3  &&  %4/%5 < %6/%7").
2864                       arg(barStart).
2865                       arg(writtenDuration).
2866                       arg(barEnd).
2867                       arg(durationRatioSum.first).
2868                       arg(durationRatioSum.second).
2869                       arg(barDurationRatio.first).
2870                       arg(barDurationRatio.second))
2871             << std::endl << indent(col);
2872 
2873         durationRatio = writeSkip(timeSignature, writtenDuration,
2874                                   (barEnd - barStart) - writtenDuration, true, str);
2875         durationRatioSum = fractionSum(durationRatioSum, durationRatio);
2876     }
2877     //
2878     // Export bar and bar checks.
2879     //
2880     if (nextBarIsDouble) {
2881         str << "\\bar \"||\" ";
2882         nextBarIsDouble = false;
2883     } else if (nextBarIsEnd) {
2884         str << "\\bar \"|.\" ";
2885         nextBarIsEnd = false;
2886     } else if (nextBarIsDot) {
2887         str << "\\bar \":\" ";
2888         nextBarIsDot = false;
2889     } else if (MultiMeasureRestCount == 0) {
2890         str << " |";
2891     }
2892 }
2893 
2894 void
writeTimeSignature(TimeSignature timeSignature,int col,std::ofstream & str)2895 LilyPondExporter::writeTimeSignature(TimeSignature timeSignature,
2896                                      int col, std::ofstream &str)
2897 {
2898     if (timeSignature.isHidden()) {
2899         str << indent (col)
2900             << "\\once \\override Staff.TimeSignature #'break-visibility = #(vector #f #f #f) "
2901             << std::endl;
2902     }
2903     //
2904     // It is not possible to jump between common time signature "C"
2905     // and fractioned time signature "4/4", because new time signature
2906     // is entered only if the time signature fraction changes.
2907     // Maybe some tweak is needed in order to allow the jumping between
2908     // "C" and "4/4" ? (HJJ)
2909     //
2910     if (timeSignature.isCommon() == false) {
2911         // use numberedtime signature: 4/4
2912         str << indent (col)
2913             << "\\once \\override Staff.TimeSignature #'style = #'() "
2914             << std::endl;
2915     }
2916     str << indent (col)
2917         << "\\time "
2918         << timeSignature.getNumerator() << "/"
2919         << timeSignature.getDenominator()
2920         << std::endl << indent(col);
2921 }
2922 
2923 std::pair<int,int>
writeSkip(const TimeSignature & timeSig,timeT offset,timeT duration,bool useRests,std::ofstream & str)2924 LilyPondExporter::writeSkip(const TimeSignature &timeSig,
2925                             timeT offset,
2926                             timeT duration,
2927                             bool useRests,
2928                             std::ofstream &str)
2929 {
2930     DurationList dlist;
2931     timeSig.getDurationListForInterval(dlist, duration, offset);
2932     std::pair<int,int> durationRatioSum(0,1);
2933     std::pair<int,int> durationRatio(0,1);
2934 
2935     int t = 0, count = 0;
2936 
2937     for (DurationList::iterator i = dlist.begin(); ; ++i) {
2938 
2939         if (i == dlist.end() || (*i) != t) {
2940 
2941             if (count > 0) {
2942 
2943                 if (!useRests)
2944                     str << "\\skip ";
2945                 else if (t == timeSig.getBarDuration())
2946                     str << "R";
2947                 else
2948                     str << "r";
2949 
2950                 durationRatio = writeDuration(t, str);
2951 
2952                 if (count > 1) {
2953                     str << "*" << count;
2954                     durationRatio = fractionProduct(durationRatio,count);
2955                 }
2956                 str << " ";
2957 
2958                 durationRatioSum = fractionSum(durationRatioSum,durationRatio);
2959             }
2960 
2961             if (i != dlist.end()) {
2962                 t = *i;
2963                 count = 1;
2964             }
2965 
2966         } else {
2967             ++count;
2968         }
2969 
2970         if (i == dlist.end())
2971             break;
2972     }
2973     return durationRatioSum;
2974 }
2975 
2976 bool
handleDirective(const Event * textEvent,std::string & lilyText,bool & nextBarIsAlt1,bool & nextBarIsAlt2,bool & nextBarIsDouble,bool & nextBarIsEnd,bool & nextBarIsDot)2977 LilyPondExporter::handleDirective(const Event *textEvent,
2978                                   std::string &lilyText,
2979                                   bool &nextBarIsAlt1, bool &nextBarIsAlt2,
2980                                   bool &nextBarIsDouble, bool &nextBarIsEnd, bool &nextBarIsDot)
2981 {
2982     Text text(*textEvent);
2983 
2984     if (text.getTextType() == Text::LilyPondDirective) {
2985         std::string directive = text.getText();
2986         if (directive == Text::FakeSegno) {
2987             lilyText += "^\\markup { \\musicglyph #\"scripts.segno\" } ";
2988         } else if (directive == Text::FakeCoda) {
2989             lilyText += "^\\markup { \\musicglyph #\"scripts.coda\" } ";
2990         } else if (directive == Text::Alternate1) {
2991             nextBarIsAlt1 = true;
2992         } else if (directive == Text::Alternate2) {
2993             nextBarIsAlt1 = false;
2994             nextBarIsAlt2 = true;
2995         } else if (directive == Text::BarDouble) {
2996             nextBarIsDouble = true;
2997         } else if (directive == Text::BarEnd) {
2998             nextBarIsEnd = true;
2999         } else if (directive == Text::BarDot) {
3000             nextBarIsDot = true;
3001         } else {
3002             // pass along less special directives for handling as plain text,
3003             // so they can be attached to chords and whatlike without
3004             // redundancy
3005             return false;
3006         }
3007         return true;
3008     } else {
3009         return false;
3010     }
3011 }
3012 
3013 void
handleText(const Event * textEvent,std::string & lilyText)3014 LilyPondExporter::handleText(const Event *textEvent,
3015                              std::string &lilyText)
3016 {
3017     try {
3018 
3019         Text text(*textEvent);
3020         std::string s = text.getText();
3021         const std::string textType = text.getTextType();
3022 
3023         // only protect illegal chars if this is Text, rather than
3024         // LilyPondDirective
3025         if (textType == Text::EventType)
3026             s = protectIllegalChars(s);
3027 
3028         if (textType == Text::Tempo) {
3029 
3030             // print above staff, bold, large
3031             lilyText += "^\\markup { \\bold \\large \"" + s + "\" } ";
3032 
3033         } else if (textType == Text::LocalTempo) {
3034 
3035             // print above staff, bold, small
3036             lilyText += "^\\markup { \\bold \"" + s + "\" } ";
3037 
3038         } else if (m_chordNamesMode == false && textType == Text::Chord) {
3039 
3040             // Either (1) the chords will have an own ChordNames context
3041             //     or (2) they will be printed above staff, bold, small.
3042             lilyText += "^\\markup { \\bold \"" + s + "\" } ";
3043 
3044         } else if (textType == Text::Dynamic) {
3045 
3046             // supported dynamics first
3047             if (s == "ppppp" || s == "pppp" || s == "ppp" ||
3048                 s == "pp" || s == "p" || s == "mp" ||
3049                 s == "mf" || s == "f" || s == "ff" ||
3050                 s == "fff" || s == "ffff" || s == "rfz" ||
3051                 s == "sf") {
3052 
3053                 lilyText += "-\\" + s + " ";
3054 
3055             } else {
3056                 // export as a plain markup:
3057                 // print below staff, bold italics, small
3058                 lilyText += "_\\markup { \\bold \\italic \"" + s + "\" } ";
3059             }
3060 
3061         } else if (textType == Text::Direction) {
3062 
3063             // print above staff, large
3064             lilyText += "^\\markup { \\large \"" + s + "\" } ";
3065 
3066         } else if (textType == Text::LocalDirection) {
3067 
3068             // print below staff, bold italics, small
3069             lilyText += "_\\markup { \\bold \\italic \"" + s + "\" } ";
3070 
3071         } else if (textType == Text::LilyPondDirective) {
3072             if (s == Text::Gliss) {
3073                 lilyText += "\\glissando ";
3074             } else if (s == Text::Arpeggio) {
3075                 lilyText += "\\arpeggio ";
3076             } else if (s == Text::Tiny) {
3077                 lilyText += "\\tiny ";
3078             } else if (s == Text::Small) {
3079                 lilyText += "\\small ";
3080             } else if (s == Text::NormalSize) {
3081                 lilyText += "\\normalsize ";
3082             }
3083             // LilyPond directives that don't require special handling across
3084             // barlines are handled here along with ordinary text types.  These
3085             // can be injected wherever they happen to occur, and should get
3086             // attached to the right bits in due course without extra effort.
3087         } else if (textType == Text::Lyric) {
3088             // Lyric are handled elsewhere in this file
3089         } else if (textType == Text::Annotation) {
3090             // TODO: should annotations be exported?
3091         } else {
3092             RG_WARNING << "LilyPondExporter::write() - unhandled text type: " << textType;
3093         }
3094     } catch (const Exception &e) {
3095         RG_WARNING << "Bad text: " << e.getMessage();
3096     }
3097 }
3098 
3099 void
writePitch(const Event * note,const Rosegarden::Key & key,std::ofstream & str)3100 LilyPondExporter::writePitch(const Event *note,
3101                              const Rosegarden::Key &key,
3102                              std::ofstream &str)
3103 {
3104     // Note pitch (need name as well as octave)
3105     // It is also possible to have "relative" pitches,
3106     // but for simplicity we always use absolute pitch
3107     // 60 is middle C, one unit is a half-step
3108 
3109     long pitch = 60;
3110     note->get
3111         <Int>(PITCH, pitch);
3112 
3113     Accidental accidental = Accidentals::NoAccidental;
3114     note->get
3115         <String>(ACCIDENTAL, accidental);
3116 
3117     // format of LilyPond note is:
3118     // name + octave + (duration) + text markup
3119 
3120     // calculate note name and write note
3121     std::string lilyNote;
3122 
3123     lilyNote = convertPitchToLilyNote(pitch, accidental, key);
3124 
3125     // handle parallel color
3126 
3127     if (note->has(BaseProperties::MEMBER_OF_PARALLEL)) {
3128 
3129         bool memberOfParallel = false;
3130 
3131         note->get<Bool>(BaseProperties::MEMBER_OF_PARALLEL, memberOfParallel);
3132 
3133         if (memberOfParallel) {
3134             str << "\\tweak color #magenta ";
3135         }
3136     }
3137 
3138     //RG_DEBUG << "NOTE" << lilyNote;
3139     str << lilyNote;
3140 }
3141 
3142 void
writeStyle(const Event * note,std::string & prevStyle,int col,std::ofstream & str,bool isInChord)3143 LilyPondExporter::writeStyle(const Event *note, std::string &prevStyle,
3144                              int col, std::ofstream &str, bool isInChord)
3145 {
3146     // some hard-coded styles in order to provide rudimentary style export support
3147     // note that this is technically bad practice, as style names are not supposed
3148     // to be fixed but deduced from the style files actually present on the system
3149     const std::string styleMensural = "Mensural";
3150     const std::string styleTriangle = "Triangle";
3151     const std::string styleCross = "Cross";
3152     const std::string styleClassical = "Classical";
3153 
3154     // handle various note styles before opening any chord
3155     // brackets
3156     std::string style = "";
3157     note->get
3158         <String>(NotationProperties::NOTE_STYLE, style);
3159 
3160     if (style != prevStyle) {
3161 
3162         if (style == styleClassical && prevStyle == "")
3163             return ;
3164 
3165         if (!isInChord)
3166             prevStyle = style;
3167 
3168         if (style == styleMensural) {
3169             style = "mensural";
3170         } else if (style == styleTriangle) {
3171             style = "triangle";
3172         } else if (style == styleCross) {
3173             style = "cross";
3174         } else {
3175             style = "default"; // failsafe default or explicit
3176         }
3177 
3178         if (!isInChord) {
3179             str << std::endl << indent(col) << "\\override Voice.NoteHead #'style = #'" << style << std::endl << indent(col);
3180         } else {
3181             str << "\\tweak #'style #'" << style << " ";
3182         }
3183     }
3184 }
3185 
3186 std::pair<int,int>
writeDuration(timeT duration,std::ofstream & str)3187 LilyPondExporter::writeDuration(timeT duration,
3188                                 std::ofstream &str)
3189 {
3190     Note note(Note::getNearestNote(duration, MAX_DOTS));
3191     std::pair<int,int> durationRatio(0,1);
3192 
3193     switch (note.getNoteType()) {
3194 
3195     case Note::SixtyFourthNote:
3196         str << "64"; durationRatio = std::pair<int,int>(1,64);
3197         break;
3198 
3199     case Note::ThirtySecondNote:
3200         str << "32"; durationRatio = std::pair<int,int>(1,32);
3201         break;
3202 
3203     case Note::SixteenthNote:
3204         str << "16"; durationRatio = std::pair<int,int>(1,16);
3205         break;
3206 
3207     case Note::EighthNote:
3208         str << "8"; durationRatio = std::pair<int,int>(1,8);
3209         break;
3210 
3211     case Note::QuarterNote:
3212         str << "4"; durationRatio = std::pair<int,int>(1,4);
3213         break;
3214 
3215     case Note::HalfNote:
3216         str << "2"; durationRatio = std::pair<int,int>(1,2);
3217         break;
3218 
3219     case Note::WholeNote:
3220         str << "1"; durationRatio = std::pair<int,int>(1,1);
3221         break;
3222 
3223     case Note::DoubleWholeNote:
3224         str << "\\breve"; durationRatio = std::pair<int,int>(2,1);
3225         break;
3226     }
3227 
3228     for (int numDots = 0; numDots < note.getDots(); numDots++) {
3229         str << ".";
3230     }
3231     durationRatio = fractionProduct(durationRatio,
3232                                     std::pair<int,int>((1<<(note.getDots()+1))-1,1<<note.getDots()));
3233     return durationRatio;
3234 }
3235 
3236 void
writeSlashes(const Event * note,std::ofstream & str)3237 LilyPondExporter::writeSlashes(const Event *note, std::ofstream &str)
3238 {
3239     // if a grace note has tremolo slashes, they have already been used to turn
3240     // the note into a slashed grace note, and need not be exported here
3241     if (note->has(IS_GRACE_NOTE) && note->get<Bool>(IS_GRACE_NOTE)) return;
3242 
3243     // write slashes after text
3244     // / = 8 // = 16 /// = 32, etc.
3245     long slashes = 0;
3246     note->get
3247         <Int>(NotationProperties::SLASHES, slashes);
3248     if (slashes > 0) {
3249         str << ":";
3250         int length = 4;
3251         for (int c = 1; c <= slashes; c++) {
3252             length *= 2;
3253         }
3254         str << length;
3255     }
3256 }
3257 
3258 }
3259