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