summaryrefslogtreecommitdiffstats
path: root/src/document/io/LilyPondExporter.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/document/io/LilyPondExporter.cpp
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/document/io/LilyPondExporter.cpp')
-rw-r--r--src/document/io/LilyPondExporter.cpp2419
1 files changed, 2419 insertions, 0 deletions
diff --git a/src/document/io/LilyPondExporter.cpp b/src/document/io/LilyPondExporter.cpp
new file mode 100644
index 0000000..68731f8
--- /dev/null
+++ b/src/document/io/LilyPondExporter.cpp
@@ -0,0 +1,2419 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file is Copyright 2002
+ Hans Kieserman <[email protected]>
+ with heavy lifting from csoundio as it was on 13/5/2002.
+
+ Numerous additions and bug fixes by
+ Michael McIntyre <[email protected]>
+
+ Some restructuring by Chris Cannam.
+
+ Massive brain surgery, fixes, improvements, and additions by
+ Heikki Junes
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "LilyPondExporter.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/BaseProperties.h"
+#include "base/Composition.h"
+#include "base/Configuration.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "base/Segment.h"
+#include "base/SegmentNotationHelper.h"
+#include "base/Sets.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "base/NotationQuantizer.h"
+#include "base/Marker.h"
+#include "base/StaffExportTypes.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/application/RosegardenGUIView.h"
+#include "gui/editors/notation/NotationProperties.h"
+#include "gui/editors/notation/NotationView.h"
+#include "gui/editors/guitar/Chord.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/widgets/CurrentProgressDialog.h"
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <qfileinfo.h>
+#include <qobject.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qtextcodec.h>
+#include <kapplication.h>
+#include <sstream>
+#include <algorithm>
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+const PropertyName LilyPondExporter::SKIP_PROPERTY
+ = "LilyPondExportSkipThisEvent";
+
+LilyPondExporter::LilyPondExporter(RosegardenGUIApp *parent,
+ RosegardenGUIDoc *doc,
+ std::string fileName) :
+ ProgressReporter((QObject *)parent, "lilypondExporter"),
+ m_doc(doc),
+ m_fileName(fileName),
+ m_lastClefFound(Clef::Treble)
+{
+ m_composition = &m_doc->getComposition();
+ m_studio = &m_doc->getStudio();
+ m_view = ((RosegardenGUIApp *)parent)->getView();
+ m_notationView = NULL;
+
+ readConfigVariables();
+}
+
+LilyPondExporter::LilyPondExporter(NotationView *parent,
+ RosegardenGUIDoc *doc,
+ std::string fileName) :
+ ProgressReporter((QObject *)parent, "lilypondExporter"),
+ m_doc(doc),
+ m_fileName(fileName),
+ m_lastClefFound(Clef::Treble)
+
+{
+ m_composition = &m_doc->getComposition();
+ m_studio = &m_doc->getStudio();
+ m_view = NULL;
+ m_notationView = ((NotationView *)parent);
+
+ readConfigVariables();
+}
+
+void
+LilyPondExporter::readConfigVariables(void)
+{
+ // grab config info
+ KConfig *cfg = kapp->config();
+ cfg->setGroup(NotationViewConfigGroup);
+
+ m_paperSize = cfg->readUnsignedNumEntry("lilypapersize", PAPER_A4);
+ m_paperLandscape = cfg->readBoolEntry("lilypaperlandscape", false);
+ m_fontSize = cfg->readUnsignedNumEntry("lilyfontsize", FONT_20);
+ m_raggedBottom = cfg->readBoolEntry("lilyraggedbottom", false);
+ m_exportSelection = cfg->readUnsignedNumEntry("lilyexportselection", EXPORT_NONMUTED_TRACKS);
+ m_exportLyrics = cfg->readBoolEntry("lilyexportlyrics", true);
+ m_exportMidi = cfg->readBoolEntry("lilyexportmidi", false);
+ m_exportTempoMarks = cfg->readUnsignedNumEntry("lilyexporttempomarks", EXPORT_NONE_TEMPO_MARKS);
+ m_exportPointAndClick = cfg->readBoolEntry("lilyexportpointandclick", false);
+ m_exportBeams = cfg->readBoolEntry("lilyexportbeamings", false);
+ m_exportStaffMerge = cfg->readBoolEntry("lilyexportstaffmerge", false);
+ m_exportStaffGroup = cfg->readBoolEntry("lilyexportstaffbrackets", true);
+ m_lyricsHAlignment = cfg->readBoolEntry("lilylyricshalignment", LEFT_ALIGN);
+
+ m_languageLevel = cfg->readUnsignedNumEntry("lilylanguage", LILYPOND_VERSION_2_6);
+ m_exportMarkerMode = cfg->readUnsignedNumEntry("lilyexportmarkermode", EXPORT_NO_MARKERS );
+}
+
+LilyPondExporter::~LilyPondExporter()
+{
+ // nothing
+}
+
+void
+LilyPondExporter::handleStartingEvents(eventstartlist &eventsToStart,
+ std::ofstream &str)
+{
+ eventstartlist::iterator m = eventsToStart.begin();
+
+ while (m != eventsToStart.end()) {
+
+ try {
+ Indication i(**m);
+
+ if (i.getIndicationType() == Indication::Slur) {
+ if ((*m)->get
+ <Bool>(NotationProperties::SLUR_ABOVE))
+ str << "^( ";
+ else
+ str << "_( ";
+ } else if (i.getIndicationType() == Indication::PhrasingSlur) {
+ if ((*m)->get
+ <Bool>(NotationProperties::SLUR_ABOVE))
+ str << "^\\( ";
+ else
+ str << "_\\( ";
+ } else if (i.getIndicationType() == Indication::Crescendo) {
+ str << "\\< ";
+ } else if (i.getIndicationType() == Indication::Decrescendo) {
+ str << "\\> ";
+ }
+
+ } catch (Event::BadType) {
+ // Not an indication
+ } catch (Event::NoData e) {
+ std::cerr << "Bad indication: " << e.getMessage() << std::endl;
+ }
+
+ eventstartlist::iterator n(m);
+ ++n;
+ eventsToStart.erase(m);
+ m = n;
+ }
+}
+
+void
+LilyPondExporter::handleEndingEvents(eventendlist &eventsInProgress,
+ const Segment::iterator &j,
+ std::ofstream &str)
+{
+ eventendlist::iterator k = eventsInProgress.begin();
+
+ while (k != eventsInProgress.end()) {
+
+ eventendlist::iterator l(k);
+ ++l;
+
+ // Handle and remove all the relevant events in progress
+ // This assumes all deferred events are indications
+
+ try {
+ Indication i(**k);
+
+ timeT indicationEnd =
+ (*k)->getNotationAbsoluteTime() + i.getIndicationDuration();
+ timeT eventEnd =
+ (*j)->getNotationAbsoluteTime() + (*j)->getNotationDuration();
+
+ if (indicationEnd < eventEnd ||
+ ((i.getIndicationType() == Indication::Slur ||
+ i.getIndicationType() == Indication::PhrasingSlur) &&
+ indicationEnd == eventEnd)) {
+
+ if (i.getIndicationType() == Indication::Slur) {
+ str << ") ";
+ } else if (i.getIndicationType() == Indication::PhrasingSlur) {
+ str << "\\) ";
+ } else if (i.getIndicationType() == Indication::Crescendo ||
+ i.getIndicationType() == Indication::Decrescendo) {
+ str << "\\! ";
+ }
+
+ eventsInProgress.erase(k);
+ }
+
+ } catch (Event::BadType) {
+ // not an indication
+
+ } catch (Event::NoData e) {
+ std::cerr << "Bad indication: " << e.getMessage() << std::endl;
+ }
+
+ k = l;
+ }
+}
+
+std::string
+LilyPondExporter::convertPitchToLilyNote(int pitch, Accidental accidental,
+ const Rosegarden::Key &key)
+{
+ Pitch p(pitch, accidental);
+ std::string lilyNote = "";
+
+ lilyNote += (char)tolower(p.getNoteName(key));
+ // std::cout << "lilyNote: " << lilyNote << std::endl;
+ Accidental acc = p.getAccidental(key);
+ if (acc == Accidentals::DoubleFlat)
+ lilyNote += "eses";
+ else if (acc == Accidentals::Flat)
+ lilyNote += "es";
+ else if (acc == Accidentals::Sharp)
+ lilyNote += "is";
+ else if (acc == Accidentals::DoubleSharp)
+ lilyNote += "isis";
+
+ return lilyNote;
+}
+
+std::string
+LilyPondExporter::composeLilyMark(std::string eventMark, bool stemUp)
+{
+
+ std::string inStr = "", outStr = "";
+ std::string prefix = (stemUp) ? "_" : "^";
+
+ // shoot text mark straight through unless it's sf or rf
+ if (Marks::isTextMark(eventMark)) {
+ inStr = protectIllegalChars(Marks::getTextFromMark(eventMark));
+
+ if (inStr == "sf") {
+ inStr = "\\sf";
+ } else if (inStr == "rf") {
+ inStr = "\\rfz";
+ } else {
+ inStr = "\\markup { \\italic " + inStr + " } ";
+ }
+
+ outStr = prefix + inStr;
+
+ } else if (Marks::isFingeringMark(eventMark)) {
+
+ // fingering marks: use markup syntax only for non-trivial fingerings
+
+ inStr = protectIllegalChars(Marks::getFingeringFromMark(eventMark));
+
+ if (inStr != "0" && inStr != "1" && inStr != "2" && inStr != "3" && inStr != "4" && inStr != "5" && inStr != "+" ) {
+ inStr = "\\markup { \\finger \"" + inStr + "\" } ";
+ }
+
+ outStr = prefix + inStr;
+
+ } else {
+ outStr = "-";
+
+ // use full \accent format for everything, even though some shortcuts
+ // exist, for the sake of consistency
+ if (eventMark == Marks::Accent) {
+ outStr += "\\accent";
+ } else if (eventMark == Marks::Tenuto) {
+ outStr += "\\tenuto";
+ } else if (eventMark == Marks::Staccato) {
+ outStr += "\\staccato";
+ } else if (eventMark == Marks::Staccatissimo) {
+ outStr += "\\staccatissimo";
+ } else if (eventMark == Marks::Marcato) {
+ outStr += "\\marcato";
+ } else if (eventMark == Marks::Trill) {
+ outStr += "\\trill";
+ } else if (eventMark == Marks::LongTrill) {
+ // span trill up to the next note:
+ // tweak the beginning of the next note using an invisible rest having zero length
+ outStr += "\\startTrillSpan s4*0 \\stopTrillSpan";
+ } else if (eventMark == Marks::Turn) {
+ outStr += "\\turn";
+ } else if (eventMark == Marks::Pause) {
+ outStr += "\\fermata";
+ } else if (eventMark == Marks::UpBow) {
+ outStr += "\\upbow";
+ } else if (eventMark == Marks::DownBow) {
+ outStr += "\\downbow";
+ } else {
+ outStr = "";
+ std::cerr << "LilyPondExporter::composeLilyMark() - unhandled mark: "
+ << eventMark << std::endl;
+ }
+ }
+
+ return outStr;
+}
+
+std::string
+LilyPondExporter::indent(const int &column)
+{
+ std::string outStr = "";
+ for (int c = 1; c <= column; c++) {
+ outStr += " ";
+ }
+ return outStr;
+}
+
+std::string
+LilyPondExporter::protectIllegalChars(std::string inStr)
+{
+
+ QString tmpStr = strtoqstr(inStr);
+
+ tmpStr.replace(QRegExp("&"), "\\&");
+ tmpStr.replace(QRegExp("\\^"), "\\^");
+ tmpStr.replace(QRegExp("%"), "\\%");
+ tmpStr.replace(QRegExp("<"), "\\<");
+ tmpStr.replace(QRegExp(">"), "\\>");
+ tmpStr.replace(QRegExp("\\["), "");
+ tmpStr.replace(QRegExp("\\]"), "");
+ tmpStr.replace(QRegExp("\\{"), "");
+ tmpStr.replace(QRegExp("\\}"), "");
+
+ //
+ // LilyPond uses utf8 encoding.
+ //
+ return tmpStr.utf8().data();
+}
+
+struct MarkerComp {
+ // Sort Markers by time
+ // Perhaps this should be made generic with a template?
+ bool operator()( Marker *a, Marker *b ) {
+ return a->getTime() < b->getTime();
+ }
+};
+
+bool
+LilyPondExporter::write()
+{
+ QString tmpName = strtoqstr(m_fileName);
+
+ // dmm - modified to act upon the filename itself, rather than the whole
+ // path; fixes bug #855349
+
+ // split name into parts:
+ QFileInfo nfo(tmpName);
+ QString dirName = nfo.dirPath();
+ QString baseName = nfo.fileName();
+
+ // sed LilyPond-choking chars out of the filename proper
+ bool illegalFilename = (baseName.contains(' ') || baseName.contains("\\"));
+ baseName.replace(QRegExp(" "), "");
+ baseName.replace(QRegExp("\\\\"), "");
+ baseName.replace(QRegExp("'"), "");
+ baseName.replace(QRegExp("\""), "");
+
+ // cat back together
+ tmpName = dirName + '/' + baseName;
+
+ if (illegalFilename) {
+ CurrentProgressDialog::freeze();
+ int reply = KMessageBox::warningContinueCancel(
+ 0, i18n("LilyPond does not allow spaces or backslashes in filenames.\n\n"
+ "Would you like to use\n\n %1\n\n instead?").arg(baseName));
+ if (reply != KMessageBox::Continue)
+ return false;
+ }
+
+ std::ofstream str(qstrtostr(tmpName).c_str(), std::ios::out);
+ if (!str) {
+ std::cerr << "LilyPondExporter::write() - can't write file " << tmpName << std::endl;
+ return false;
+ }
+
+ str << "% This LilyPond file was generated by Rosegarden " << protectIllegalChars(VERSION) << std::endl;
+
+ switch (m_languageLevel) {
+
+ case LILYPOND_VERSION_2_6:
+ str << "\\version \"2.6.0\"" << std::endl;
+ break;
+
+ case LILYPOND_VERSION_2_8:
+ str << "\\version \"2.8.0\"" << std::endl;
+ break;
+
+ case LILYPOND_VERSION_2_10:
+ str << "\\version \"2.10.0\"" << std::endl;
+ break;
+
+ case LILYPOND_VERSION_2_12:
+ str << "\\version \"2.12.0\"" << std::endl;
+ break;
+
+ default:
+ // force the default version if there was an error
+ std::cerr << "ERROR: Unknown language level " << m_languageLevel
+ << ", using \\version \"2.6.0\" instead" << std::endl;
+ str << "\\version \"2.6.0\"" << std::endl;
+ m_languageLevel = LILYPOND_VERSION_2_6;
+ }
+
+ // enable "point and click" debugging via pdf to make finding the
+ // unfortunately inevitable errors easier
+ if (m_exportPointAndClick) {
+ str << "% point and click debugging is enabled" << std::endl;
+ } else {
+ str << "% point and click debugging is disabled" << std::endl;
+ str << "#(ly:set-option 'point-and-click #f)" << std::endl;
+ }
+
+ // LilyPond \header block
+
+ // set indention level to make future changes to horizontal layout less
+ // tedious, ++col to indent a new level, --col to de-indent
+ int col = 0;
+
+ // grab user headers from metadata
+ Configuration metadata = m_composition->getMetadata();
+ std::vector<std::string> propertyNames = metadata.getPropertyNames();
+
+ // open \header section if there's metadata to grab, and if the user
+ // wishes it
+ if (!propertyNames.empty()) {
+ str << "\\header {" << std::endl;
+ col++; // indent+
+
+ bool userTagline = false;
+
+ for (unsigned int index = 0; index < propertyNames.size(); ++index) {
+ std::string property = propertyNames [index];
+ if (property == headerDedication || property == headerTitle ||
+ property == headerSubtitle || property == headerSubsubtitle ||
+ property == headerPoet || property == headerComposer ||
+ property == headerMeter || property == headerOpus ||
+ property == headerArranger || property == headerInstrument ||
+ property == headerPiece || property == headerCopyright ||
+ property == headerTagline) {
+ std::string header = protectIllegalChars(metadata.get<String>(property));
+ if (header != "") {
+ str << indent(col) << property << " = \"" << header << "\"" << std::endl;
+ // let users override defaults, but allow for providing
+ // defaults if they don't:
+ if (property == headerTagline)
+ userTagline = true;
+ }
+ }
+ }
+
+ // default tagline
+ if (!userTagline) {
+ str << indent(col) << "tagline = \""
+ << "Created using Rosegarden " << protectIllegalChars(VERSION) << " and LilyPond"
+ << "\"" << std::endl;
+ }
+
+ // close \header
+ str << indent(--col) << "}" << std::endl;
+ }
+
+ // LilyPond \paper block (optional)
+ if (m_raggedBottom) {
+ str << indent(col) << "\\paper {" << std::endl;
+ str << indent(++col) << "ragged-bottom=##t" << std::endl;
+ str << indent(--col) << "}" << std::endl;
+ }
+
+ // LilyPond music data! Mapping:
+ // LilyPond Voice = Rosegarden Segment
+ // LilyPond Staff = Rosegarden Track
+ // (not the cleanest output but maybe the most reliable)
+
+ // paper/font sizes
+ int font;
+ switch (m_fontSize) {
+ case 0 :
+ font = 11;
+ break;
+ case 1 :
+ font = 13;
+ break;
+ case 2 :
+ font = 16;
+ break;
+ case 3 :
+ font = 19;
+ break;
+ case 4 :
+ font = 20;
+ break;
+ case 5 :
+ font = 23;
+ break;
+ case 6 :
+ font = 26;
+ break;
+ default :
+ font = 20; // if config problem
+ }
+
+ str << indent(col) << "#(set-global-staff-size " << font << ")" << std::endl;
+
+ // write user-specified paper type as default paper size
+ std::string paper = "";
+ switch (m_paperSize) {
+ case PAPER_A3 :
+ paper += "a3";
+ break;
+ case PAPER_A4 :
+ paper += "a4";
+ break;
+ case PAPER_A5 :
+ paper += "a5";
+ break;
+ case PAPER_A6 :
+ paper += "a6";
+ break;
+ case PAPER_LEGAL :
+ paper += "legal";
+ break;
+ case PAPER_LETTER :
+ paper += "letter";
+ break;
+ case PAPER_TABLOID :
+ paper += "tabloid";
+ break;
+ case PAPER_NONE :
+ paper = "";
+ break; // "do not specify"
+ }
+ if (paper != "") {
+ str << indent(col) << "#(set-default-paper-size \"" << paper << "\""
+ << (m_paperLandscape ? " 'landscape" : "") << ")"
+ << std::endl;
+ }
+
+ // Find out the printed length of the composition
+ Composition::iterator i = m_composition->begin();
+ if ((*i) == NULL) {
+ str << indent(col) << "\\score {" << std::endl;
+ str << indent(++col) << "% no segments found" << std::endl;
+ // bind staffs with or without staff group bracket
+ str << indent(col) // indent
+ << "<<" << " s4 " << ">>" << std::endl;
+ str << indent(col) << "\\layout { }" << std::endl;
+ str << indent(--col) << "}" << std::endl;
+ return true;
+ }
+ timeT compositionStartTime = (*i)->getStartTime();
+ timeT compositionEndTime = (*i)->getEndMarkerTime();
+ for (; i != m_composition->end(); ++i) {
+ if (compositionStartTime > (*i)->getStartTime() && (*i)->getTrack() >= 0) {
+ compositionStartTime = (*i)->getStartTime();
+ }
+ if (compositionEndTime < (*i)->getEndMarkerTime()) {
+ compositionEndTime = (*i)->getEndMarkerTime();
+ }
+ }
+
+ // define global context which is common for all staffs
+ str << indent(col++) << "global = { " << std::endl;
+ TimeSignature timeSignature = m_composition->
+ getTimeSignatureAt(m_composition->getStartMarker());
+ if (m_composition->getBarStart(m_composition->getBarNumber(compositionStartTime)) < compositionStartTime) {
+ str << indent(col) << "\\partial ";
+ // Arbitrary partial durations are handled by the following way:
+ // split the partial duration to 64th notes: instead of "4" write "64*16". (hjj)
+ Note partialNote = Note::getNearestNote(1, MAX_DOTS);
+ int partialDuration = m_composition->getBarStart(m_composition->getBarNumber(compositionStartTime) + 1) - compositionStartTime;
+ writeDuration(1, str);
+ str << "*" << ((int)(partialDuration / partialNote.getDuration()))
+ << std::endl;
+ }
+ int leftBar = 0;
+ int rightBar = leftBar;
+ do {
+ bool isNew = false;
+ m_composition->getTimeSignatureInBar(rightBar + 1, isNew);
+
+ if (isNew || (m_composition->getBarStart(rightBar + 1) >= compositionEndTime)) {
+ // - set initial time signature; further time signature changes
+ // are defined within the segments, because they may be hidden
+ str << indent(col) << (leftBar == 0 ? "" : "% ") << "\\time "
+ << timeSignature.getNumerator() << "/"
+ << timeSignature.getDenominator() << std::endl;
+ // - place skips upto the end of the composition;
+ // this justifies the printed staffs
+ str << indent(col);
+ timeT leftTime = m_composition->getBarStart(leftBar);
+ timeT rightTime = m_composition->getBarStart(rightBar + 1);
+ if (leftTime < compositionStartTime) {
+ leftTime = compositionStartTime;
+ }
+ writeSkip(timeSignature, leftTime, rightTime - leftTime, false, str);
+ str << " %% " << (leftBar + 1) << "-" << (rightBar + 1) << std::endl;
+
+ timeSignature = m_composition->getTimeSignatureInBar(rightBar + 1, isNew);
+ leftBar = rightBar + 1;
+ }
+ } while (m_composition->getBarStart(++rightBar) < compositionEndTime);
+ str << indent(--col) << "}" << std::endl;
+
+ // time signatures changes are in segments, reset initial value
+ timeSignature = m_composition->
+ getTimeSignatureAt(m_composition->getStartMarker());
+
+ // All the tempo changes are included in "globalTempo" context.
+ // This context contains only skip notes between the tempo changes.
+ // First tempo marking should still be include in \midi{ } block.
+ // If tempo marks are printed in future, they should probably be
+ // included in this context and the note duration in the tempo
+ // mark should be according to the time signature. (hjj)
+ int tempoCount = m_composition->getTempoChangeCount();
+
+ if (tempoCount > 0) {
+
+ timeT prevTempoChangeTime = m_composition->getStartMarker();
+ int tempo = int(Composition::getTempoQpm(m_composition->getTempoAtTime(prevTempoChangeTime)));
+ bool tempoMarksInvisible = false;
+
+ str << indent(col++) << "globalTempo = {" << std::endl;
+ if (m_exportTempoMarks == EXPORT_NONE_TEMPO_MARKS && tempoMarksInvisible == false) {
+ str << indent(col) << "\\override Score.MetronomeMark #'transparent = ##t" << std::endl;
+ tempoMarksInvisible = true;
+ }
+ str << indent(col) << "\\tempo 4 = " << tempo << " ";
+ int prevTempo = tempo;
+
+ for (int i = 0; i < tempoCount; ++i) {
+
+ std::pair<timeT, long> tempoChange =
+ m_composition->getTempoChange(i);
+
+ timeT tempoChangeTime = tempoChange.first;
+
+ tempo = int(Composition::getTempoQpm(tempoChange.second));
+
+ // First tempo change may be before the first segment.
+ // Do not apply it before the first segment appears.
+ if (tempoChangeTime < compositionStartTime) {
+ tempoChangeTime = compositionStartTime;
+ } else if (tempoChangeTime >= compositionEndTime) {
+ tempoChangeTime = compositionEndTime;
+ }
+ if (prevTempoChangeTime < compositionStartTime) {
+ prevTempoChangeTime = compositionStartTime;
+ } else if (prevTempoChangeTime >= compositionEndTime) {
+ prevTempoChangeTime = compositionEndTime;
+ }
+ writeSkip(m_composition->getTimeSignatureAt(tempoChangeTime),
+ tempoChangeTime, tempoChangeTime - prevTempoChangeTime, false, str);
+ // add new \tempo only if tempo was changed
+ if (tempo != prevTempo) {
+ if (m_exportTempoMarks == EXPORT_FIRST_TEMPO_MARK && tempoMarksInvisible == false) {
+ str << std::endl << indent(col) << "\\override Score.MetronomeMark #'transparent = ##t";
+ tempoMarksInvisible = true;
+ }
+ str << std::endl << indent(col) << "\\tempo 4 = " << tempo << " ";
+ }
+
+ prevTempo = tempo;
+ prevTempoChangeTime = tempoChangeTime;
+ if (prevTempoChangeTime == compositionEndTime)
+ break;
+ }
+ // First tempo change may be before the first segment.
+ // Do not apply it before the first segment appears.
+ if (prevTempoChangeTime < compositionStartTime) {
+ prevTempoChangeTime = compositionStartTime;
+ }
+ writeSkip(m_composition->getTimeSignatureAt(prevTempoChangeTime),
+ prevTempoChangeTime, compositionEndTime - prevTempoChangeTime, false, str);
+ str << std::endl;
+ str << indent(--col) << "}" << std::endl;
+ }
+ // Markers
+ // Skip until marker, make sure there's only one marker per measure
+ if ( m_exportMarkerMode != EXPORT_NO_MARKERS ) {
+ str << indent(col++) << "markers = {" << std::endl;
+ timeT prevMarkerTime = 0;
+
+ // Need the markers sorted by time
+ Composition::markercontainer markers( m_composition->getMarkers() ); // copy
+ std::sort( markers.begin(), markers.end(), MarkerComp() );
+ Composition::markerconstiterator i_marker = markers.begin();
+
+ while ( i_marker != markers.end() ) {
+ timeT markerTime = m_composition->getBarStartForTime((*i_marker)->getTime());
+ RG_DEBUG << "Marker: " << (*i_marker)->getTime() << " previous: " << prevMarkerTime << endl;
+ // how to cope with time signature changes?
+ if ( markerTime > prevMarkerTime ) {
+ str << indent(col);
+ writeSkip(m_composition->getTimeSignatureAt(markerTime),
+ markerTime, markerTime - prevMarkerTime, false, str);
+ str << "\\mark ";
+ switch (m_exportMarkerMode) {
+ case EXPORT_DEFAULT_MARKERS:
+ // Use the marker name for text
+ str << "\\default %% " << (*i_marker)->getName() << std::endl;
+ break;
+ case EXPORT_TEXT_MARKERS:
+ // Raise the text above the staff as not to clash with the other stuff
+ str << "\\markup { \\hspace #0 \\raise #1.5 \"" << (*i_marker)->getName() << "\" }" << std::endl;
+ break;
+ default:
+ break;
+ }
+ prevMarkerTime = markerTime;
+ }
+ ++i_marker;
+ }
+ str << indent(--col) << "}" << std::endl;
+ }
+
+ // open \score section
+ str << "\\score {" << std::endl;
+
+ int lastTrackIndex = -1;
+ int voiceCounter = 0;
+ bool firstTrack = true;
+ int staffGroupCounter = 0;
+ int pianoStaffCounter = 0;
+ int bracket = 0;
+ int prevBracket = -1;
+
+ // Write out all segments for each Track, in track order.
+ // This involves a hell of a lot of loops through all tracks
+ // and segments, but the time spent doing that should still
+ // be relatively small in the greater scheme.
+
+ Track *track = 0;
+
+ for (int trackPos = 0;
+ (track = m_composition->getTrackByPosition(trackPos)) != 0; ++trackPos) {
+
+ for (Composition::iterator i = m_composition->begin();
+ i != m_composition->end(); ++i) {
+
+ if ((*i)->getTrack() != track->getId())
+ continue;
+
+ // handle the bracket(s) for the first track, and if no brackets
+ // present, open with a <<
+ prevBracket = bracket;
+ bracket = track->getStaffBracket();
+
+ //!!! how will all these indentions work out? Probably not well,
+ // but maybe if users always provide sensible input, this will work
+ // out sensibly. Maybe. If not, we'll need some tracking gizmos to
+ // figure out the indention, or just skip the indention for these or
+ // something. TBA.
+ if (firstTrack) {
+ // seems to be common to every case now
+ str << indent(col++) << "<< % common" << std::endl;
+ }
+
+ if (firstTrack && m_exportStaffGroup) {
+
+ if (bracket == Brackets::SquareOn) {
+ str << indent(col++) << "\\context StaffGroup = \"" << staffGroupCounter++
+ << "\" << " << std::endl; //indent+
+ } else if (bracket == Brackets::CurlyOn) {
+ str << indent(col++) << "\\context PianoStaff = \"" << pianoStaffCounter++
+ << "\" << " << std::endl; //indent+
+ } else if (bracket == Brackets::CurlySquareOn) {
+ str << indent(col++) << "\\context StaffGroup = \"" << staffGroupCounter++
+ << "\" << " << std::endl; //indent+
+ str << indent(col++) << "\\context PianoStaff = \"" << pianoStaffCounter++
+ << "\" << " << std::endl; //indent+
+ }
+
+ // Make chords offset colliding notes by default (only write for
+ // first track)
+ str << indent(++col) << "% force offset of colliding notes in chords:"
+ << std::endl;
+ str << indent(col) << "\\override Score.NoteColumn #\'force-hshift = #1.0"
+ << std::endl;
+ }
+
+ emit setProgress(int(double(trackPos) /
+ double(m_composition->getNbTracks()) * 100.0));
+ rgapp->refreshGUI(50);
+
+ bool currentSegmentSelected = false;
+ if ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) &&
+ (m_view != NULL) && (m_view->haveSelection())) {
+ //
+ // Check whether the current segment is in the list of selected segments.
+ //
+ SegmentSelection selection = m_view->getSelection();
+ for (SegmentSelection::iterator it = selection.begin(); it != selection.end(); it++) {
+ if ((*it) == (*i)) currentSegmentSelected = true;
+ }
+ } else if ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && (m_notationView != NULL)) {
+ currentSegmentSelected = m_notationView->hasSegment(*i);
+ }
+
+ // Check whether the track is a non-midi track.
+ InstrumentId instrumentId = track->getInstrument();
+ bool isMidiTrack = instrumentId >= MidiInstrumentBase;
+
+ if (isMidiTrack && ( // Skip non-midi tracks.
+ (m_exportSelection == EXPORT_ALL_TRACKS) ||
+ ((m_exportSelection == EXPORT_NONMUTED_TRACKS) && (!track->isMuted())) ||
+ ((m_exportSelection == EXPORT_SELECTED_TRACK) && (m_view != NULL) &&
+ (track->getId() == m_composition->getSelectedTrack())) ||
+ ((m_exportSelection == EXPORT_SELECTED_TRACK) && (m_notationView != NULL) &&
+ (track->getId() == m_notationView->getCurrentSegment()->getTrack())) ||
+ ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && (currentSegmentSelected)))) {
+ if ((int) (*i)->getTrack() != lastTrackIndex) {
+ if (lastTrackIndex != -1) {
+ // close the old track (Staff context)
+ str << indent(--col) << ">> % Staff ends" << std::endl; //indent-
+ }
+ lastTrackIndex = (*i)->getTrack();
+
+
+ // handle any necessary bracket closures with a rude
+ // hack, because bracket closures need to be handled
+ // right under staff closures, but at this point in the
+ // loop we are one track too early for closing, so we use
+ // the bracket setting for the previous track for closing
+ // purposes (I'm not quite sure why this works, but it does)
+ if (m_exportStaffGroup) {
+ if (prevBracket == Brackets::SquareOff ||
+ prevBracket == Brackets::SquareOnOff) {
+ str << indent(--col) << ">> % StaffGroup " << staffGroupCounter
+ << std::endl; //indent-
+ } else if (prevBracket == Brackets::CurlyOff) {
+ str << indent(--col) << ">> % PianoStaff " << pianoStaffCounter
+ << std::endl; //indent-
+ } else if (prevBracket == Brackets::CurlySquareOff) {
+ str << indent(--col) << ">> % PianoStaff " << pianoStaffCounter
+ << std::endl; //indent-
+ str << indent(--col) << ">> % StaffGroup " << staffGroupCounter
+ << std::endl; //indent-
+ }
+ }
+
+ // handle any bracket start events (unless track staff
+ // brackets are being ignored, as when printing single parts
+ // out of a bigger score one by one)
+ if (!firstTrack && m_exportStaffGroup) {
+ if (bracket == Brackets::SquareOn ||
+ bracket == Brackets::SquareOnOff) {
+ str << indent(col++) << "\\context StaffGroup = \""
+ << ++staffGroupCounter << "\" <<" << std::endl;
+ } else if (bracket == Brackets::CurlyOn) {
+ str << indent(col++) << "\\context PianoStaff = \""
+ << ++pianoStaffCounter << "\" <<" << std::endl;
+ } else if (bracket == Brackets::CurlySquareOn) {
+ str << indent(col++) << "\\context StaffGroup = \""
+ << ++staffGroupCounter << "\" <<" << std::endl;
+ str << indent(col++) << "\\context PianoStaff = \""
+ << ++pianoStaffCounter << "\" <<" << std::endl;
+ }
+ }
+
+ // avoid problem with <untitled> tracks yielding a
+ // .ly file that jumbles all notes together on a
+ // single staff... every Staff context has to
+ // have a unique name, even if the
+ // Staff.instrument property is the same for
+ // multiple staffs...
+ // Added an option to merge staffs with the same, non-empty
+ // name. This option makes it possible to produce staffs
+ // with polyphonic, and polyrhytmic, music. Polyrhytmic
+ // music in a single staff is typical in piano, or
+ // guitar music. (hjj)
+ // In the case of colliding note heads, user may define
+ // - DISPLACED_X -- for a note/chord
+ // - INVISIBLE -- for a rest
+ std::ostringstream staffName;
+ staffName << protectIllegalChars(m_composition->
+ getTrackById(lastTrackIndex)->getLabel());
+
+ if (!m_exportStaffMerge || staffName.str() == "") {
+ str << std::endl << indent(col)
+ << "\\context Staff = \"track "
+ << (trackPos + 1) << "\" ";
+ } else {
+ str << std::endl << indent(col)
+ << "\\context Staff = \"" << staffName.str()
+ << "\" ";
+ }
+
+ str << "<< " << std::endl;
+
+ // The octavation is omitted in the instrument name.
+ // HJJ: Should it be automatically added to the clef: G^8 ?
+ // What if two segments have different transpose in a track?
+ std::ostringstream staffNameWithTranspose;
+ staffNameWithTranspose << "\\markup { \\column { \"" << staffName.str() << " \"";
+ if (((*i)->getTranspose() % 12) != 0) {
+ staffNameWithTranspose << " \\line { ";
+ switch ((*i)->getTranspose() % 12) {
+ case 1 : staffNameWithTranspose << "\"in D\" \\smaller \\flat"; break;
+ case 2 : staffNameWithTranspose << "\"in D\""; break;
+ case 3 : staffNameWithTranspose << "\"in E\" \\smaller \\flat"; break;
+ case 4 : staffNameWithTranspose << "\"in E\""; break;
+ case 5 : staffNameWithTranspose << "\"in F\""; break;
+ case 6 : staffNameWithTranspose << "\"in G\" \\smaller \\flat"; break;
+ case 7 : staffNameWithTranspose << "\"in G\""; break;
+ case 8 : staffNameWithTranspose << "\"in A\" \\smaller \\flat"; break;
+ case 9 : staffNameWithTranspose << "\"in A\""; break;
+ case 10 : staffNameWithTranspose << "\"in B\" \\smaller \\flat"; break;
+ case 11 : staffNameWithTranspose << "\"in B\""; break;
+ }
+ staffNameWithTranspose << " }";
+ }
+ staffNameWithTranspose << " } }";
+ if (m_languageLevel < LILYPOND_VERSION_2_10) {
+ str << indent(++col) << "\\set Staff.instrument = " << staffNameWithTranspose.str()
+ << std::endl;
+ } else {
+ str << indent(++col) << "\\set Staff.instrumentName = "
+ << staffNameWithTranspose.str() << std::endl;
+ }
+
+ if (m_exportMidi) {
+ // Set midi instrument for the Staff
+ std::ostringstream staffMidiName;
+ Instrument *instr = m_studio->getInstrumentById(
+ m_composition->getTrackById(lastTrackIndex)->getInstrument());
+ staffMidiName << instr->getProgramName();
+
+ str << indent(col) << "\\set Staff.midiInstrument = \"" << staffMidiName.str()
+ << "\"" << std::endl;
+ }
+
+ // multi measure rests are used by default
+ str << indent(col) << "\\set Score.skipBars = ##t" << std::endl;
+
+ // turn off the stupid accidental cancelling business,
+ // because we don't do that ourselves, and because my 11
+ // year old son pointed out to me that it "Looks really
+ // stupid. Why is it cancelling out four flats and then
+ // adding five flats back? That's brain damaged."
+ str << indent(col) << "\\set Staff.printKeyCancellation = ##f" << std::endl;
+ str << indent(col) << "\\new Voice \\global" << std::endl;
+ if (tempoCount > 0) {
+ str << indent(col) << "\\new Voice \\globalTempo" << std::endl;
+ }
+ if ( m_exportMarkerMode != EXPORT_NO_MARKERS ) {
+ str << indent(col) << "\\new Voice \\markers" << std::endl;
+ }
+
+ }
+
+ // Temporary storage for non-atomic events (!BOOM)
+ // ex. LilyPond expects signals when a decrescendo starts
+ // as well as when it ends
+ eventendlist eventsInProgress;
+ eventstartlist eventsToStart;
+
+ // If the segment doesn't start at 0, add a "skip" to the start
+ // No worries about overlapping segments, because Voices can overlap
+ // voiceCounter is a hack because LilyPond does not by default make
+ // them unique
+ std::ostringstream voiceNumber;
+ voiceNumber << "voice " << ++voiceCounter;
+
+ str << std::endl << indent(col++) << "\\context Voice = \"" << voiceNumber.str()
+ << "\" {"; // indent+
+
+ str << std::endl << indent(col) << "\\override Voice.TextScript #'padding = #2.0";
+ str << std::endl << indent(col) << "\\override MultiMeasureRest #'expand-limit = 1" << std::endl;
+
+ // staff notation size
+ int staffSize = track->getStaffSize();
+ if (staffSize == StaffTypes::Small) str << indent(col) << "\\small" << std::endl;
+ else if (staffSize == StaffTypes::Tiny) str << indent(col) << "\\tiny" << std::endl;
+
+ SegmentNotationHelper helper(**i);
+ helper.setNotationProperties();
+
+ int firstBar = m_composition->getBarNumber((*i)->getStartTime());
+
+ if (firstBar > 0) {
+ // Add a skip for the duration until the start of the first
+ // bar in the segment. If the segment doesn't start on a bar
+ // line, an additional skip will be written (in the form of
+ // a series of rests) at the start of writeBar, below.
+ //!!! This doesn't cope correctly yet with time signature changes
+ // during this skipped section.
+ str << std::endl << indent(col);
+ writeSkip(timeSignature, compositionStartTime,
+ m_composition->getBarStart(firstBar) - compositionStartTime,
+ false, str);
+ }
+
+ std::string lilyText = ""; // text events
+ std::string prevStyle = ""; // track note styles
+
+ Rosegarden::Key key;
+
+ bool haveRepeating = false;
+ bool haveAlternates = false;
+
+ bool nextBarIsAlt1 = false;
+ bool nextBarIsAlt2 = false;
+ bool prevBarWasAlt2 = false;
+
+ int MultiMeasureRestCount = 0;
+
+ bool nextBarIsDouble = false;
+ bool nextBarIsEnd = false;
+ bool nextBarIsDot = false;
+
+ for (int barNo = m_composition->getBarNumber((*i)->getStartTime());
+ barNo <= m_composition->getBarNumber((*i)->getEndMarkerTime());
+ ++barNo) {
+
+ timeT barStart = m_composition->getBarStart(barNo);
+ timeT barEnd = m_composition->getBarEnd(barNo);
+ if (barStart < compositionStartTime) {
+ barStart = compositionStartTime;
+ }
+
+ // open \repeat section if this is the first bar in the
+ // repeat
+ if ((*i)->isRepeating() && !haveRepeating) {
+
+ haveRepeating = true;
+
+ //!!! calculate the number of times this segment
+ //repeats and make the following variable meaningful
+ int numRepeats = 2;
+
+ str << std::endl << indent(col++) << "\\repeat volta " << numRepeats << " {";
+ }
+
+ // open the \alternative section if this bar is alternative ending 1
+ // ending (because there was an "Alt1" flag in the
+ // previous bar to the left of where we are right now)
+ //
+ // Alt1 remains in effect until we run into Alt2, which
+ // runs to the end of the segment
+ if (nextBarIsAlt1 && haveRepeating) {
+ str << std::endl << indent(--col) << "} \% repeat close (before alternatives) ";
+ str << std::endl << indent(col++) << "\\alternative {";
+ str << std::endl << indent(col++) << "{ \% open alternative 1 ";
+ nextBarIsAlt1 = false;
+ haveAlternates = true;
+ } else if (nextBarIsAlt2 && haveRepeating) {
+ if (!prevBarWasAlt2) {
+ col--;
+ // add an extra str to the following to shut up
+ // compiler warning from --ing and ++ing it in the
+ // same statement
+ str << std::endl << indent(--col) << "} \% close alternative 1 ";
+ str << std::endl << indent(col++) << "{ \% open alternative 2";
+ col++;
+ }
+ prevBarWasAlt2 = true;
+ }
+
+ // write out a bar's worth of events
+ writeBar(*i, barNo, barStart, barEnd, col, key,
+ lilyText,
+ prevStyle, eventsInProgress, str,
+ MultiMeasureRestCount,
+ nextBarIsAlt1, nextBarIsAlt2, nextBarIsDouble, nextBarIsEnd, nextBarIsDot);
+
+ }
+
+ // close \repeat
+ if (haveRepeating) {
+
+ // close \alternative section if present
+ if (haveAlternates) {
+ str << std::endl << indent(--col) << " } \% close alternative 2 ";
+ }
+
+ // close \repeat section in either case
+ str << std::endl << indent(--col) << " } \% close "
+ << (haveAlternates ? "alternatives" : "repeat");
+ }
+
+ // closing bar
+ if (((*i)->getEndMarkerTime() == compositionEndTime) && !haveRepeating) {
+ str << std::endl << indent(col) << "\\bar \"|.\"";
+ }
+
+ // close Voice context
+ str << std::endl << indent(--col) << "} % Voice" << std::endl; // indent-
+
+ //
+ // Write accumulated lyric events to the Lyric context, if desired.
+ //
+ // Sync the code below with LyricEditDialog::unparse() !!
+ //
+ if (m_exportLyrics) {
+ for (long currentVerse = 0, lastVerse = 0;
+ currentVerse <= lastVerse;
+ currentVerse++) {
+ bool haveLyric = false;
+ bool firstNote = true;
+ QString text = "";
+
+ timeT lastTime = (*i)->getStartTime();
+ for (Segment::iterator j = (*i)->begin();
+ (*i)->isBeforeEndMarker(j); ++j) {
+
+ bool isNote = (*j)->isa(Note::EventType);
+ bool isLyric = false;
+
+ if (!isNote) {
+ if ((*j)->isa(Text::EventType)) {
+ std::string textType;
+ if ((*j)->get
+ <String>(Text::TextTypePropertyName, textType) &&
+ textType == Text::Lyric) {
+ isLyric = true;
+ }
+ }
+ }
+
+ if (!isNote && !isLyric) continue;
+
+ timeT myTime = (*j)->getNotationAbsoluteTime();
+
+ if (isNote) {
+ if ((myTime > lastTime) || firstNote) {
+ if (!haveLyric)
+ text += " _";
+ lastTime = myTime;
+ haveLyric = false;
+ firstNote = false;
+ }
+ }
+
+ if (isLyric) {
+ long verse;
+ (*j)->get<Int>(Text::LyricVersePropertyName, verse);
+
+ if (verse == currentVerse) {
+ std::string ssyllable;
+ (*j)->get<String>(Text::TextPropertyName, ssyllable);
+ text += " ";
+
+ QString syllable(strtoqstr(ssyllable));
+ syllable.replace(QRegExp("\\s+"), "");
+ text += "\"" + syllable + "\"";
+ haveLyric = true;
+ } else if (verse > lastVerse) {
+ lastVerse = verse;
+ }
+ }
+ }
+
+ text.replace( QRegExp(" _+([^ ])") , " \\1" );
+ text.replace( "\"_\"" , " " );
+
+ // Do not create empty context for lyrics.
+ // Does this save some vertical space, as was written
+ // in earlier comment?
+ QRegExp rx( "\"" );
+ if ( rx.search( text ) != -1 ) {
+
+ str << indent(col) << "\\lyricsto \"" << voiceNumber.str() << "\""
+ << " \\new Lyrics \\lyricmode {" << std::endl;
+ if (m_lyricsHAlignment == RIGHT_ALIGN) {
+ str << indent(++col) << "\\override LyricText #'self-alignment-X = #RIGHT"
+ << std::endl;
+ } else if (m_lyricsHAlignment == CENTER_ALIGN) {
+ str << indent(++col) << "\\override LyricText #'self-alignment-X = #CENTER"
+ << std::endl;
+ } else {
+ str << indent(++col) << "\\override LyricText #'self-alignment-X = #LEFT"
+ << std::endl;
+ }
+ str << indent(col) << "\\set ignoreMelismata = ##t" << std::endl;
+ str << indent(col) << text.utf8() << " " << std::endl;
+ str << indent(col) << "\\unset ignoreMelismata" << std::endl;
+ str << indent(--col) << "} % Lyrics " << (currentVerse+1) << std::endl;
+ // close the Lyrics context
+ } // if ( rx.search( text....
+ } // for (long currentVerse = 0....
+ } // if (m_exportLyrics....
+ } // if (isMidiTrack....
+ firstTrack = false;
+ } // for (Composition::iterator i = m_composition->begin()....
+ } // for (int trackPos = 0....
+
+ // close the last track (Staff context)
+ if (voiceCounter > 0) {
+ str << indent(--col) << ">> % Staff (final) ends" << std::endl; // indent-
+
+ // handle any necessary final bracket closures (if brackets are being
+ // exported)
+ if (m_exportStaffGroup) {
+ if (bracket == Brackets::SquareOff ||
+ bracket == Brackets::SquareOnOff) {
+ str << indent(--col) << ">> % StaffGroup " << staffGroupCounter
+ << std::endl; //indent-
+ } else if (bracket == Brackets::CurlyOff) {
+ str << indent(--col) << ">> % PianoStaff (final) " << pianoStaffCounter
+ << std::endl; //indent-
+ } else if (bracket == Brackets::CurlySquareOff) {
+ str << indent(--col) << ">> % PianoStaff (final) " << pianoStaffCounter
+ << std::endl; //indent-
+ str << indent(--col) << ">> % StaffGroup (final) " << staffGroupCounter
+ << std::endl; //indent-
+ }
+ }
+ } else {
+ str << indent(--col) << "% (All staffs were muted.)" << std::endl;
+ }
+
+ // close \notes section
+ str << std::endl << indent(--col) << ">> % notes" << std::endl << std::endl; // indent-
+// str << std::endl << indent(col) << ">> % global wrapper" << std::endl;
+
+ // write \layout block
+ str << indent(col) << "\\layout { }" << std::endl;
+
+ // write initial tempo in Midi block, if user wishes (added per user request...
+ // makes debugging the .ly file easier because fewer "noisy" errors are
+ // produced during the process of rendering MIDI...)
+ if (m_exportMidi) {
+ int tempo = int(Composition::getTempoQpm(m_composition->getTempoAtTime(m_composition->getStartMarker())));
+ // Incomplete? Can I get away without converting tempo relative to the time
+ // signature for this purpose? we'll see...
+ str << indent(col++) << "\\midi {" << std::endl;
+ str << indent(col) << "\\tempo 4 = " << tempo << std::endl;
+ str << indent(--col) << "} " << std::endl;
+ }
+
+ // close \score section and close out the file
+ str << "} % score" << std::endl;
+ str.close();
+ return true;
+}
+
+timeT
+LilyPondExporter::calculateDuration(Segment *s,
+ const Segment::iterator &i,
+ timeT barEnd,
+ timeT &soundingDuration,
+ const std::pair<int, int> &tupletRatio,
+ bool &overlong)
+{
+ timeT duration = (*i)->getNotationDuration();
+ timeT absTime = (*i)->getNotationAbsoluteTime();
+
+ RG_DEBUG << "LilyPondExporter::calculateDuration: first duration, absTime: "
+ << duration << ", " << absTime << endl;
+
+ timeT durationCorrection = 0;
+
+ if ((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType)) {
+ try {
+ // tuplet compensation, etc
+ Note::Type type = (*i)->get<Int>(NOTE_TYPE);
+ int dots = (*i)->get<Int>(NOTE_DOTS);
+ durationCorrection = Note(type, dots).getDuration() - duration;
+ } catch (Exception e) { // no properties
+ }
+ }
+
+ duration += durationCorrection;
+
+ RG_DEBUG << "LilyPondExporter::calculateDuration: now duration is "
+ << duration << " after correction of " << durationCorrection << endl;
+
+ soundingDuration = duration * tupletRatio.first/ tupletRatio.second;
+
+ timeT toNext = barEnd - absTime;
+ if (soundingDuration > toNext) {
+ soundingDuration = toNext;
+ duration = soundingDuration * tupletRatio.second/ tupletRatio.first;
+ overlong = true;
+ }
+
+ RG_DEBUG << "LilyPondExporter::calculateDuration: time to barEnd is "
+ << toNext << endl;
+
+ // Examine the following event, and truncate our duration
+ // if we overlap it.
+ Segment::iterator nextElt = s->end();
+ toNext = soundingDuration;
+
+ if ((*i)->isa(Note::EventType)) {
+
+ Chord chord(*s, i, m_composition->getNotationQuantizer());
+ Segment::iterator nextElt = chord.getFinalElement();
+ ++nextElt;
+
+ if (s->isBeforeEndMarker(nextElt)) {
+ // The quantizer sometimes sticks a rest at the same time
+ // as this note -- don't use that one here, and mark it as
+ // not to be exported -- it's just a heavy-handed way of
+ // rendering counterpoint in RG
+ if ((*nextElt)->isa(Note::EventRestType) &&
+ (*nextElt)->getNotationAbsoluteTime() == absTime) {
+ (*nextElt)->set<Bool>(SKIP_PROPERTY, true);
+ ++nextElt;
+ }
+ }
+
+ } else {
+ nextElt = i;
+ ++nextElt;
+ while (s->isBeforeEndMarker(nextElt)) {
+ if ((*nextElt)->isa(Controller::EventType) ||
+ (*nextElt)->isa(ProgramChange::EventType) ||
+ (*nextElt)->isa(SystemExclusive::EventType) ||
+ (*nextElt)->isa(ChannelPressure::EventType) ||
+ (*nextElt)->isa(KeyPressure::EventType) ||
+ (*nextElt)->isa(PitchBend::EventType))
+ ++nextElt;
+ else
+ break;
+ }
+ }
+
+ if (s->isBeforeEndMarker(nextElt)) {
+ RG_DEBUG << "LilyPondExporter::calculateDuration: inside conditional " << endl;
+ toNext = (*nextElt)->getNotationAbsoluteTime() - absTime;
+ // if the note was lengthened, assume it was lengthened to the left
+ // when truncating to the beginning of the next note
+ if (durationCorrection > 0) {
+ toNext += durationCorrection;
+ }
+ if (soundingDuration > toNext) {
+ soundingDuration = toNext;
+ duration = soundingDuration * tupletRatio.second/ tupletRatio.first;
+ }
+ }
+
+ RG_DEBUG << "LilyPondExporter::calculateDuration: second toNext is "
+ << toNext << endl;
+
+ RG_DEBUG << "LilyPondExporter::calculateDuration: final duration, soundingDuration: " << duration << ", " << soundingDuration << endl;
+
+ return duration;
+}
+
+void
+LilyPondExporter::writeBar(Segment *s,
+ int barNo, int barStart, int barEnd, int col,
+ Rosegarden::Key &key,
+ std::string &lilyText,
+ std::string &prevStyle,
+ eventendlist &eventsInProgress,
+ std::ofstream &str,
+ int &MultiMeasureRestCount,
+ bool &nextBarIsAlt1, bool &nextBarIsAlt2,
+ bool &nextBarIsDouble, bool &nextBarIsEnd, bool &nextBarIsDot)
+{
+ int lastStem = 0; // 0 => unset, -1 => down, 1 => up
+ int isGrace = 0;
+
+ Segment::iterator i = s->findTime(barStart);
+ if (!s->isBeforeEndMarker(i))
+ return ;
+
+ if (MultiMeasureRestCount == 0) {
+ str << std::endl;
+
+ if ((barNo + 1) % 5 == 0) {
+ str << "%% " << barNo + 1 << std::endl << indent(col);
+ } else {
+ str << indent(col);
+ }
+ }
+
+ bool isNew = false;
+ TimeSignature timeSignature = m_composition->getTimeSignatureInBar(barNo, isNew);
+ if (isNew) {
+ if (timeSignature.isHidden()) {
+ str << "\\once \\override Staff.TimeSignature #'break-visibility = #(vector #f #f #f) ";
+ }
+ str << "\\time "
+ << timeSignature.getNumerator() << "/"
+ << timeSignature.getDenominator()
+ << std::endl << indent(col);
+ }
+
+ timeT absTime = (*i)->getNotationAbsoluteTime();
+ timeT writtenDuration = 0;
+ std::pair<int,int> barDurationRatio(timeSignature.getNumerator(),timeSignature.getDenominator());
+ std::pair<int,int> durationRatioSum(0,1);
+ static std::pair<int,int> durationRatio(0,1);
+
+ if (absTime > barStart) {
+ Note note(Note::getNearestNote(absTime - barStart, MAX_DOTS));
+ writtenDuration += note.getDuration();
+ durationRatio = writeSkip(timeSignature, 0, note.getDuration(), true, str);
+ durationRatioSum = fractionSum(durationRatioSum,durationRatio);
+ // str << qstrtostr(QString(" %{ %1/%2 %} ").arg(durationRatio.first).arg(durationRatio.second)); // DEBUG
+ }
+
+ timeT prevDuration = -1;
+ eventstartlist eventsToStart;
+
+ long groupId = -1;
+ std::string groupType = "";
+ std::pair<int, int> tupletRatio(1, 1);
+
+ bool overlong = false;
+ bool newBeamedGroup = false;
+ int notesInBeamedGroup = 0;
+
+ while (s->isBeforeEndMarker(i)) {
+
+ if ((*i)->getNotationAbsoluteTime() >= barEnd)
+ break;
+
+ // First test whether we're entering or leaving a group,
+ // before we consider how to write the event itself (at least
+ // for pre-2.0 LilyPond output)
+ QString startGroupBeamingsStr = "";
+ QString endGroupBeamingsStr = "";
+
+ if ((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType) ||
+ (*i)->isa(Clef::EventType) || (*i)->isa(Rosegarden::Key::EventType)) {
+
+ long newGroupId = -1;
+ if ((*i)->get
+ <Int>(BEAMED_GROUP_ID, newGroupId)) {
+
+ if (newGroupId != groupId) {
+ // entering a new beamed group
+
+ if (groupId != -1) {
+ // and leaving an old one
+ if (groupType == GROUP_TYPE_TUPLED) {
+ if (m_exportBeams && notesInBeamedGroup > 0)
+ endGroupBeamingsStr += "] ";
+ endGroupBeamingsStr += "} ";
+ } else if (groupType == GROUP_TYPE_BEAMED) {
+ if (m_exportBeams && notesInBeamedGroup > 0)
+ endGroupBeamingsStr += "] ";
+ }
+ }
+
+ groupId = newGroupId;
+ groupType = "";
+ (void)(*i)->get
+ <String>(BEAMED_GROUP_TYPE, groupType);
+
+ if (groupType == GROUP_TYPE_TUPLED) {
+ long numerator = 0;
+ long denominator = 0;
+ (*i)->get
+ <Int>(BEAMED_GROUP_TUPLED_COUNT, numerator);
+ (*i)->get
+ <Int>(BEAMED_GROUP_UNTUPLED_COUNT, denominator);
+ if (numerator == 0 || denominator == 0) {
+ std::cerr << "WARNING: LilyPondExporter::writeBar: "
+ << "tupled event without tupled/untupled counts"
+ << std::endl;
+ groupId = -1;
+ groupType = "";
+ } else {
+ startGroupBeamingsStr += QString("\\times %1/%2 { ").arg(numerator).arg(denominator);
+ tupletRatio = std::pair<int, int>(numerator, denominator);
+ // Require explicit beamed groups,
+ // fixes bug #1683205.
+ // HJJ: Why line below was originally present?
+ // newBeamedGroup = true;
+ notesInBeamedGroup = 0;
+ }
+ } else if (groupType == GROUP_TYPE_BEAMED) {
+ newBeamedGroup = true;
+ notesInBeamedGroup = 0;
+ // there can currently be only on group type, reset tuplet ratio
+ tupletRatio = std::pair<int, int>(1,1);
+ }
+ }
+
+ }
+ else {
+
+ if (groupId != -1) {
+ // leaving a beamed group
+ if (groupType == GROUP_TYPE_TUPLED) {
+ if (m_exportBeams && notesInBeamedGroup > 0)
+ endGroupBeamingsStr += "] ";
+ endGroupBeamingsStr += "} ";
+ tupletRatio = std::pair<int, int>(1, 1);
+ } else if (groupType == GROUP_TYPE_BEAMED) {
+ if (m_exportBeams && notesInBeamedGroup > 0)
+ endGroupBeamingsStr += "] ";
+ }
+ groupId = -1;
+ groupType = "";
+ }
+ }
+ }
+
+ // Test whether the next note is grace note or not.
+ // The start or end of beamed grouping should be put in proper places.
+ str << endGroupBeamingsStr.utf8();
+ if ((*i)->has(IS_GRACE_NOTE) && (*i)->get<Bool>(IS_GRACE_NOTE)) {
+ if (isGrace == 0) {
+ isGrace = 1;
+ str << "\\grace { ";
+ // str << "%{ grace starts %} "; // DEBUG
+ }
+ } else if (isGrace == 1) {
+ isGrace = 0;
+ // str << "%{ grace ends %} "; // DEBUG
+ str << "} ";
+ }
+ str << startGroupBeamingsStr.utf8();
+
+ timeT soundingDuration = -1;
+ timeT duration = calculateDuration
+ (s, i, barEnd, soundingDuration, tupletRatio, overlong);
+
+ if (soundingDuration == -1) {
+ soundingDuration = duration * tupletRatio.first / tupletRatio.second;
+ }
+
+ if ((*i)->has(SKIP_PROPERTY)) {
+ (*i)->unset(SKIP_PROPERTY);
+ ++i;
+ continue;
+ }
+
+ bool needsSlashRest = false;
+
+ if ((*i)->isa(Note::EventType)) {
+
+ Chord chord(*s, i, m_composition->getNotationQuantizer());
+ Event *e = *chord.getInitialNote();
+ bool tiedForward = false;
+ bool tiedUp = false;
+
+ // Examine the following event, and truncate our duration
+ // if we overlap it.
+
+ if (e->has(DISPLACED_X)) {
+ double xDisplacement = 1 + ((double) e->get
+ <Int>(DISPLACED_X)) / 1000;
+ str << "\\once \\override NoteColumn #'force-hshift = #"
+ << xDisplacement << " ";
+ }
+
+ bool hiddenNote = false;
+ if (e->has(INVISIBLE)) {
+ if (e->get
+ <Bool>(INVISIBLE)) {
+ hiddenNote = true;
+ }
+ }
+
+ if ( hiddenNote ) {
+ str << "\\hideNotes ";
+ }
+
+ if (e->has(NotationProperties::STEM_UP)) {
+ if (e->get
+ <Bool>(NotationProperties::STEM_UP)) {
+ if (lastStem != 1) {
+ str << "\\stemUp ";
+ lastStem = 1;
+ }
+ }
+ else {
+ if (lastStem != -1) {
+ str << "\\stemDown ";
+ lastStem = -1;
+ }
+ }
+ } else {
+ if (lastStem != 0) {
+ str << "\\stemNeutral ";
+ lastStem = 0;
+ }
+ }
+
+ if (chord.size() > 1)
+ str << "< ";
+
+ Segment::iterator stylei = s->end();
+
+ for (i = chord.getInitialElement(); s->isBeforeEndMarker(i); ++i) {
+
+ if ((*i)->isa(Text::EventType)) {
+ if (!handleDirective(*i, lilyText, nextBarIsAlt1, nextBarIsAlt2,
+ nextBarIsDouble, nextBarIsEnd, nextBarIsDot)) {
+
+ handleText(*i, lilyText);
+ }
+
+ } else if ((*i)->isa(Note::EventType)) {
+
+ if (m_languageLevel >= LILYPOND_VERSION_2_8) {
+ // one \tweak per each chord note
+ if (chord.size() > 1)
+ writeStyle(*i, prevStyle, col, str, true);
+ else
+ writeStyle(*i, prevStyle, col, str, false);
+ } else {
+ // only one override per chord, and that outside the <>
+ stylei = i;
+ }
+ writePitch(*i, key, str);
+
+ bool noteHasCautionaryAccidental = false;
+ (*i)->get
+ <Bool>(NotationProperties::USE_CAUTIONARY_ACCIDENTAL, noteHasCautionaryAccidental);
+ if (noteHasCautionaryAccidental)
+ str << "?";
+
+ // get TIED_FORWARD and TIE_IS_ABOVE for later
+ (*i)->get<Bool>(TIED_FORWARD, tiedForward);
+ (*i)->get<Bool>(TIE_IS_ABOVE, tiedUp);
+
+ str << " ";
+ } else if ((*i)->isa(Indication::EventType)) {
+ eventsToStart.insert(*i);
+ eventsInProgress.insert(*i);
+ }
+
+ if (i == chord.getFinalElement())
+ break;
+ }
+
+ if (chord.size() > 1)
+ str << "> ";
+
+ if (duration != prevDuration) {
+ durationRatio = writeDuration(duration, str);
+ str << " ";
+ prevDuration = duration;
+ }
+
+ if (m_languageLevel == LILYPOND_VERSION_2_6) {
+ // only one override per chord, and that outside the <>
+ if (stylei != s->end()) {
+ writeStyle(*stylei, prevStyle, col, str, false);
+ stylei = s->end();
+ }
+ }
+
+ if (lilyText != "") {
+ str << lilyText;
+ lilyText = "";
+ }
+ writeSlashes(*i, str);
+
+ writtenDuration += soundingDuration;
+ std::pair<int,int> ratio = fractionProduct(durationRatio,tupletRatio);
+ durationRatioSum = fractionSum(durationRatioSum, ratio);
+ // 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
+
+ std::vector<Mark> marks(chord.getMarksForChord());
+ // problem here: stem direction unavailable (it's a view-local property)
+ bool stemUp = true;
+ e->get
+ <Bool>(NotationProperties::STEM_UP, stemUp);
+ for (std::vector<Mark>::iterator j = marks.begin(); j != marks.end(); ++j) {
+ str << composeLilyMark(*j, stemUp);
+ }
+ if (marks.size() > 0)
+ str << " ";
+
+ handleEndingEvents(eventsInProgress, i, str);
+ handleStartingEvents(eventsToStart, str);
+
+ if (tiedForward)
+ if (tiedUp)
+ str << "^~ ";
+ else
+ str << "_~ ";
+
+ if ( hiddenNote ) {
+ str << "\\unHideNotes ";
+ }
+
+ if (newBeamedGroup) {
+ // This is a workaround for bug #1705430:
+ // Beaming groups erroneous after merging notes
+ // There will be fewer "e4. [ ]" errors in LilyPond-compiling.
+ // HJJ: This should be fixed in notation engine,
+ // after which the workaround below should be removed.
+ Note note(Note::getNearestNote(duration, MAX_DOTS));
+
+ switch (note.getNoteType()) {
+ case Note::SixtyFourthNote:
+ case Note::ThirtySecondNote:
+ case Note::SixteenthNote:
+ case Note::EighthNote:
+ notesInBeamedGroup++;
+ break;
+ }
+ }
+ // // Old version before the workaround for bug #1705430:
+ // if (newBeamedGroup)
+ // notesInBeamedGroup++;
+ } else if ((*i)->isa(Note::EventRestType)) {
+
+ bool hiddenRest = false;
+ if ((*i)->has(INVISIBLE)) {
+ if ((*i)->get
+ <Bool>(INVISIBLE)) {
+ hiddenRest = true;
+ }
+ }
+
+ bool offsetRest = false;
+ int restOffset = 0;
+ if ((*i)->has(DISPLACED_Y)) {
+ restOffset = (*i)->get<Int>(DISPLACED_Y);
+ offsetRest = true;
+ }
+
+ if (offsetRest) {
+ std::cout << "REST OFFSET: " << restOffset << std::endl;
+ } else {
+ std::cout << "NO REST OFFSET" << std::endl;
+ }
+
+ if (MultiMeasureRestCount == 0) {
+ if (hiddenRest) {
+ str << "s";
+ } else if (duration == timeSignature.getBarDuration()) {
+ // Look ahead the segment in order to detect
+ // the number of measures in the multi measure rest.
+ Segment::iterator mm_i = i;
+ while (s->isBeforeEndMarker(++mm_i)) {
+ if ((*mm_i)->isa(Note::EventRestType) &&
+ (*mm_i)->getNotationDuration() == (*i)->getNotationDuration() &&
+ timeSignature == m_composition->getTimeSignatureAt((*mm_i)->getNotationAbsoluteTime())) {
+ MultiMeasureRestCount++;
+ } else {
+ break;
+ }
+ }
+ str << "R";
+ } else {
+ if (offsetRest) {
+ // use offset height to get an approximate corresponding
+ // height on staff
+ restOffset = restOffset / 1000;
+ restOffset -= restOffset * 2;
+
+ // use height on staff to get a MIDI pitch
+ // get clef from whatever the last clef event was
+ Rosegarden::Key k;
+ Accidental a;
+ Pitch helper(restOffset, m_lastClefFound, k, a);
+
+ // port some code from writePitch() here, rather than
+ // rewriting writePitch() to do both jobs, which
+ // somebody could conceivably clean up one day if anyone
+ // is bored
+
+ // use MIDI pitch to get a named note
+ int p = helper.getPerformancePitch();
+ std::string n = convertPitchToLilyNote(p, a, k);
+
+ // write named note
+ str << n;
+
+ // generate and write octave marks
+ std::string m = "";
+ int o = (int)(p / 12);
+
+ // mystery hack (it was always aiming too low)
+ o++;
+
+ if (o < 4) {
+ for (; o < 4; o++)
+ m += ",";
+ } else {
+ for (; o > 4; o--)
+ m += "\'";
+ }
+
+ str << m;
+
+ // defer the \rest until after any duration, because it
+ // can't come before a duration if a duration change is
+ // necessary, which is all determined a bit further on
+ needsSlashRest = true;
+
+
+ std::cout << "using pitch letter:"
+ << n << m
+ << " for offset: "
+ << restOffset
+ << " for calculated octave: "
+ << o
+ << " in clef: "
+ << m_lastClefFound.getClefType()
+ << std::endl;
+ } else {
+ str << "r";
+ }
+ }
+
+ if (duration != prevDuration) {
+ durationRatio = writeDuration(duration, str);
+ if (MultiMeasureRestCount > 0) {
+ str << "*" << (1 + MultiMeasureRestCount);
+ }
+ prevDuration = duration;
+ }
+
+ // have to add \rest to a fake rest note after any required
+ // duration change
+ if (needsSlashRest) {
+ str << "\\rest";
+ needsSlashRest = false;
+ }
+
+ if (lilyText != "") {
+ str << lilyText;
+ lilyText = "";
+ }
+
+ str << " ";
+
+ handleEndingEvents(eventsInProgress, i, str);
+ handleStartingEvents(eventsToStart, str);
+
+ if (newBeamedGroup)
+ notesInBeamedGroup++;
+ } else {
+ MultiMeasureRestCount--;
+ }
+ writtenDuration += soundingDuration;
+ std::pair<int,int> ratio = fractionProduct(durationRatio,tupletRatio);
+ durationRatioSum = fractionSum(durationRatioSum, ratio);
+ // 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
+ } else if ((*i)->isa(Clef::EventType)) {
+
+ try {
+ // Incomplete: Set which note the clef should center on (DMM - why?)
+ // To allow octavation of the clef, enclose the clefname always with quotes.
+ str << "\\clef \"";
+
+ Clef clef(**i);
+
+ if (clef.getClefType() == Clef::Treble) {
+ str << "treble";
+ } else if (clef.getClefType() == Clef::French) {
+ str << "french";
+ } else if (clef.getClefType() == Clef::Soprano) {
+ str << "soprano";
+ } else if (clef.getClefType() == Clef::Mezzosoprano) {
+ str << "mezzosoprano";
+ } else if (clef.getClefType() == Clef::Alto) {
+ str << "alto";
+ } else if (clef.getClefType() == Clef::Tenor) {
+ str << "tenor";
+ } else if (clef.getClefType() == Clef::Baritone) {
+ str << "baritone";
+ } else if (clef.getClefType() == Clef::Varbaritone) {
+ str << "varbaritone";
+ } else if (clef.getClefType() == Clef::Bass) {
+ str << "bass";
+ } else if (clef.getClefType() == Clef::Subbass) {
+ str << "subbass";
+ }
+
+ // save clef for later use by rests that need repositioned
+ m_lastClefFound = clef;
+ std::cout << "getting clef"
+ << std::endl
+ << "clef: "
+ << clef.getClefType()
+ << " lastClefFound: "
+ << m_lastClefFound.getClefType()
+ << std::endl;
+
+ // Transpose the clef one or two octaves up or down, if specified.
+ int octaveOffset = clef.getOctaveOffset();
+ if (octaveOffset > 0) {
+ str << "^" << 8*octaveOffset;
+ } else if (octaveOffset < 0) {
+ str << "_" << -8*octaveOffset;
+ }
+
+ str << "\"" << std::endl << indent(col);
+
+ } catch (Exception e) {
+ std::cerr << "Bad clef: " << e.getMessage() << std::endl;
+ }
+
+ } else if ((*i)->isa(Rosegarden::Key::EventType)) {
+ // ignore hidden key signatures
+ bool hiddenKey = false;
+ if ((*i)->has(INVISIBLE)) {
+ (*i)->get <Bool>(INVISIBLE, hiddenKey);
+ }
+
+ if (!hiddenKey) {
+ try {
+ str << "\\key ";
+ key = Rosegarden::Key(**i);
+
+ Accidental accidental = Accidentals::NoAccidental;
+
+ str << convertPitchToLilyNote(key.getTonicPitch(), accidental, key);
+
+ if (key.isMinor()) {
+ str << " \\minor";
+ } else {
+ str << " \\major";
+ }
+ str << std::endl << indent(col);
+
+ } catch (Exception e) {
+ std::cerr << "Bad key: " << e.getMessage() << std::endl;
+ }
+ }
+
+ } else if ((*i)->isa(Text::EventType)) {
+
+ if (!handleDirective(*i, lilyText, nextBarIsAlt1, nextBarIsAlt2,
+ nextBarIsDouble, nextBarIsEnd, nextBarIsDot)) {
+ handleText(*i, lilyText);
+ }
+
+ } else if ((*i)->isa(Guitar::Chord::EventType)) {
+
+ try {
+ Guitar::Chord chord = Guitar::Chord(**i);
+ const Guitar::Fingering& fingering = chord.getFingering();
+
+ int barreStart = 0, barreEnd = 0, barreFret = 0;
+
+ //
+ // Check if there is a barre.
+ //
+ if (fingering.hasBarre()) {
+ Guitar::Fingering::Barre barre = fingering.getBarre();
+ barreStart = barre.start;
+ barreEnd = barre.end;
+ barreFret = barre.fret;
+ }
+
+ if (barreStart == 0) {
+ str << " s4*0^\\markup \\fret-diagram #\"";
+ } else {
+ str << " s4*0^\\markup \\override #'(barre-type . straight) \\fret-diagram #\"";
+ }
+ //
+ // Check each string individually.
+ // Note: LilyPond numbers strings differently.
+ //
+ for (int stringNum = 6; stringNum >= 1; --stringNum) {
+ if (barreStart == stringNum) {
+ str << "c:" << barreStart << "-" << barreEnd << "-" << barreFret << ";";
+ }
+
+ if (fingering.getStringStatus( 6-stringNum ) == Guitar::Fingering::MUTED) {
+ str << stringNum << "-x;";
+ } else if (fingering.getStringStatus( 6-stringNum ) == Guitar::Fingering::OPEN) {
+ str << stringNum << "-o;";
+ } else {
+ int stringStatus = fingering.getStringStatus(6-stringNum);
+ if ((stringNum <= barreStart) && (stringNum >= barreEnd)) {
+ str << stringNum << "-" << barreFret << ";";
+ } else {
+ str << stringNum << "-" << stringStatus << ";";
+ }
+ }
+ }
+ str << "\" ";
+
+ } catch (Exception e) { // GuitarChord ctor failed
+ RG_DEBUG << "Bad GuitarChord event in LilyPond export" << endl;
+ }
+ }
+
+ // LilyPond 2.0 introduces required postfix syntax for beaming
+ if (m_exportBeams && newBeamedGroup && notesInBeamedGroup > 0) {
+ str << "[ ";
+ newBeamedGroup = false;
+ }
+
+ if ((*i)->isa(Indication::EventType)) {
+ eventsToStart.insert(*i);
+ eventsInProgress.insert(*i);
+ }
+
+ ++i;
+ }
+
+ if (groupId != -1) {
+ if (groupType == GROUP_TYPE_TUPLED) {
+ if (m_exportBeams && notesInBeamedGroup > 0)
+ str << "] ";
+ str << "} ";
+ tupletRatio = std::pair<int, int>(1, 1);
+ } else if (groupType == GROUP_TYPE_BEAMED) {
+ if (m_exportBeams && notesInBeamedGroup > 0)
+ str << "] ";
+ }
+ }
+
+ if (isGrace == 1) {
+ isGrace = 0;
+ // str << "%{ grace ends %} "; // DEBUG
+ str << "} ";
+ }
+
+ if (lastStem != 0) {
+ str << "\\stemNeutral ";
+ }
+
+ if (overlong) {
+ str << std::endl << indent(col) <<
+ qstrtostr(QString("% %1").
+ arg(i18n("warning: overlong bar truncated here")));
+ }
+
+ if (fractionSmaller(durationRatioSum, barDurationRatio)) {
+ str << std::endl << indent(col) <<
+ qstrtostr(QString("% %1").
+ arg(i18n("warning: bar too short, padding with rests")));
+ str << std::endl << indent(col) <<
+ qstrtostr(QString("% %1/%2 < %3/%4").
+ arg(durationRatioSum.first).
+ arg(durationRatioSum.second).
+ arg(barDurationRatio.first).
+ arg(barDurationRatio.second))
+ << std::endl << indent(col);
+ durationRatio = writeSkip(timeSignature, writtenDuration,
+ (barEnd - barStart) - writtenDuration, true, str);
+ durationRatioSum = fractionSum(durationRatioSum,durationRatio);
+ }
+ //
+ // Export bar and bar checks.
+ //
+ if (nextBarIsDouble) {
+ str << "\\bar \"||\" ";
+ nextBarIsDouble = false;
+ } else if (nextBarIsEnd) {
+ str << "\\bar \"|.\" ";
+ nextBarIsEnd = false;
+ } else if (nextBarIsDot) {
+ str << "\\bar \":\" ";
+ nextBarIsDot = false;
+ } else if (MultiMeasureRestCount == 0) {
+ str << " |";
+ }
+}
+
+std::pair<int,int>
+LilyPondExporter::writeSkip(const TimeSignature &timeSig,
+ timeT offset,
+ timeT duration,
+ bool useRests,
+ std::ofstream &str)
+{
+ DurationList dlist;
+ timeSig.getDurationListForInterval(dlist, duration, offset);
+ std::pair<int,int> durationRatioSum(0,1);
+ std::pair<int,int> durationRatio(0,1);
+
+ int t = 0, count = 0;
+
+ for (DurationList::iterator i = dlist.begin(); ; ++i) {
+
+ if (i == dlist.end() || (*i) != t) {
+
+ if (count > 0) {
+
+ if (!useRests)
+ str << "\\skip ";
+ else if (t == timeSig.getBarDuration())
+ str << "R";
+ else
+ str << "r";
+
+ durationRatio = writeDuration(t, str);
+
+ if (count > 1) {
+ str << "*" << count;
+ durationRatio = fractionProduct(durationRatio,count);
+ }
+ str << " ";
+
+ durationRatioSum = fractionSum(durationRatioSum,durationRatio);
+ }
+
+ if (i != dlist.end()) {
+ t = *i;
+ count = 1;
+ }
+
+ } else {
+ ++count;
+ }
+
+ if (i == dlist.end())
+ break;
+ }
+ return durationRatioSum;
+}
+
+bool
+LilyPondExporter::handleDirective(const Event *textEvent,
+ std::string &lilyText,
+ bool &nextBarIsAlt1, bool &nextBarIsAlt2,
+ bool &nextBarIsDouble, bool &nextBarIsEnd, bool &nextBarIsDot)
+{
+ Text text(*textEvent);
+
+ if (text.getTextType() == Text::LilyPondDirective) {
+ std::string directive = text.getText();
+ if (directive == Text::Segno) {
+ lilyText += "^\\markup { \\musicglyph #\"scripts.segno\" } ";
+ } else if (directive == Text::Coda) {
+ lilyText += "^\\markup { \\musicglyph #\"scripts.coda\" } ";
+ } else if (directive == Text::Alternate1) {
+ nextBarIsAlt1 = true;
+ } else if (directive == Text::Alternate2) {
+ nextBarIsAlt1 = false;
+ nextBarIsAlt2 = true;
+ } else if (directive == Text::BarDouble) {
+ nextBarIsDouble = true;
+ } else if (directive == Text::BarEnd) {
+ nextBarIsEnd = true;
+ } else if (directive == Text::BarDot) {
+ nextBarIsDot = true;
+ } else {
+ // pass along less special directives for handling as plain text,
+ // so they can be attached to chords and whatlike without
+ // redundancy
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void
+LilyPondExporter::handleText(const Event *textEvent,
+ std::string &lilyText)
+{
+ try {
+
+ Text text(*textEvent);
+ std::string s = text.getText();
+
+ // only protect illegal chars if this is Text, rather than
+ // LilyPondDirective
+ if ((*textEvent).isa(Text::EventType))
+ s = protectIllegalChars(s);
+
+ if (text.getTextType() == Text::Tempo) {
+
+ // print above staff, bold, large
+ lilyText += "^\\markup { \\bold \\large \"" + s + "\" } ";
+
+ } else if (text.getTextType() == Text::LocalTempo ||
+ text.getTextType() == Text::Chord) {
+
+ // print above staff, bold, small
+ lilyText += "^\\markup { \\bold \"" + s + "\" } ";
+
+ } else if (text.getTextType() == Text::Dynamic) {
+
+ // supported dynamics first
+ if (s == "ppppp" || s == "pppp" || s == "ppp" ||
+ s == "pp" || s == "p" || s == "mp" ||
+ s == "mf" || s == "f" || s == "ff" ||
+ s == "fff" || s == "ffff" || s == "rfz" ||
+ s == "sf") {
+
+ lilyText += "-\\" + s + " ";
+
+ } else {
+ // export as a plain markup:
+ // print below staff, bold italics, small
+ lilyText += "_\\markup { \\bold \\italic \"" + s + "\" } ";
+ }
+
+ } else if (text.getTextType() == Text::Direction) {
+
+ // print above staff, large
+ lilyText += "^\\markup { \\large \"" + s + "\" } ";
+
+ } else if (text.getTextType() == Text::LocalDirection) {
+
+ // print below staff, bold italics, small
+ lilyText += "_\\markup { \\bold \\italic \"" + s + "\" } ";
+
+ // LilyPond directives that don't require special handling across
+ // barlines are handled here along with ordinary text types. These
+ // can be injected wherever they happen to occur, and should get
+ // attached to the right bits in due course without extra effort.
+ //
+ } else if (text.getText() == Text::Gliss) {
+ lilyText += "\\glissando ";
+ } else if (text.getText() == Text::Arpeggio) {
+ lilyText += "\\arpeggio ";
+ } else if (text.getText() == Text::Tiny) {
+ lilyText += "\\tiny ";
+ } else if (text.getText() == Text::Small) {
+ lilyText += "\\small ";
+ } else if (text.getText() == Text::NormalSize) {
+ lilyText += "\\normalsize ";
+ } else {
+ textEvent->get
+ <String>(Text::TextTypePropertyName, s);
+ std::cerr << "LilyPondExporter::write() - unhandled text type: "
+ << s << std::endl;
+ }
+ } catch (Exception e) {
+ std::cerr << "Bad text: " << e.getMessage() << std::endl;
+ }
+}
+
+void
+LilyPondExporter::writePitch(const Event *note,
+ const Rosegarden::Key &key,
+ std::ofstream &str)
+{
+ // Note pitch (need name as well as octave)
+ // It is also possible to have "relative" pitches,
+ // but for simplicity we always use absolute pitch
+ // 60 is middle C, one unit is a half-step
+
+ long pitch = 60;
+ note->get
+ <Int>(PITCH, pitch);
+
+ Accidental accidental = Accidentals::NoAccidental;
+ note->get
+ <String>(ACCIDENTAL, accidental);
+
+ // format of LilyPond note is:
+ // name + octave + (duration) + text markup
+
+ // calculate note name and write note
+ std::string lilyNote;
+
+ lilyNote = convertPitchToLilyNote(pitch, accidental, key);
+
+ str << lilyNote;
+
+ // generate and write octave marks
+ std::string octaveMarks = "";
+ int octave = (int)(pitch / 12);
+
+ // tweak the octave break for B# / Cb
+ if ((lilyNote == "bisis") || (lilyNote == "bis")) {
+ octave--;
+ } else if ((lilyNote == "ceses") || (lilyNote == "ces")) {
+ octave++;
+ }
+
+ if (octave < 4) {
+ for (; octave < 4; octave++)
+ octaveMarks += ",";
+ } else {
+ for (; octave > 4; octave--)
+ octaveMarks += "\'";
+ }
+
+ str << octaveMarks;
+}
+
+void
+LilyPondExporter::writeStyle(const Event *note, std::string &prevStyle,
+ int col, std::ofstream &str, bool isInChord)
+{
+ // some hard-coded styles in order to provide rudimentary style export support
+ // note that this is technically bad practice, as style names are not supposed
+ // to be fixed but deduced from the style files actually present on the system
+ const std::string styleMensural = "Mensural";
+ const std::string styleTriangle = "Triangle";
+ const std::string styleCross = "Cross";
+ const std::string styleClassical = "Classical";
+
+ // handle various note styles before opening any chord
+ // brackets
+ std::string style = "";
+ note->get
+ <String>(NotationProperties::NOTE_STYLE, style);
+
+ if (style != prevStyle) {
+
+ if (style == styleClassical && prevStyle == "")
+ return ;
+
+ if (!isInChord)
+ prevStyle = style;
+
+ if (style == styleMensural) {
+ style = "mensural";
+ } else if (style == styleTriangle) {
+ style = "triangle";
+ } else if (style == styleCross) {
+ style = "cross";
+ } else {
+ style = "default"; // failsafe default or explicit
+ }
+
+ if (!isInChord) {
+ str << std::endl << indent(col) << "\\override Voice.NoteHead #'style = #'" << style << std::endl << indent(col);
+ } else {
+ str << "\\tweak #'style #'" << style << " ";
+ }
+ }
+}
+
+std::pair<int,int>
+LilyPondExporter::writeDuration(timeT duration,
+ std::ofstream &str)
+{
+ Note note(Note::getNearestNote(duration, MAX_DOTS));
+ std::pair<int,int> durationRatio(0,1);
+
+ switch (note.getNoteType()) {
+
+ case Note::SixtyFourthNote:
+ str << "64"; durationRatio = std::pair<int,int>(1,64);
+ break;
+
+ case Note::ThirtySecondNote:
+ str << "32"; durationRatio = std::pair<int,int>(1,32);
+ break;
+
+ case Note::SixteenthNote:
+ str << "16"; durationRatio = std::pair<int,int>(1,16);
+ break;
+
+ case Note::EighthNote:
+ str << "8"; durationRatio = std::pair<int,int>(1,8);
+ break;
+
+ case Note::QuarterNote:
+ str << "4"; durationRatio = std::pair<int,int>(1,4);
+ break;
+
+ case Note::HalfNote:
+ str << "2"; durationRatio = std::pair<int,int>(1,2);
+ break;
+
+ case Note::WholeNote:
+ str << "1"; durationRatio = std::pair<int,int>(1,1);
+ break;
+
+ case Note::DoubleWholeNote:
+ str << "\\breve"; durationRatio = std::pair<int,int>(2,1);
+ break;
+ }
+
+ for (int numDots = 0; numDots < note.getDots(); numDots++) {
+ str << ".";
+ }
+ durationRatio = fractionProduct(durationRatio,
+ std::pair<int,int>((1<<(note.getDots()+1))-1,1<<note.getDots()));
+ return durationRatio;
+}
+
+void
+LilyPondExporter::writeSlashes(const Event *note, std::ofstream &str)
+{
+ // write slashes after text
+ // / = 8 // = 16 /// = 32, etc.
+ long slashes = 0;
+ note->get
+ <Int>(NotationProperties::SLASHES, slashes);
+ if (slashes > 0) {
+ str << ":";
+ int length = 4;
+ for (int c = 1; c <= slashes; c++) {
+ length *= 2;
+ }
+ str << length;
+ }
+}
+
+}