1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
5
A MIDI and audio sequencer and musical notation editor.
7
This program is Copyright 2000-2007
8
Guillaume Laurent <glaurent@telegraph-road.org>,
9
Chris Cannam <cannam@all-day-breakfast.com>,
10
Richard Bown <richard.bown@ferventsoftware.com>
12
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
13
Bown to claim authorship of this work have been asserted.
15
This file is Copyright 2002
16
Hans Kieserman <hkieserman@mail.com>
17
with heavy lifting from csoundio as it was on 13/5/2002.
19
Numerous additions and bug fixes by
20
Michael McIntyre <dmmcintyr@users.sourceforge.net>
22
Some restructuring by Chris Cannam.
24
Brain surgery to support LilyPond 2.x export by Heikki Junes.
26
Other copyrights also apply to some parts of this work. Please
27
see the AUTHORS file and individual file headers for details.
29
This program is free software; you can redistribute it and/or
30
modify it under the terms of the GNU General Public License as
31
published by the Free Software Foundation; either version 2 of the
32
License, or (at your option) any later version. See the file
33
COPYING included with this distribution for more information.
37
#include "LilypondExporter.h"
40
#include "misc/Debug.h"
41
#include "misc/Strings.h"
42
#include "document/ConfigGroups.h"
43
#include "base/BaseProperties.h"
44
#include "base/Composition.h"
45
#include "base/Configuration.h"
46
#include "base/Event.h"
47
#include "base/Exception.h"
48
#include "base/Instrument.h"
49
#include "base/NotationTypes.h"
50
#include "base/PropertyName.h"
51
#include "base/Segment.h"
52
#include "base/SegmentNotationHelper.h"
53
#include "base/Sets.h"
54
#include "base/Staff.h"
55
#include "base/Studio.h"
56
#include "base/Track.h"
57
#include "base/NotationQuantizer.h"
58
#include "document/RosegardenGUIDoc.h"
59
#include "gui/application/RosegardenApplication.h"
60
#include "gui/application/RosegardenGUIView.h"
61
#include "gui/editors/notation/NotationProperties.h"
62
#include "gui/editors/notation/NotationView.h"
63
#include "gui/editors/guitar/Chord.h"
64
#include "gui/general/ProgressReporter.h"
65
#include "gui/widgets/CurrentProgressDialog.h"
67
#include <kmessagebox.h>
68
#include <qfileinfo.h>
72
#include <qtextcodec.h>
73
#include <kapplication.h>
79
using namespace BaseProperties;
81
const PropertyName LilypondExporter::SKIP_PROPERTY
82
= "LilypondExportSkipThisEvent";
84
LilypondExporter::LilypondExporter(RosegardenGUIApp *parent,
85
RosegardenGUIDoc *doc,
86
std::string fileName) :
87
ProgressReporter((QObject *)parent, "lilypondExporter"),
91
m_composition = &m_doc->getComposition();
92
m_studio = &m_doc->getStudio();
93
m_view = ((RosegardenGUIApp *)parent)->getView();
94
m_notationView = NULL;
96
readConfigVariables();
99
LilypondExporter::LilypondExporter(NotationView *parent,
100
RosegardenGUIDoc *doc,
101
std::string fileName) :
102
ProgressReporter((QObject *)parent, "lilypondExporter"),
106
m_composition = &m_doc->getComposition();
107
m_studio = &m_doc->getStudio();
109
m_notationView = ((NotationView *)parent);
111
readConfigVariables();
115
LilypondExporter::readConfigVariables(void)
118
KConfig *cfg = kapp->config();
119
cfg->setGroup(NotationViewConfigGroup);
121
m_paperSize = cfg->readUnsignedNumEntry("lilypapersize", PAPER_A4);
122
m_paperLandscape = cfg->readBoolEntry("lilypaperlandscape", false);
123
m_fontSize = cfg->readUnsignedNumEntry("lilyfontsize", FONT_20);
124
m_raggedBottom = cfg->readBoolEntry("lilyraggedbottom", false);
125
m_exportSelection = cfg->readUnsignedNumEntry("lilyexportselection", EXPORT_NONMUTED_TRACKS);
126
m_exportLyrics = cfg->readBoolEntry("lilyexportlyrics", true);
127
m_exportMidi = cfg->readBoolEntry("lilyexportmidi", false);
128
m_exportTempoMarks = cfg->readUnsignedNumEntry("lilyexporttempomarks", EXPORT_NONE_TEMPO_MARKS);
129
m_exportPointAndClick = cfg->readBoolEntry("lilyexportpointandclick", false);
130
m_exportBeams = cfg->readBoolEntry("lilyexportbeamings", false);
131
m_exportStaffGroup = cfg->readBoolEntry("lilyexportstaffgroup", false);
132
m_exportStaffMerge = cfg->readBoolEntry("lilyexportstaffmerge", false);
133
m_lyricsHAlignment = cfg->readBoolEntry("lilylyricshalignment", LEFT_ALIGN);
135
m_languageLevel = cfg->readUnsignedNumEntry("lilylanguage", LILYPOND_VERSION_2_6);
138
LilypondExporter::~LilypondExporter()
144
LilypondExporter::handleStartingEvents(eventstartlist &eventsToStart,
147
eventstartlist::iterator m = eventsToStart.begin();
149
while (m != eventsToStart.end()) {
154
if (i.getIndicationType() == Indication::Slur) {
156
<Bool>(NotationProperties::SLUR_ABOVE))
160
} else if (i.getIndicationType() == Indication::PhrasingSlur) {
162
} else if (i.getIndicationType() == Indication::Crescendo) {
164
} else if (i.getIndicationType() == Indication::Decrescendo) {
168
} catch (Event::BadType) {
170
} catch (Event::NoData e) {
171
std::cerr << "Bad indication: " << e.getMessage() << std::endl;
174
eventstartlist::iterator n(m);
176
eventsToStart.erase(m);
182
LilypondExporter::handleEndingEvents(eventendlist &eventsInProgress,
183
const Segment::iterator &j,
186
eventendlist::iterator k = eventsInProgress.begin();
188
while (k != eventsInProgress.end()) {
190
eventendlist::iterator l(k);
193
// Handle and remove all the relevant events in progress
194
// This assumes all deferred events are indications
199
timeT indicationEnd =
200
(*k)->getNotationAbsoluteTime() + i.getIndicationDuration();
202
(*j)->getNotationAbsoluteTime() + (*j)->getNotationDuration();
204
if (indicationEnd < eventEnd ||
205
((i.getIndicationType() == Indication::Slur ||
206
i.getIndicationType() == Indication::PhrasingSlur) &&
207
indicationEnd == eventEnd)) {
209
if (i.getIndicationType() == Indication::Slur) {
211
} else if (i.getIndicationType() == Indication::PhrasingSlur) {
213
} else if (i.getIndicationType() == Indication::Crescendo ||
214
i.getIndicationType() == Indication::Decrescendo) {
218
eventsInProgress.erase(k);
221
} catch (Event::BadType) {
224
} catch (Event::NoData e) {
225
std::cerr << "Bad indication: " << e.getMessage() << std::endl;
233
LilypondExporter::convertPitchToLilyNote(int pitch, Accidental accidental,
234
const Rosegarden::Key &key)
236
Pitch p(pitch, accidental);
237
std::string lilyNote = "";
239
lilyNote += (char)tolower(p.getNoteName(key));
240
// std::cout << "lilyNote: " << lilyNote << std::endl;
241
Accidental acc = p.getAccidental(key);
242
if (acc == Accidentals::DoubleFlat)
244
else if (acc == Accidentals::Flat)
246
else if (acc == Accidentals::Sharp)
248
else if (acc == Accidentals::DoubleSharp)
255
LilypondExporter::composeLilyMark(std::string eventMark, bool stemUp)
258
std::string inStr = "", outStr = "";
259
std::string prefix = (stemUp) ? "_" : "^";
261
// shoot text mark straight through unless it's sf or rf
262
if (Marks::isTextMark(eventMark)) {
263
inStr = protectIllegalChars(Marks::getTextFromMark(eventMark));
267
} else if (inStr == "rf") {
270
inStr = "\\markup { \\italic " + inStr + " } ";
273
outStr = prefix + inStr;
275
} else if (Marks::isFingeringMark(eventMark)) {
277
// fingering marks: use markup syntax only for non-trivial fingerings
279
inStr = protectIllegalChars(Marks::getFingeringFromMark(eventMark));
281
if (inStr != "0" && inStr != "1" && inStr != "2" && inStr != "3" && inStr != "4" && inStr != "5" && inStr != "+" ) {
282
inStr = "\\markup { \\finger \"" + inStr + "\" } ";
285
outStr = prefix + inStr;
290
// use full \accent format for everything, even though some shortcuts
291
// exist, for the sake of consistency
292
if (eventMark == Marks::Accent) {
293
outStr += "\\accent";
294
} else if (eventMark == Marks::Tenuto) {
295
outStr += "\\tenuto";
296
} else if (eventMark == Marks::Staccato) {
297
outStr += "\\staccato";
298
} else if (eventMark == Marks::Staccatissimo) {
299
outStr += "\\staccatissimo";
300
} else if (eventMark == Marks::Marcato) {
301
outStr += "\\marcato";
302
} else if (eventMark == Marks::Trill) {
304
} else if (eventMark == Marks::LongTrill) {
305
// span trill up to the next note:
306
// tweak the beginning of the next note using an invisible rest having zero length
307
outStr += "\\startTrillSpan s4*0 \\stopTrillSpan";
308
} else if (eventMark == Marks::Turn) {
310
} else if (eventMark == Marks::Pause) {
311
outStr += "\\fermata";
312
} else if (eventMark == Marks::UpBow) {
314
} else if (eventMark == Marks::DownBow) {
315
outStr += "\\downbow";
318
std::cerr << "LilypondExporter::composeLilyMark() - unhandled mark: "
319
<< eventMark << std::endl;
327
LilypondExporter::indent(const int &column)
329
std::string outStr = "";
330
for (int c = 1; c <= column; c++) {
337
LilypondExporter::protectIllegalChars(std::string inStr)
340
QString tmpStr = strtoqstr(inStr);
342
tmpStr.replace(QRegExp("&"), "\\&");
343
tmpStr.replace(QRegExp("\\^"), "\\^");
344
tmpStr.replace(QRegExp("%"), "\\%");
345
tmpStr.replace(QRegExp("<"), "\\<");
346
tmpStr.replace(QRegExp(">"), "\\>");
347
tmpStr.replace(QRegExp("\\["), "");
348
tmpStr.replace(QRegExp("\\]"), "");
349
tmpStr.replace(QRegExp("\\{"), "");
350
tmpStr.replace(QRegExp("\\}"), "");
353
// LilyPond uses utf8 encoding.
355
return tmpStr.utf8().data();
359
LilypondExporter::write()
361
QString tmpName = strtoqstr(m_fileName);
363
// dmm - modified to act upon the filename itself, rather than the whole
364
// path; fixes bug #855349
366
// split name into parts:
367
QFileInfo nfo(tmpName);
368
QString dirName = nfo.dirPath();
369
QString baseName = nfo.fileName();
371
// sed Lilypond-choking chars out of the filename proper
372
bool illegalFilename = (baseName.contains(' ') || baseName.contains("\\"));
373
baseName.replace(QRegExp(" "), "");
374
baseName.replace(QRegExp("\\\\"), "");
375
baseName.replace(QRegExp("'"), "");
376
baseName.replace(QRegExp("\""), "");
379
tmpName = dirName + '/' + baseName;
381
if (illegalFilename) {
382
CurrentProgressDialog::freeze();
383
int reply = KMessageBox::warningContinueCancel(
384
0, i18n("Lilypond does not allow spaces or backslashes in filenames.\n\n"
385
"Would you like to use\n\n %1\n\n instead?").arg(baseName));
386
if (reply != KMessageBox::Continue)
390
std::ofstream str(qstrtostr(tmpName).c_str(), std::ios::out);
392
std::cerr << "LilypondExporter::write() - can't write file " << tmpName << std::endl;
396
str << "% This LilyPond file was generated by Rosegarden " << protectIllegalChars(VERSION) << std::endl;
398
switch (m_languageLevel) {
400
case LILYPOND_VERSION_2_6:
401
str << "\\version \"2.6.0\"" << std::endl;
404
case LILYPOND_VERSION_2_8:
405
str << "\\version \"2.8.0\"" << std::endl;
408
case LILYPOND_VERSION_2_10:
409
str << "\\version \"2.10.0\"" << std::endl;
412
case LILYPOND_VERSION_2_12:
413
str << "\\version \"2.12.0\"" << std::endl;
417
// force the default version if there was an error
418
std::cerr << "ERROR: Unknown language level " << m_languageLevel
419
<< ", using \\version \"2.6.0\" instead" << std::endl;
420
str << "\\version \"2.6.0\"" << std::endl;
421
m_languageLevel = LILYPOND_VERSION_2_6;
424
// enable "point and click" debugging via pdf to make finding the
425
// unfortunately inevitable errors easier
426
if (m_exportPointAndClick) {
427
str << "% point and click debugging is enabled" << std::endl;
429
str << "% point and click debugging is disabled" << std::endl;
430
str << "#(ly:set-option 'point-and-click #f)" << std::endl;
433
// Lilypond \header block
435
// set indention level to make future changes to horizontal layout less
436
// tedious, ++col to indent a new level, --col to de-indent
439
// grab user headers from metadata
440
Configuration metadata = m_composition->getMetadata();
441
std::vector<std::string> propertyNames = metadata.getPropertyNames();
443
// open \header section if there's metadata to grab, and if the user
445
if (!propertyNames.empty()) {
446
str << "\\header {" << std::endl;
449
bool userTagline = false;
451
for (unsigned int index = 0; index < propertyNames.size(); ++index) {
452
std::string property = propertyNames [index];
453
if (property == headerDedication || property == headerTitle ||
454
property == headerSubtitle || property == headerSubsubtitle ||
455
property == headerPoet || property == headerComposer ||
456
property == headerMeter || property == headerOpus ||
457
property == headerArranger || property == headerInstrument ||
458
property == headerPiece || property == headerCopyright ||
459
property == headerTagline) {
460
std::string header = protectIllegalChars(metadata.get<String>(property));
462
str << indent(col) << property << " = \"" << header << "\"" << std::endl;
463
// let users override defaults, but allow for providing
464
// defaults if they don't:
465
if (property == headerTagline)
473
str << indent(col) << "tagline = \""
474
<< "Created using Rosegarden " << protectIllegalChars(VERSION) << " and LilyPond"
475
<< "\"" << std::endl;
479
str << indent(--col) << "}" << std::endl;
482
// Lilypond \paper block (optional)
483
if (m_raggedBottom) {
484
str << indent(col) << "\\paper {" << std::endl;
485
str << indent(++col) << "ragged-bottom=##t" << std::endl;
486
str << indent(--col) << "}" << std::endl;
489
// Lilypond music data! Mapping:
490
// Lilypond Voice = Rosegarden Segment
491
// Lilypond Staff = Rosegarden Track
492
// (not the cleanest output but maybe the most reliable)
496
switch (m_fontSize) {
519
font = 20; // if config problem
522
str << indent(col) << "#(set-global-staff-size " << font << ")" << std::endl;
524
// write user-specified paper type as default paper size
525
std::string paper = "";
526
switch (m_paperSize) {
550
break; // "do not specify"
553
str << indent(col) << "#(set-default-paper-size \"" << paper << "\""
554
<< (m_paperLandscape ? " 'landscape" : "") << ")"
558
// Find out the printed length of the composition
559
Composition::iterator i = m_composition->begin();
561
str << indent(col) << "\\score {" << std::endl;
562
str << indent(++col) << "% no segments found" << std::endl;
563
// bind staffs with or without staff group bracket
564
str << indent(col) // indent
565
<< "<<" << " s4 " << ">>" << std::endl;
566
str << indent(col) << "\\layout { }" << std::endl;
567
str << indent(--col) << "}" << std::endl;
570
timeT compositionStartTime = (*i)->getStartTime();
571
timeT compositionEndTime = (*i)->getEndMarkerTime();
572
for (; i != m_composition->end(); ++i) {
573
if (compositionStartTime > (*i)->getStartTime() && (*i)->getTrack() >= 0) {
574
compositionStartTime = (*i)->getStartTime();
576
if (compositionEndTime < (*i)->getEndMarkerTime()) {
577
compositionEndTime = (*i)->getEndMarkerTime();
581
// define global context which is common for all staffs
582
str << indent(col++) << "global = { " << std::endl;
583
TimeSignature timeSignature = m_composition->
584
getTimeSignatureAt(m_composition->getStartMarker());
585
if (m_composition->getBarStart(m_composition->getBarNumber(compositionStartTime)) < compositionStartTime) {
586
str << indent(col) << "\\partial ";
587
// Arbitrary partial durations are handled by the following way:
588
// split the partial duration to 64th notes: instead of "4" write "64*16". (hjj)
589
Note partialNote = Note::getNearestNote(1, MAX_DOTS);
590
int partialDuration = m_composition->getBarStart(m_composition->getBarNumber(compositionStartTime) + 1) - compositionStartTime;
591
writeDuration(1, str);
592
str << "*" << ((int)(partialDuration / partialNote.getDuration()))
596
int rightBar = leftBar;
599
m_composition->getTimeSignatureInBar(rightBar + 1, isNew);
601
if (isNew || (m_composition->getBarStart(rightBar + 1) >= compositionEndTime)) {
602
// - set initial time signature; further time signature changes
603
// are defined within the segments, because they may be hidden
604
str << indent(col) << (leftBar == 0 ? "" : "% ") << "\\time "
605
<< timeSignature.getNumerator() << "/"
606
<< timeSignature.getDenominator() << std::endl;
607
// - place skips upto the end of the composition;
608
// this justifies the printed staffs
610
timeT leftTime = m_composition->getBarStart(leftBar);
611
timeT rightTime = m_composition->getBarStart(rightBar + 1);
612
if (leftTime < compositionStartTime) {
613
leftTime = compositionStartTime;
615
writeSkip(timeSignature, leftTime, rightTime - leftTime, false, str);
616
str << " %% " << (leftBar + 1) << "-" << (rightBar + 1) << std::endl;
618
timeSignature = m_composition->getTimeSignatureInBar(rightBar + 1, isNew);
619
leftBar = rightBar + 1;
621
} while (m_composition->getBarStart(++rightBar) < compositionEndTime);
622
str << indent(--col) << "}" << std::endl;
624
// time signatures changes are in segments, reset initial value
625
timeSignature = m_composition->
626
getTimeSignatureAt(m_composition->getStartMarker());
628
// All the tempo changes are included in "globalTempo" context.
629
// This context contains only skip notes between the tempo changes.
630
// First tempo marking should still be include in \midi{ } block.
631
// If tempo marks are printed in future, they should probably be
632
// included in this context and the note duration in the tempo
633
// mark should be according to the time signature. (hjj)
634
int tempoCount = m_composition->getTempoChangeCount();
636
if (tempoCount > 0) {
638
timeT prevTempoChangeTime = m_composition->getStartMarker();
639
int tempo = int(Composition::getTempoQpm(m_composition->getTempoAtTime(prevTempoChangeTime)));
640
bool tempoMarksInvisible = false;
642
str << indent(col++) << "globalTempo = {" << std::endl;
643
if (m_exportTempoMarks == EXPORT_NONE_TEMPO_MARKS && tempoMarksInvisible == false) {
644
str << indent(col) << "\\override Score.MetronomeMark #'transparent = ##t" << std::endl;
645
tempoMarksInvisible = true;
647
str << indent(col) << "\\tempo 4 = " << tempo << " ";
648
int prevTempo = tempo;
650
for (int i = 0; i < tempoCount; ++i) {
652
std::pair<timeT, long> tempoChange =
653
m_composition->getTempoChange(i);
655
timeT tempoChangeTime = tempoChange.first;
657
tempo = int(Composition::getTempoQpm(tempoChange.second));
659
// First tempo change may be before the first segment.
660
// Do not apply it before the first segment appears.
661
if (tempoChangeTime < compositionStartTime) {
662
tempoChangeTime = compositionStartTime;
663
} else if (tempoChangeTime >= compositionEndTime) {
664
tempoChangeTime = compositionEndTime;
666
if (prevTempoChangeTime < compositionStartTime) {
667
prevTempoChangeTime = compositionStartTime;
668
} else if (prevTempoChangeTime >= compositionEndTime) {
669
prevTempoChangeTime = compositionEndTime;
671
writeSkip(m_composition->getTimeSignatureAt(tempoChangeTime),
672
tempoChangeTime, tempoChangeTime - prevTempoChangeTime, false, str);
673
// add new \tempo only if tempo was changed
674
if (tempo != prevTempo) {
675
if (m_exportTempoMarks == EXPORT_FIRST_TEMPO_MARK && tempoMarksInvisible == false) {
676
str << std::endl << indent(col) << "\\override Score.MetronomeMark #'transparent = ##t";
677
tempoMarksInvisible = true;
679
str << std::endl << indent(col) << "\\tempo 4 = " << tempo << " ";
683
prevTempoChangeTime = tempoChangeTime;
684
if (prevTempoChangeTime == compositionEndTime)
687
// First tempo change may be before the first segment.
688
// Do not apply it before the first segment appears.
689
if (prevTempoChangeTime < compositionStartTime) {
690
prevTempoChangeTime = compositionStartTime;
692
writeSkip(m_composition->getTimeSignatureAt(prevTempoChangeTime),
693
prevTempoChangeTime, compositionEndTime - prevTempoChangeTime, false, str);
695
str << indent(--col) << "}" << std::endl;
698
// open \score section
699
str << "\\score {" << std::endl;
701
// bind staffs with or without staff group bracket
702
str << indent(++col) // indent+
703
<< (m_exportStaffGroup == true ? "\\new StaffGroup " : "")
704
<< "<<" << std::endl;
706
// Make chords offset colliding notes by default
707
str << indent(++col) << "% force offset of colliding notes in chords:" << std::endl;
709
<< "\\override Score.NoteColumn #\'force-hshift = #1.0" << std::endl;
711
int lastTrackIndex = -1;
712
int voiceCounter = 0;
714
// Write out all segments for each Track, in track order.
715
// This involves a hell of a lot of loops through all tracks
716
// and segments, but the time spent doing that should still
717
// be relatively small in the greater scheme.
721
for (int trackPos = 0;
722
(track = m_composition->getTrackByPosition(trackPos)) != 0; ++trackPos) {
724
for (Composition::iterator i = m_composition->begin();
725
i != m_composition->end(); ++i) {
727
if ((*i)->getTrack() != track->getId())
730
emit setProgress(int(double(trackPos) /
731
double(m_composition->getNbTracks()) * 100.0));
732
rgapp->refreshGUI(50);
734
bool currentSegmentSelected = false;
735
if ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) &&
736
(m_view != NULL) && (m_view->haveSelection())) {
738
// Check whether the current segment is in the list of selected segments.
740
SegmentSelection selection = m_view->getSelection();
741
for (SegmentSelection::iterator it = selection.begin(); it != selection.end(); it++) {
742
if ((*it) == (*i)) currentSegmentSelected = true;
744
} else if ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && (m_notationView != NULL)) {
745
currentSegmentSelected = m_notationView->hasSegment(*i);
748
// Check whether the track is a non-midi track.
749
InstrumentId instrumentId = track->getInstrument();
750
// very off the cuff tentative fix for #1836149; this is so obvious
751
// a fix that I wonder why this <SoftSynthInstrumentBase was ever
752
// written in the first place, and it makes me suspicous that I'm
753
// missing the big picture somewhere. Anyway, it seems to work:
754
//bool isMidiTrack = (instrumentId >= MidiInstrumentBase && instrumentId) < SoftSynthInstrumentBase);
755
bool isMidiTrack = instrumentId >= MidiInstrumentBase;
757
if (isMidiTrack && ( // Skip non-midi tracks.
758
(m_exportSelection == EXPORT_ALL_TRACKS) ||
759
((m_exportSelection == EXPORT_NONMUTED_TRACKS) && (!track->isMuted())) ||
760
((m_exportSelection == EXPORT_SELECTED_TRACK) && (m_view != NULL) && (track->getId() == m_composition->getSelectedTrack())) ||
761
((m_exportSelection == EXPORT_SELECTED_TRACK) && (m_notationView != NULL) && (track->getId() == m_notationView->getCurrentSegment()->getTrack())) ||
762
((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && (currentSegmentSelected)))) {
763
if ((int) (*i)->getTrack() != lastTrackIndex) {
764
if (lastTrackIndex != -1) {
765
// close the old track (Staff context)
766
str << indent(--col) << ">> % Staff" << std::endl; // indent-
768
lastTrackIndex = (*i)->getTrack();
770
// avoid problem with <untitled> tracks yielding a
771
// .ly file that jumbles all notes together on a
772
// single staff... every Staff context has to
773
// have a unique name, even if the
774
// Staff.instrument property is the same for
775
// multiple staffs...
776
// Added an option to merge staffs with the same, non-empty
777
// name. This option makes it possible to produce staffs
778
// with polyphonic, and polyrhytmic, music. Polyrhytmic
779
// music in a single staff is typical in piano, or
780
// guitar music. (hjj)
781
// In the case of colliding note heads, user may define
782
// - DISPLACED_X -- for a note/chord
783
// - INVISIBLE -- for a rest
784
std::ostringstream staffName;
785
staffName << protectIllegalChars(m_composition->
786
getTrackById(lastTrackIndex)->getLabel());
788
if (!m_exportStaffMerge || staffName.str() == "") {
789
str << std::endl << indent(col)
790
<< "\\context Staff = \"track "
791
<< (trackPos + 1) << "\" ";
793
str << std::endl << indent(col)
794
<< "\\context Staff = \"" << staffName.str()
798
str << "<< " << std::endl;
800
// The octavation is omitted in the instrument name.
801
// HJJ: Should it be automatically added to the clef: G^8 ?
802
// What if two segments have different transpose in a track?
803
std::ostringstream staffNameWithTranspose;
804
staffNameWithTranspose << "\\markup { \\column { \"" << staffName.str() << " \"";
805
if (((*i)->getTranspose() % 12) != 0) {
806
staffNameWithTranspose << " \\line { ";
807
switch ((*i)->getTranspose() % 12) {
808
case 1 : staffNameWithTranspose << "\"in D\" \\smaller \\flat"; break;
809
case 2 : staffNameWithTranspose << "\"in D\""; break;
810
case 3 : staffNameWithTranspose << "\"in E\" \\smaller \\flat"; break;
811
case 4 : staffNameWithTranspose << "\"in E\""; break;
812
case 5 : staffNameWithTranspose << "\"in F\""; break;
813
case 6 : staffNameWithTranspose << "\"in G\" \\smaller \\flat"; break;
814
case 7 : staffNameWithTranspose << "\"in G\""; break;
815
case 8 : staffNameWithTranspose << "\"in A\" \\smaller \\flat"; break;
816
case 9 : staffNameWithTranspose << "\"in A\""; break;
817
case 10 : staffNameWithTranspose << "\"in B\" \\smaller \\flat"; break;
818
case 11 : staffNameWithTranspose << "\"in B\""; break;
820
staffNameWithTranspose << " }";
822
staffNameWithTranspose << " } }";
823
if (m_languageLevel < LILYPOND_VERSION_2_10) {
824
str << indent(++col) << "\\set Staff.instrument = " << staffNameWithTranspose.str() << std::endl;
826
str << indent(++col) << "\\set Staff.instrumentName = " << staffNameWithTranspose.str() << std::endl;
830
// Set midi instrument for the Staff
831
std::ostringstream staffMidiName;
832
Instrument *instr = m_studio->getInstrumentById(m_composition->getTrackById(lastTrackIndex)->getInstrument());
833
staffMidiName << instr->getProgramName();
835
str << indent(col) << "\\set Staff.midiInstrument = \"" << staffMidiName.str() << "\"" << std::endl;
838
// multi measure rests are used by default
839
str << indent(col) << "\\set Score.skipBars = ##t" << std::endl;
841
// turn off the stupid accidental cancelling business,
842
// because we don't do that ourselves, and because my 11
843
// year old son pointed out to me that it "Looks really
844
// stupid. Why is it cancelling out four flats and then
845
// adding five flats back? That's brain damaged."
846
str << indent(col) << "\\set Staff.printKeyCancellation = ##f" << std::endl;
847
str << indent(col) << "\\new Voice \\global" << std::endl;
848
if (tempoCount > 0) {
849
str << indent(col) << "\\new Voice \\globalTempo" << std::endl;
853
// Temporary storage for non-atomic events (!BOOM)
854
// ex. Lilypond expects signals when a decrescendo starts
855
// as well as when it ends
856
eventendlist eventsInProgress;
857
eventstartlist eventsToStart;
859
// If the segment doesn't start at 0, add a "skip" to the start
860
// No worries about overlapping segments, because Voices can overlap
861
// voiceCounter is a hack because Lilypond does not by default make
863
std::ostringstream voiceNumber;
864
voiceNumber << "voice " << ++voiceCounter;
866
str << std::endl << indent(col++) << "\\context Voice = \"" << voiceNumber.str()
867
<< "\" {"; // indent+
869
str << std::endl << indent(col) << "\\override Voice.TextScript #'padding = #2.0";
870
str << std::endl << indent(col) << "\\override MultiMeasureRest #'expand-limit = 1" << std::endl;
872
SegmentNotationHelper helper(**i);
873
helper.setNotationProperties();
875
int firstBar = m_composition->getBarNumber((*i)->getStartTime());
878
// Add a skip for the duration until the start of the first
879
// bar in the segment. If the segment doesn't start on a bar
880
// line, an additional skip will be written (in the form of
881
// a series of rests) at the start of writeBar, below.
882
//!!! This doesn't cope correctly yet with time signature changes
883
// during this skipped section.
884
str << std::endl << indent(col);
885
writeSkip(timeSignature, compositionStartTime, m_composition->getBarStart(firstBar) - compositionStartTime,
889
std::string lilyText = ""; // text events
890
std::string prevStyle = ""; // track note styles
894
bool haveRepeating = false;
895
bool haveAlternates = false;
897
bool nextBarIsAlt1 = false;
898
bool nextBarIsAlt2 = false;
899
bool prevBarWasAlt2 = false;
901
int MultiMeasureRestCount = 0;
903
bool nextBarIsDouble = false;
904
bool nextBarIsEnd = false;
905
bool nextBarIsDot = false;
907
for (int barNo = m_composition->getBarNumber((*i)->getStartTime());
908
barNo <= m_composition->getBarNumber((*i)->getEndMarkerTime());
911
timeT barStart = m_composition->getBarStart(barNo);
912
timeT barEnd = m_composition->getBarEnd(barNo);
913
if (barStart < compositionStartTime) {
914
barStart = compositionStartTime;
917
// open \repeat section if this is the first bar in the
919
if ((*i)->isRepeating() && !haveRepeating) {
921
haveRepeating = true;
923
//!!! calculate the number of times this segment
924
//repeats and make the following variable meaningful
927
str << std::endl << indent(col++) << "\\repeat volta " << numRepeats << " {";
930
// open the \alternative section if this bar is alternative ending 1
931
// ending (because there was an "Alt1" flag in the
932
// previous bar to the left of where we are right now)
934
// Alt1 remains in effect until we run into Alt2, which
935
// runs to the end of the segment
936
if (nextBarIsAlt1 && haveRepeating) {
937
str << std::endl << indent(--col) << "} \% repeat close (before alternatives) ";
938
str << std::endl << indent(col++) << "\\alternative {";
939
str << std::endl << indent(col++) << "{ \% open alternative 1 ";
940
nextBarIsAlt1 = false;
941
haveAlternates = true;
942
} else if (nextBarIsAlt2 && haveRepeating) {
943
if (!prevBarWasAlt2) {
945
str << std::endl << indent(--col) << "} \% close alternative 1 "
946
<< std::endl << indent(col++) << "{ \% open alternative 2";
949
prevBarWasAlt2 = true;
952
// write out a bar's worth of events
953
writeBar(*i, barNo, barStart, barEnd, col, key,
955
prevStyle, eventsInProgress, str,
956
MultiMeasureRestCount,
957
nextBarIsAlt1, nextBarIsAlt2, nextBarIsDouble, nextBarIsEnd, nextBarIsDot);
964
// close \alternative section if present
965
if (haveAlternates) {
966
str << std::endl << indent(--col) << " } \% close alternative 2 ";
969
// close \repeat section in either case
970
str << std::endl << indent(--col) << " } \% close "
971
<< (haveAlternates ? "alternatives" : "repeat");
975
if (((*i)->getEndMarkerTime() == compositionEndTime) && !haveRepeating) {
976
str << std::endl << indent(col) << "\\bar \"|.\"";
979
// close Voice context
980
str << std::endl << indent(--col) << "} % Voice" << std::endl; // indent-
983
// Write accumulated lyric events to the Lyric context, if desired.
985
// Sync the code below with LyricEditDialog::unparse() !!
987
if (m_exportLyrics) {
988
for (long currentVerse = 0, lastVerse = 0;
989
currentVerse <= lastVerse;
991
bool haveLyric = false;
992
bool firstNote = true;
995
timeT lastTime = (*i)->getStartTime();
996
int lastBarNo = m_composition->getBarNumber(lastTime);
997
for (Segment::iterator j = (*i)->begin();
998
(*i)->isBeforeEndMarker(j); ++j) {
1000
bool isNote = (*j)->isa(Note::EventType);
1001
bool isLyric = false;
1004
if ((*j)->isa(Text::EventType)) {
1005
std::string textType;
1007
<String>(Text::TextTypePropertyName, textType) &&
1008
textType == Text::Lyric) {
1014
if (!isNote && !isLyric) continue;
1016
timeT myTime = (*j)->getNotationAbsoluteTime();
1017
int myBarNo = m_composition->getBarNumber(myTime);
1020
if ((myTime > lastTime) || firstNote) {
1031
(*j)->get<Int>(Text::LyricVersePropertyName, verse);
1033
if (verse == currentVerse) {
1034
std::string ssyllable;
1035
(*j)->get<String>(Text::TextPropertyName, ssyllable);
1038
QString syllable(strtoqstr(ssyllable));
1039
syllable.replace(QRegExp("\\s+"), "");
1040
text += "\"" + syllable + "\"";
1042
} else if (verse > lastVerse) {
1048
text.replace( QRegExp(" _+([^ ])") , " \\1" );
1049
text.replace( "\"_\"" , " " );
1051
// Do not create empty context for lyrics.
1052
// Does this save some vertical space, as was written
1053
// in earlier comment?
1055
if ( rx.search( text ) != -1 ) {
1057
str << indent(col) << "\\lyricsto \"" << voiceNumber.str() << "\""
1058
<< " \\new Lyrics \\lyricmode {" << std::endl;
1059
if (m_lyricsHAlignment == RIGHT_ALIGN) {
1060
str << indent(++col) << "\\override LyricText #'self-alignment-X = #RIGHT" << std::endl;
1061
} else if (m_lyricsHAlignment == CENTER_ALIGN) {
1062
str << indent(++col) << "\\override LyricText #'self-alignment-X = #CENTER" << std::endl;
1064
str << indent(++col) << "\\override LyricText #'self-alignment-X = #LEFT" << std::endl;
1066
str << indent(col) << "\\set ignoreMelismata = ##t" << std::endl;
1067
str << indent(col) << text.utf8() << " " << std::endl;
1068
str << indent(col) << "\\unset ignoreMelismata" << std::endl;
1069
str << indent(--col) << "} % Lyrics " << (currentVerse+1) << std::endl;
1070
// close the Lyrics context
1078
// close the last track (Staff context)
1079
if (voiceCounter > 0) {
1080
str << indent(--col) << ">> % Staff (final)"; // indent-
1082
str << indent(--col) << "% (All staffs were muted.)" << std::endl;
1085
// close \notes section
1086
str << std::endl << indent(--col) << ">> % notes" << std::endl << std::endl; // indent-
1088
// write \layout block
1089
str << indent(col) << "\\layout { }" << std::endl;
1091
// write initial tempo in Midi block, if user wishes (added per user request...
1092
// makes debugging the .ly file easier because fewer "noisy" errors are
1093
// produced during the process of rendering MIDI...)
1095
int tempo = int(Composition::getTempoQpm(m_composition->getTempoAtTime(m_composition->getStartMarker())));
1096
// Incomplete? Can I get away without converting tempo relative to the time
1097
// signature for this purpose? we'll see...
1098
str << indent(col++) << "\\midi {" << std::endl;
1099
str << indent(col) << "\\tempo 4 = " << tempo << std::endl;
1100
str << indent(--col) << "} " << std::endl;
1103
// close \score section and close out the file
1104
str << "} % score" << std::endl;
1110
LilypondExporter::calculateDuration(Segment *s,
1111
const Segment::iterator &i,
1113
timeT &soundingDuration,
1114
const std::pair<int, int> &tupletRatio,
1117
timeT duration = (*i)->getNotationDuration();
1118
timeT absTime = (*i)->getNotationAbsoluteTime();
1120
RG_DEBUG << "LilypondExporter::calculateDuration: first duration, absTime: "
1121
<< duration << ", " << absTime << endl;
1123
timeT durationCorrection = 0;
1125
if ((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType)) {
1127
// tuplet compensation, etc
1128
Note::Type type = (*i)->get<Int>(NOTE_TYPE);
1129
int dots = (*i)->get<Int>(NOTE_DOTS);
1130
durationCorrection = Note(type, dots).getDuration() - duration;
1131
} catch (Exception e) { // no properties
1135
duration += durationCorrection;
1137
RG_DEBUG << "LilypondExporter::calculateDuration: now duration is "
1138
<< duration << " after correction of " << durationCorrection << endl;
1140
soundingDuration = duration * tupletRatio.first/ tupletRatio.second;
1142
timeT toNext = barEnd - absTime;
1143
if (soundingDuration > toNext) {
1144
soundingDuration = toNext;
1145
duration = soundingDuration * tupletRatio.second/ tupletRatio.first;
1149
RG_DEBUG << "LilypondExporter::calculateDuration: time to barEnd is "
1152
// Examine the following event, and truncate our duration
1153
// if we overlap it.
1154
Segment::iterator nextElt = s->end();
1155
toNext = soundingDuration;
1157
if ((*i)->isa(Note::EventType)) {
1159
Chord chord(*s, i, m_composition->getNotationQuantizer());
1160
Segment::iterator nextElt = chord.getFinalElement();
1163
if (s->isBeforeEndMarker(nextElt)) {
1164
// The quantizer sometimes sticks a rest at the same time
1165
// as this note -- don't use that one here, and mark it as
1166
// not to be exported -- it's just a heavy-handed way of
1167
// rendering counterpoint in RG
1168
if ((*nextElt)->isa(Note::EventRestType) &&
1169
(*nextElt)->getNotationAbsoluteTime() == absTime) {
1170
(*nextElt)->set<Bool>(SKIP_PROPERTY, true);
1178
while (s->isBeforeEndMarker(nextElt)) {
1179
if ((*nextElt)->isa(Controller::EventType) ||
1180
(*nextElt)->isa(ProgramChange::EventType) ||
1181
(*nextElt)->isa(SystemExclusive::EventType) ||
1182
(*nextElt)->isa(ChannelPressure::EventType) ||
1183
(*nextElt)->isa(KeyPressure::EventType) ||
1184
(*nextElt)->isa(PitchBend::EventType))
1191
if (s->isBeforeEndMarker(nextElt)) {
1192
RG_DEBUG << "LilypondExporter::calculateDuration: inside conditional " << endl;
1193
toNext = (*nextElt)->getNotationAbsoluteTime() - absTime;
1194
// if the note was lengthened, assume it was lengthened to the left
1195
// when truncating to the beginning of the next note
1196
if (durationCorrection > 0) {
1197
toNext += durationCorrection;
1199
if (soundingDuration > toNext) {
1200
soundingDuration = toNext;
1201
duration = soundingDuration * tupletRatio.second/ tupletRatio.first;
1205
RG_DEBUG << "LilypondExporter::calculateDuration: second toNext is "
1208
RG_DEBUG << "LilypondExporter::calculateDuration: final duration, soundingDuration: " << duration << ", " << soundingDuration << endl;
1214
LilypondExporter::writeBar(Segment *s,
1215
int barNo, int barStart, int barEnd, int col,
1216
Rosegarden::Key &key,
1217
std::string &lilyText,
1218
std::string &prevStyle,
1219
eventendlist &eventsInProgress,
1221
int &MultiMeasureRestCount,
1222
bool &nextBarIsAlt1, bool &nextBarIsAlt2,
1223
bool &nextBarIsDouble, bool &nextBarIsEnd, bool &nextBarIsDot)
1225
int lastStem = 0; // 0 => unset, -1 => down, 1 => up
1227
Segment::iterator i = s->findTime(barStart);
1228
if (!s->isBeforeEndMarker(i))
1231
if (MultiMeasureRestCount == 0) {
1234
if ((barNo + 1) % 5 == 0) {
1235
str << "%% " << barNo + 1 << std::endl << indent(col);
1242
TimeSignature timeSignature = m_composition->getTimeSignatureInBar(barNo, isNew);
1244
if (timeSignature.isHidden()) {
1245
str << "\\once \\override Staff.TimeSignature #'break-visibility = #(vector #f #f #f) ";
1248
<< timeSignature.getNumerator() << "/"
1249
<< timeSignature.getDenominator()
1250
<< std::endl << indent(col);
1253
timeT absTime = (*i)->getNotationAbsoluteTime();
1254
timeT writtenDuration = 0;
1255
std::pair<int,int> barDurationRatio(timeSignature.getNumerator(),timeSignature.getDenominator());
1256
std::pair<int,int> durationRatioSum(0,1);
1257
static std::pair<int,int> durationRatio(0,1);
1259
if (absTime > barStart) {
1260
Note note(Note::getNearestNote(absTime - barStart, MAX_DOTS));
1261
writtenDuration += note.getDuration();
1262
durationRatio = writeSkip(timeSignature, 0, note.getDuration(), true, str);
1263
durationRatioSum = fractionSum(durationRatioSum,durationRatio);
1264
// str << qstrtostr(QString(" %{ %1/%2 %} ").arg(durationRatio.first).arg(durationRatio.second)); // DEBUG
1267
timeT prevDuration = -1;
1268
eventstartlist eventsToStart;
1271
std::string groupType = "";
1272
std::pair<int, int> tupletRatio(1, 1);
1274
bool overlong = false;
1275
bool newBeamedGroup = false;
1276
int notesInBeamedGroup = 0;
1278
while (s->isBeforeEndMarker(i)) {
1280
if ((*i)->getNotationAbsoluteTime() >= barEnd)
1283
// First test whether we're entering or leaving a group,
1284
// before we consider how to write the event itself (at least
1285
// for pre-2.0 Lilypond output)
1287
if ((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType) ||
1288
(*i)->isa(Clef::EventType) || (*i)->isa(Rosegarden::Key::EventType)) {
1290
long newGroupId = -1;
1292
<Int>(BEAMED_GROUP_ID, newGroupId)) {
1294
if (newGroupId != groupId) {
1295
// entering a new beamed group
1297
if (groupId != -1) {
1298
// and leaving an old one
1299
if (groupType == GROUP_TYPE_TUPLED ||
1300
groupType == GROUP_TYPE_GRACE) {
1301
if (m_exportBeams && notesInBeamedGroup > 0)
1304
} else if (groupType == GROUP_TYPE_BEAMED) {
1305
if (m_exportBeams && notesInBeamedGroup > 0)
1310
groupId = newGroupId;
1313
<String>(BEAMED_GROUP_TYPE, groupType);
1315
if (groupType == GROUP_TYPE_TUPLED) {
1317
long denominator = 0;
1319
<Int>(BEAMED_GROUP_TUPLED_COUNT, numerator);
1321
<Int>(BEAMED_GROUP_UNTUPLED_COUNT, denominator);
1322
if (numerator == 0 || denominator == 0) {
1323
std::cerr << "WARNING: LilypondExporter::writeBar: "
1324
<< "tupled event without tupled/untupled counts"
1329
str << "\\times " << numerator << "/" << denominator << " { ";
1330
tupletRatio = std::pair<int, int>(numerator, denominator);
1331
// Require explicit beamed groups,
1332
// fixes bug #1683205.
1333
// HJJ: Why line below was originally present?
1334
// newBeamedGroup = true;
1335
notesInBeamedGroup = 0;
1337
} else if (groupType == GROUP_TYPE_BEAMED) {
1338
newBeamedGroup = true;
1339
notesInBeamedGroup = 0;
1340
// there can currently be only on group type, reset tuplet ratio
1341
tupletRatio = std::pair<int, int>(1,1);
1342
} else if (groupType == GROUP_TYPE_GRACE) {
1343
str << "\\grace { ";
1344
// Require explicit beamed group,
1345
// fixes bug #1683205.
1346
// HJJ: Why line below was originally present?
1347
// newBeamedGroup = true;
1348
notesInBeamedGroup = 0;
1349
// there can currently be only on group type, reset tuplet ratio
1350
tupletRatio = std::pair<int, int>(1,1);
1357
if (groupId != -1) {
1358
// leaving a beamed group
1359
if (groupType == GROUP_TYPE_TUPLED ||
1360
groupType == GROUP_TYPE_GRACE) {
1361
if (m_exportBeams && notesInBeamedGroup > 0)
1364
tupletRatio = std::pair<int, int>(1, 1);
1365
} else if (groupType == GROUP_TYPE_BEAMED) {
1366
if (m_exportBeams && notesInBeamedGroup > 0)
1375
timeT soundingDuration = -1;
1376
timeT duration = calculateDuration
1377
(s, i, barEnd, soundingDuration, tupletRatio, overlong);
1379
if (soundingDuration == -1) {
1380
soundingDuration = duration * tupletRatio.first / tupletRatio.second;
1383
if ((*i)->has(SKIP_PROPERTY)) {
1384
(*i)->unset(SKIP_PROPERTY);
1389
if ((*i)->isa(Note::EventType)) {
1391
Chord chord(*s, i, m_composition->getNotationQuantizer());
1392
Event *e = *chord.getInitialNote();
1393
bool tiedForward = false;
1395
// Examine the following event, and truncate our duration
1396
// if we overlap it.
1398
if (e->has(DISPLACED_X)) {
1399
double xDisplacement = 1 + ((double) e->get
1400
<Int>(DISPLACED_X)) / 1000;
1401
str << "\\once \\override NoteColumn #'force-hshift = #"
1402
<< xDisplacement << " ";
1405
bool hiddenNote = false;
1406
if (e->has(INVISIBLE)) {
1408
<Bool>(INVISIBLE)) {
1414
str << "\\hideNotes ";
1417
if (e->has(NotationProperties::STEM_UP)) {
1419
<Bool>(NotationProperties::STEM_UP)) {
1420
if (lastStem != 1) {
1426
if (lastStem != -1) {
1427
str << "\\stemDown ";
1432
if (lastStem != 0) {
1433
str << "\\stemNeutral ";
1438
if (chord.size() > 1)
1441
Segment::iterator stylei = s->end();
1443
for (i = chord.getInitialElement(); s->isBeforeEndMarker(i); ++i) {
1445
if ((*i)->isa(Text::EventType)) {
1446
if (!handleDirective(*i, lilyText, nextBarIsAlt1, nextBarIsAlt2,
1447
nextBarIsDouble, nextBarIsEnd, nextBarIsDot)) {
1449
handleText(*i, lilyText);
1452
} else if ((*i)->isa(Note::EventType)) {
1454
if (m_languageLevel >= LILYPOND_VERSION_2_8) {
1455
// one \tweak per each chord note
1456
if (chord.size() > 1)
1457
writeStyle(*i, prevStyle, col, str, true);
1459
writeStyle(*i, prevStyle, col, str, false);
1461
// only one override per chord, and that outside the <>
1464
writePitch(*i, key, str);
1466
bool noteHasCautionaryAccidental = false;
1468
<Bool>(NotationProperties::USE_CAUTIONARY_ACCIDENTAL, noteHasCautionaryAccidental);
1469
if (noteHasCautionaryAccidental)
1472
bool noteTiedForward = false;
1474
<Bool>(TIED_FORWARD, noteTiedForward);
1475
if (noteTiedForward)
1479
} else if ((*i)->isa(Indication::EventType)) {
1480
eventsToStart.insert(*i);
1481
eventsInProgress.insert(*i);
1484
if (i == chord.getFinalElement())
1488
if (chord.size() > 1)
1491
if (duration != prevDuration) {
1492
durationRatio = writeDuration(duration, str);
1494
prevDuration = duration;
1497
if (m_languageLevel == LILYPOND_VERSION_2_6) {
1498
// only one override per chord, and that outside the <>
1499
if (stylei != s->end()) {
1500
writeStyle(*stylei, prevStyle, col, str, false);
1505
if (lilyText != "") {
1509
writeSlashes(*i, str);
1511
writtenDuration += soundingDuration;
1512
std::pair<int,int> ratio = fractionProduct(durationRatio,tupletRatio);
1513
durationRatioSum = fractionSum(durationRatioSum, ratio);
1514
// 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
1516
std::vector<Mark> marks(chord.getMarksForChord());
1517
// problem here: stem direction unavailable (it's a view-local property)
1520
<Bool>(NotationProperties::STEM_UP, stemUp);
1521
for (std::vector<Mark>::iterator j = marks.begin(); j != marks.end(); ++j) {
1522
str << composeLilyMark(*j, stemUp);
1524
if (marks.size() > 0)
1527
handleEndingEvents(eventsInProgress, i, str);
1528
handleStartingEvents(eventsToStart, str);
1534
str << "\\unHideNotes ";
1537
if (newBeamedGroup) {
1538
// This is a workaround for bug #1705430:
1539
// Beaming groups erroneous after merging notes
1540
// There will be fewer "e4. [ ]" errors in LilyPond-compiling.
1541
// HJJ: This should be fixed in notation engine,
1542
// after which the workaround below should be removed.
1543
Note note(Note::getNearestNote(duration, MAX_DOTS));
1545
switch (note.getNoteType()) {
1546
case Note::SixtyFourthNote:
1547
case Note::ThirtySecondNote:
1548
case Note::SixteenthNote:
1549
case Note::EighthNote:
1550
notesInBeamedGroup++;
1554
// // Old version before the workaround for bug #1705430:
1555
// if (newBeamedGroup)
1556
// notesInBeamedGroup++;
1557
} else if ((*i)->isa(Note::EventRestType)) {
1559
bool hiddenRest = false;
1560
if ((*i)->has(INVISIBLE)) {
1562
<Bool>(INVISIBLE)) {
1567
if (MultiMeasureRestCount == 0) {
1570
} else if (duration == timeSignature.getBarDuration()) {
1571
// Look ahead the segment in order to detect
1572
// the number of measures in the multi measure rest.
1573
Segment::iterator mm_i = i;
1574
while (s->isBeforeEndMarker(++mm_i)) {
1575
if ((*mm_i)->isa(Note::EventRestType) &&
1576
(*mm_i)->getNotationDuration() == (*i)->getNotationDuration() &&
1577
timeSignature == m_composition->getTimeSignatureAt((*mm_i)->getNotationAbsoluteTime())) {
1578
MultiMeasureRestCount++;
1588
if (duration != prevDuration) {
1589
durationRatio = writeDuration(duration, str);
1590
if (MultiMeasureRestCount > 0) {
1591
str << "*" << (1 + MultiMeasureRestCount);
1593
prevDuration = duration;
1596
if (lilyText != "") {
1603
handleEndingEvents(eventsInProgress, i, str);
1604
handleStartingEvents(eventsToStart, str);
1607
notesInBeamedGroup++;
1609
MultiMeasureRestCount--;
1611
writtenDuration += soundingDuration;
1612
std::pair<int,int> ratio = fractionProduct(durationRatio,tupletRatio);
1613
durationRatioSum = fractionSum(durationRatioSum, ratio);
1614
// 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
1615
} else if ((*i)->isa(Clef::EventType)) {
1618
// Incomplete: Set which note the clef should center on (DMM - why?)
1619
// To allow octavation of the clef, enclose the clefname always with quotes.
1624
if (clef.getClefType() == Clef::Treble) {
1626
} else if (clef.getClefType() == Clef::French) {
1628
} else if (clef.getClefType() == Clef::Soprano) {
1630
} else if (clef.getClefType() == Clef::Mezzosoprano) {
1631
str << "mezzosoprano";
1632
} else if (clef.getClefType() == Clef::Alto) {
1634
} else if (clef.getClefType() == Clef::Tenor) {
1636
} else if (clef.getClefType() == Clef::Baritone) {
1638
} else if (clef.getClefType() == Clef::Varbaritone) {
1639
str << "varbaritone";
1640
} else if (clef.getClefType() == Clef::Bass) {
1642
} else if (clef.getClefType() == Clef::Subbass) {
1646
// Transpose the clef one or two octaves up or down, if specified.
1647
int octaveOffset = clef.getOctaveOffset();
1648
if (octaveOffset > 0) {
1649
str << "^" << 8*octaveOffset;
1650
} else if (octaveOffset < 0) {
1651
str << "_" << -8*octaveOffset;
1654
str << "\"" << std::endl << indent(col);
1656
} catch (Exception e) {
1657
std::cerr << "Bad clef: " << e.getMessage() << std::endl;
1660
} else if ((*i)->isa(Rosegarden::Key::EventType)) {
1664
key = Rosegarden::Key(**i);
1665
Accidental accidental = Accidentals::NoAccidental;
1667
std::cout << "key tonic pitch: " << key.getTonicPitch() << std::endl; //REMOVE
1668
str << convertPitchToLilyNote(key.getTonicPitch(), accidental, key);
1670
if (key.isMinor()) {
1675
str << std::endl << indent(col);
1677
} catch (Exception e) {
1678
std::cerr << "Bad key: " << e.getMessage() << std::endl;
1681
} else if ((*i)->isa(Text::EventType)) {
1683
if (!handleDirective(*i, lilyText, nextBarIsAlt1, nextBarIsAlt2,
1684
nextBarIsDouble, nextBarIsEnd, nextBarIsDot)) {
1685
handleText(*i, lilyText);
1688
} else if ((*i)->isa(Guitar::Chord::EventType)) {
1691
Guitar::Chord chord = Guitar::Chord(**i);
1692
const Guitar::Fingering& fingering = chord.getFingering();
1694
int firstFret = fingering.getStartFret();
1695
int barreStart = 0, barreEnd = 0, barreFret = 0;
1698
// Check if there is a barre.
1700
if (fingering.hasBarre()) {
1701
Guitar::Fingering::Barre barre = fingering.getBarre();
1702
barreStart = barre.start;
1703
barreEnd = barre.end;
1704
barreFret = barre.fret;
1707
if (barreStart == 0) {
1708
str << " s4*0^\\markup \\fret-diagram #\"";
1710
str << " s4*0^\\markup \\override #'(barre-type . straight) \\fret-diagram #\"";
1713
// Check each string individually.
1714
// Note: LilyPond numbers strings differently.
1716
for (int stringNum = 6; stringNum >= 1; --stringNum) {
1717
if (barreStart == stringNum) {
1718
str << "c:" << barreStart << "-" << barreEnd << "-" << barreFret << ";";
1721
if (fingering.getStringStatus( 6-stringNum ) == Guitar::Fingering::MUTED) {
1722
str << stringNum << "-x;";
1723
} else if (fingering.getStringStatus( 6-stringNum ) == Guitar::Fingering::OPEN) {
1724
str << stringNum << "-o;";
1726
int stringStatus = fingering.getStringStatus(6-stringNum);
1727
if ((stringNum <= barreStart) && (stringNum >= barreEnd)) {
1728
str << stringNum << "-" << barreFret << ";";
1730
str << stringNum << "-" << stringStatus << ";";
1736
} catch (Exception e) { // GuitarChord ctor failed
1737
RG_DEBUG << "Bad GuitarChord event in Lilypond export" << endl;
1741
// LilyPond 2.0 introduces required postfix syntax for beaming
1742
if (m_exportBeams && newBeamedGroup && notesInBeamedGroup > 0) {
1744
newBeamedGroup = false;
1747
if ((*i)->isa(Indication::EventType)) {
1748
eventsToStart.insert(*i);
1749
eventsInProgress.insert(*i);
1755
if (groupId != -1) {
1756
if (groupType == GROUP_TYPE_TUPLED ||
1757
groupType == GROUP_TYPE_GRACE) {
1758
if (m_exportBeams && notesInBeamedGroup > 0)
1761
tupletRatio = std::pair<int, int>(1, 1);
1762
} else if (groupType == GROUP_TYPE_BEAMED) {
1763
if (m_exportBeams && notesInBeamedGroup > 0)
1768
if (lastStem != 0) {
1769
str << "\\stemNeutral ";
1773
str << std::endl << indent(col) <<
1774
qstrtostr(QString("% %1").
1775
arg(i18n("warning: overlong bar truncated here")));
1778
if (fractionSmaller(durationRatioSum, barDurationRatio)) {
1779
str << std::endl << indent(col) <<
1780
qstrtostr(QString("% %1").
1781
arg(i18n("warning: bar too short, padding with rests")));
1782
str << std::endl << indent(col) <<
1783
qstrtostr(QString("% %1/%2 < %3/%4").
1784
arg(durationRatioSum.first).
1785
arg(durationRatioSum.second).
1786
arg(barDurationRatio.first).
1787
arg(barDurationRatio.second))
1788
<< std::endl << indent(col);
1789
durationRatio = writeSkip(timeSignature, writtenDuration,
1790
(barEnd - barStart) - writtenDuration, true, str);
1791
durationRatioSum = fractionSum(durationRatioSum,durationRatio);
1794
// Export bar and bar checks.
1796
if (nextBarIsDouble) {
1797
str << "\\bar \"||\" ";
1798
nextBarIsDouble = false;
1799
} else if (nextBarIsEnd) {
1800
str << "\\bar \"|.\" ";
1801
nextBarIsEnd = false;
1802
} else if (nextBarIsDot) {
1803
str << "\\bar \":\" ";
1804
nextBarIsDot = false;
1805
} else if (MultiMeasureRestCount == 0) {
1811
LilypondExporter::writeSkip(const TimeSignature &timeSig,
1818
timeSig.getDurationListForInterval(dlist, duration, offset);
1819
std::pair<int,int> durationRatioSum(0,1);
1820
std::pair<int,int> durationRatio(0,1);
1822
int t = 0, count = 0;
1824
for (DurationList::iterator i = dlist.begin(); ; ++i) {
1826
if (i == dlist.end() || (*i) != t) {
1832
else if (t == timeSig.getBarDuration())
1837
durationRatio = writeDuration(t, str);
1840
str << "*" << count;
1841
durationRatio = fractionProduct(durationRatio,count);
1845
durationRatioSum = fractionSum(durationRatioSum,durationRatio);
1848
if (i != dlist.end()) {
1857
if (i == dlist.end())
1860
return durationRatioSum;
1864
LilypondExporter::handleDirective(const Event *textEvent,
1865
std::string &lilyText,
1866
bool &nextBarIsAlt1, bool &nextBarIsAlt2,
1867
bool &nextBarIsDouble, bool &nextBarIsEnd, bool &nextBarIsDot)
1869
Text text(*textEvent);
1871
if (text.getTextType() == Text::LilypondDirective) {
1872
std::string directive = text.getText();
1873
if (directive == Text::Segno) {
1874
lilyText += "^\\markup { \\musicglyph #\"scripts.segno\" } ";
1875
} else if (directive == Text::Coda) {
1876
lilyText += "^\\markup { \\musicglyph #\"scripts.coda\" } ";
1877
} else if (directive == Text::Alternate1) {
1878
nextBarIsAlt1 = true;
1879
} else if (directive == Text::Alternate2) {
1880
nextBarIsAlt1 = false;
1881
nextBarIsAlt2 = true;
1882
} else if (directive == Text::BarDouble) {
1883
nextBarIsDouble = true;
1884
} else if (directive == Text::BarEnd) {
1885
nextBarIsEnd = true;
1886
} else if (directive == Text::BarDot) {
1887
nextBarIsDot = true;
1889
// pass along less special directives for handling as plain text,
1890
// so they can be attached to chords and whatlike without
1901
LilypondExporter::handleText(const Event *textEvent,
1902
std::string &lilyText)
1906
Text text(*textEvent);
1907
std::string s = text.getText();
1909
// only protect illegal chars if this is Text, rather than
1910
// LilypondDirective
1911
if ((*textEvent).isa(Text::EventType))
1912
s = protectIllegalChars(s);
1914
if (text.getTextType() == Text::Tempo) {
1916
// print above staff, bold, large
1917
lilyText += "^\\markup { \\bold \\large \"" + s + "\" } ";
1919
} else if (text.getTextType() == Text::LocalTempo ||
1920
text.getTextType() == Text::Chord) {
1922
// print above staff, bold, small
1923
lilyText += "^\\markup { \\bold \"" + s + "\" } ";
1925
} else if (text.getTextType() == Text::Dynamic) {
1927
// pass through only supported types
1928
if (s == "ppppp" || s == "pppp" || s == "ppp" ||
1929
s == "pp" || s == "p" || s == "mp" ||
1930
s == "mf" || s == "f" || s == "ff" ||
1931
s == "fff" || s == "ffff" || s == "rfz" ||
1934
lilyText += "-\\" + s + " ";
1937
std::cerr << "LilypondExporter::write() - illegal Lilypond dynamic: "
1941
} else if (text.getTextType() == Text::Direction) {
1943
// print above staff, large
1944
lilyText += "^\\markup { \\large \"" + s + "\" } ";
1946
} else if (text.getTextType() == Text::LocalDirection) {
1948
// print below staff, bold italics, small
1949
lilyText += "_\\markup { \\bold \\italic \"" + s + "\" } ";
1951
// LilyPond directives that don't require special handling across
1952
// barlines are handled here along with ordinary text types. These
1953
// can be injected wherever they happen to occur, and should get
1954
// attached to the right bits in due course without extra effort.
1956
} else if (text.getText() == Text::Gliss) {
1957
lilyText += "\\glissando ";
1958
} else if (text.getText() == Text::Arpeggio) {
1959
lilyText += "\\arpeggio ";
1960
} else if (text.getText() == Text::Tiny) {
1961
lilyText += "\\tiny ";
1962
} else if (text.getText() == Text::Small) {
1963
lilyText += "\\small ";
1964
} else if (text.getText() == Text::NormalSize) {
1965
lilyText += "\\normalsize ";
1968
<String>(Text::TextTypePropertyName, s);
1969
std::cerr << "LilypondExporter::write() - unhandled text type: "
1972
} catch (Exception e) {
1973
std::cerr << "Bad text: " << e.getMessage() << std::endl;
1978
LilypondExporter::writePitch(const Event *note,
1979
const Rosegarden::Key &key,
1982
// Note pitch (need name as well as octave)
1983
// It is also possible to have "relative" pitches,
1984
// but for simplicity we always use absolute pitch
1985
// 60 is middle C, one unit is a half-step
1989
<Int>(PITCH, pitch);
1991
Accidental accidental = Accidentals::NoAccidental;
1993
<String>(ACCIDENTAL, accidental);
1995
// format of Lilypond note is:
1996
// name + octave + (duration) + text markup
1998
// calculate note name and write note
1999
std::string lilyNote;
2001
lilyNote = convertPitchToLilyNote(pitch, accidental, key);
2005
// generate and write octave marks
2006
std::string octaveMarks = "";
2007
int octave = (int)(pitch / 12);
2009
// tweak the octave break for B# / Cb
2010
if ((lilyNote == "bisis") || (lilyNote == "bis")) {
2012
} else if ((lilyNote == "ceses") || (lilyNote == "ces")) {
2017
for (; octave < 4; octave++)
2020
for (; octave > 4; octave--)
2021
octaveMarks += "\'";
2028
LilypondExporter::writeStyle(const Event *note, std::string &prevStyle,
2029
int col, std::ofstream &str, bool isInChord)
2031
// some hard-coded styles in order to provide rudimentary style export support
2032
// note that this is technically bad practice, as style names are not supposed
2033
// to be fixed but deduced from the style files actually present on the system
2034
const std::string styleMensural = "Mensural";
2035
const std::string styleTriangle = "Triangle";
2036
const std::string styleCross = "Cross";
2037
const std::string styleClassical = "Classical";
2039
// handle various note styles before opening any chord
2041
std::string style = "";
2043
<String>(NotationProperties::NOTE_STYLE, style);
2045
if (style != prevStyle) {
2047
if (style == styleClassical && prevStyle == "")
2053
if (style == styleMensural) {
2055
} else if (style == styleTriangle) {
2057
} else if (style == styleCross) {
2060
style = "default"; // failsafe default or explicit
2064
str << std::endl << indent(col) << "\\override Voice.NoteHead #'style = #'" << style << std::endl << indent(col);
2066
str << "\\tweak #'style #'" << style << " ";
2072
LilypondExporter::writeDuration(timeT duration,
2075
Note note(Note::getNearestNote(duration, MAX_DOTS));
2076
std::pair<int,int> durationRatio(0,1);
2078
switch (note.getNoteType()) {
2080
case Note::SixtyFourthNote:
2081
str << "64"; durationRatio = std::pair<int,int>(1,64);
2084
case Note::ThirtySecondNote:
2085
str << "32"; durationRatio = std::pair<int,int>(1,32);
2088
case Note::SixteenthNote:
2089
str << "16"; durationRatio = std::pair<int,int>(1,16);
2092
case Note::EighthNote:
2093
str << "8"; durationRatio = std::pair<int,int>(1,8);
2096
case Note::QuarterNote:
2097
str << "4"; durationRatio = std::pair<int,int>(1,4);
2100
case Note::HalfNote:
2101
str << "2"; durationRatio = std::pair<int,int>(1,2);
2104
case Note::WholeNote:
2105
str << "1"; durationRatio = std::pair<int,int>(1,1);
2108
case Note::DoubleWholeNote:
2109
str << "\\breve"; durationRatio = std::pair<int,int>(2,1);
2113
for (int numDots = 0; numDots < note.getDots(); numDots++) {
2116
durationRatio = fractionProduct(durationRatio,
2117
std::pair<int,int>((1<<(note.getDots()+1))-1,1<<note.getDots()));
2118
return durationRatio;
2122
LilypondExporter::writeSlashes(const Event *note, std::ofstream &str)
2124
// write slashes after text
2125
// / = 8 // = 16 /// = 32, etc.
2128
<Int>(NotationProperties::SLASHES, slashes);
2132
for (int c = 1; c <= slashes; c++) {