//============================================================================= // MusE Score // Linux Music Score Editor // // Copyright (C) 2007 Werner Schweer and others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //============================================================================= // // Lilypond export. // For HISTORY, NEWS and TODOS: see end of file (Olav). // // Some revisions by olagunde@start.no As I have no prior knowledge of // C or C++, I have to resort to techniques well known in standard // Pascal as it was known in 1983, when I got my very short // programming education in the "Programming for Poets" course. // I have written a primitive program that takes a full score output // from exportly and adds separate parts to it: // http://home.online.no/~olagu2/lyparts.cpp // It does not work with other lilypond files. // Olav. #include "libmscore/arpeggio.h" #include "libmscore/articulation.h" #include "libmscore/barline.h" #include "libmscore/beam.h" #include "libmscore/bracket.h" #include "libmscore/chord.h" #include "libmscore/clef.h" #include "config.h" #include "libmscore/dynamic.h" #include "libmscore/element.h" #include #include "libmscore/glissando.h" #include "globals.h" #include using std::cout; #include "libmscore/hairpin.h" #include "libmscore/harmony.h" #include "libmscore/key.h" #include "libmscore/keysig.h" #include "libmscore/lyrics.h" #include "libmscore/measure.h" #include "libmscore/note.h" #include "libmscore/ottava.h" #include "libmscore/page.h" #include "libmscore/part.h" #include "libmscore/pedal.h" #include "libmscore/pitchspelling.h" #include "libmscore/repeat.h" #include "libmscore/rest.h" #include "libmscore/score.h" #include "libmscore/segment.h" #include "libmscore/slur.h" #include "libmscore/staff.h" #include #include #include #include "libmscore/style.h" #include "libmscore/sym.h" #include "libmscore/tempotext.h" #include "libmscore/text.h" #include "libmscore/timesig.h" #include "libmscore/tremolo.h" #include "libmscore/tuplet.h" #include "libmscore/volta.h" #include "libmscore/marker.h" #include "libmscore/jump.h" #include "musescore.h" namespace Ms { static const int MAX_SLURS = 8; static const int BRACKSTAVES=64; static const int MAXPARTGROUPS = 8; static const int VERSES = 8; //--------------------------------------------------------- // ExportLy //--------------------------------------------------------- class ExportLy { Score* score; QFile f; QTextStream os; int level; // indent level int curTicks; MScore::Direction stemDirection; int indx; bool partial; //length of pickupbar int timedenom, z1, z2, z3, z4; //timesignatures int barlen, wholemeasurerest; QString wholemeasuretext; bool pickup; bool rehearsalnumbers; bool donefirst; //to prevent doing things in first ordinary bar which are already done in pickupbar bool graceswitch, gracebeam; int gracecount; int prevpitch, staffpitch, chordpitch; int measurenumber, lastind, taktnr, staffInd; bool repeatactive; bool firstalt,secondalt; enum voltatype { startending, endending, startrepeat, endrepeat, bothrepeat, doublebar, brokenbar, endbar, none }; struct voltareg { voltatype voltart; int barno; }; struct voltareg voltarray[255]; int tupletcount; bool pianostaff; bool slur; const Slur* slurre[MAX_SLURS]; bool started[MAX_SLURS]; int phraseslur; int slurstack; int findSlur(const Slur* s) const; const char* relativ, * staffrelativ; bool voiceActive[VOICES]; int prevElTick; bool ottvaswitch, jumpswitch; char privateRehearsalMark; struct lybrackets { bool piano; bool bracestart,brakstart, braceend, brakend; int braceno, brakno; }; struct lybrackets lybracks[BRACKSTAVES]; void bracktest(); struct staffnameinfo { QString voicename[VOICES]; QString staffid, partname, partshort; bool simultaneousvoices; int numberofvoices; }; struct staffnameinfo staffname[32]; QString cleannote, prevnote; struct InstructionAnchor // Even if it is exactly the same thing as "direction" of music-xml, // the word "instruction" is used in this file, so as not to cause // confusion with "direction" of the exportxml-file. { Element* instruct; // the element containing the instruction Element* anchor; // the element it is attached to bool start; // whether it is attached to start or end int tick; // the timestamp }; int nextAnchor; struct InstructionAnchor anker; struct InstructionAnchor anchors[1024]; struct glisstablelem { Chord* chord; int tick; QString glisstext; int type; }; int glisscount; struct glisstablelem glisstable[99]; QString voicebuffer; QTextStream out; QString scorebuffer; QTextStream scorout; // int numberofverses; bool nochord; int chordcount; void chordName(struct InstructionAnchor chordanchor); struct chordData { QString chrName; QString extName; int alt; QString bsnName; int bsnAlt; int ticklen; int tickpos; }; struct chordData thisHarmony; struct chordData prevHarmony; void resetChordData(struct chordData&); QString chord2Name(int ch); struct chordPost //element of a list to store hamonies. { struct chordData cd; struct chordPost* next; struct chordPost* prev; }; struct chordPost cp; struct chordPost* chordHead; struct chordPost* chordThis; // one lyricsRecord for each staff. Each record have room for VERSES // no. of verses. struct lyricsData { QString verselyrics[VERSES]; QString voicename[VERSES]; QString staffname; int tick[VERSES]; int segmentnumber[VERSES]; }; struct lyricsRecord { int numberofverses; struct lyricsData lyrdat; struct lyricsRecord* next; struct lyricsRecord* prev; }; // struct lyricsRecord * lyrrec; struct lyricsRecord* thisLyrics; struct lyricsRecord* headOfLyrics; struct lyricsRecord* tailOfLyrics; void storeChord(struct InstructionAnchor chAnk); void chordInsertList(chordPost*); void printChordList(); void cleanupChordList(); void writeFingering(int&, QString fingering[5]); void findLyrics(); void newLyricsRecord(); void cleanupLyrics(); void writeLyrics(); void connectLyricsToStaff(); void findGraceNotes(Note*,bool&, int); void setOctave(int&, int&, int (& foo)[12]); bool arpeggioTest(Chord* chord); bool glissandotest(Chord*); bool findNoteSymbol(Note*, QString&); void buildGlissandoList(int strack, int etrack); void writeStringInstruction(int&, QString stringarr[10]); void findFingerAndStringno(Note * note, int&, int&, QString(&finger)[5], QString(&strng)[10]); struct jumpOrMarkerLM { Marker* marker; int measurenum; bool start; }; int lastJumpOrMarker; struct jumpOrMarkerLM jumpOrMarkerList[100]; void writeLilyHeader(); void writeLilyMacros(); void writePageFormat(); void writeScoreTitles(); void initJumpOrMarkerLMs(); void resetJumpOrMarkerLM(struct jumpOrMarkerLM& mlm); void removeJumpOrMarkerLM(int); void preserveJumpOrMarker(Element*, int, bool); void printJumpOrMarker(int mnum, bool start); void anchortest(); void voltatest(); void jumptest(); void storeAnchor(struct InstructionAnchor); void initAnchors(); void removeAnchor(int); void resetAnchor(struct InstructionAnchor& ank); bool findMatchInMeasure(int, Staff*, Measure*, int, int, bool); bool findMatchInPart(int, Staff*, Score*, int, int, bool); void jumpAtMeasureStop(Measure*); void findMarkerAtMeasureStart(Measure*); void writeMeasuRestNum(); void writeTremolo(Chord*); void writeSymbol(QString); void tempoText(TempoText*); void words(Text*); void hairpin(Hairpin* hp, int tick); void ottava(Ottava* ot, int tick); void pedal(Pedal* pd, int tick); void dynamic(Dynamic*, int); void textLine(Element*, int, bool); void findTextProperties(Text*, QString&, int&); bool textspannerdown; // to avoid writing barlinecheck in the middle of a textspanner. bool textspanswitch; //from exportxml's class directionhandler: void buildInstructionListPart(int strack, int etrack); void buildInstructionList(Measure* m, int strack, int etrack); void handleElement(Element* el); void handlePreInstruction(Element* el); void instructionJump(Jump*); void instructionMarker(Marker*); QString primitiveJump(Jump*); QString primitiveMarker(Marker*); int checkJumpOrMarker(int, bool, Element*&); void writeCombinedMarker(int, Element*); QString flatInInstrName(QString); void indent(); //buffer-string void indentF(); //file int getLen(int ticks, int* dots); void writeLen(int); void writeChordLen(int ticks); QString tpc2name(int tpc); QString tpc2purename(int tpc); void writeScore(); void stemDir(Chord*); void writeVoiceMeasure(MeasureBase*, Staff*, int, int); void writeKeySig(int); void writeTimeSig(TimeSig*); void writeClef(int); void writeChord(Chord*, bool); void writeRest(int, int); void findVolta(); void findStartRepNoBarline(int& i, Measure*); void writeBarline(Measure*); int voltaCheckBar(Measure*, int); void writeVolta(int, int); void findTuplets(ChordRest*); void writeArticulation(ChordRest*); void writeScoreBlock(); void checkSlur(Chord*, bool); void doSlurStart(Chord*, bool); void doSlurStop(Chord*); void initBrackets(); void brackRegister(int, int, int, bool, bool); void findBrackets(); public: ExportLy(Score* s) { score = s; level = 0; curTicks = MScore::division; slur = false; stemDirection = MScore::AUTO; } bool write(const QString& name); }; //--------------------------------------------------------- // abs num value //--------------------------------------------------------- int numval(int num) { if (num < 0) { return -num; } return num; } //--------------------------------------------------------- // initBrackets -- init array of brackets and braces info //--------------------------------------------------------- void ExportLy::initBrackets() { for (int i = 0; i < BRACKSTAVES; ++i) { //init bracket-array lybracks[i].piano=false; lybracks[i].bracestart=false; lybracks[i].brakstart=false; lybracks[i].braceend=false; lybracks[i].brakend=false; lybracks[i].braceno=0; lybracks[i].brakno=0; } } //---------------------------------------------------------------- // brackRegister register where partGroup Start, and whether brace, // bracket or pianostaff. //---------------------------------------------------------------- void ExportLy::brackRegister(int brnumber, int bratype, int staffnr, bool start, bool end) { QString br = ""; switch (bratype) { case BRACKET_NORMAL: if (start) { lybracks[staffnr].brakstart=true; } if (end) { lybracks[staffnr].brakend=true; } lybracks[staffnr].brakno=brnumber; break; case BRACKET_BRACE: if (start) { lybracks[staffnr].bracestart=true; } if (end) { lybracks[staffnr].braceend=true; } lybracks[staffnr].braceno=brnumber; break; case -1: //piano-staff: lilypond makes rigid distance between //staffs to allow cross-staff beaming. lybracks[staffnr].piano=true; if (start) { lybracks[staffnr].bracestart=true; } if (end) { lybracks[staffnr].braceend=true; } lybracks[staffnr].braceno=brnumber; break; default: qDebug("bracket subtype %d not understood\n", bratype); } } //------------------------------------------------------------- // findBrackets // run thru parts and staffs to find start and end of braces and brackets //--------------------------------------------------------------- void ExportLy::findBrackets() { initBrackets(); char groupnumber; groupnumber=1; const QList& il = score->parts(); //list of parts for (int partnumber = 0; partnumber < il.size(); ++partnumber) { //run thru list of parts Part* part = il.at(partnumber); if (part->nstaves() == 2) { pianostaff=true; } for (int stavno = 0; stavno < part->nstaves(); stavno++) { //run thru list of staves in part. if (pianostaff) { if (stavno == 0) { brackRegister(groupnumber, -1, partnumber + stavno, true, false); } if (stavno == 1) { brackRegister(groupnumber, -1, partnumber + stavno, false, true); pianostaff=false; } } else { //not pianostaff Staff* st = part->staff(stavno); if (st) { for (int braclev= 0; braclev < st->bracketLevels(); braclev++) { //run thru bracketlevels of staff if (st->bracket(braclev) != NO_BRACKET) { //if bracket groupnumber++; if (groupnumber < MAXPARTGROUPS) { //brackRegister(bracketnumber, brackettype, staffnr, start, end) brackRegister(groupnumber, st->bracket(braclev), partnumber, true, false); brackRegister(groupnumber,st->bracket(braclev), partnumber - 1 + st->bracketSpan( braclev), false, true); } }//end of if bracket }//end of bracket-levels of staff }//end if staff } // end of else:not pianostaff }//end of stafflist }//end of parts-list }//end of findBrackets; void ExportLy::bracktest() { for (int i = 0; i < 10; i++) { qDebug("stavnr: %d braceno: %d brackno %d\n", i, lybracks[i].braceno, lybracks[i].brakno); } } //------------------------------------------------------- // instructionJump //-------------------------------------------------------- void ExportLy::instructionJump(Jump* jp) { JumpType jtp = jp->jumpType(); QString words = "\n \\once\\override Score.RehearsalMark #'self-alignment-X = #RIGHT \n "; if (jtp == JumpType::DC) { words += "\\mark \"Da capo\" "; } else if (jtp == JumpType::DC_AL_FINE) { words += "\\DCalfine "; } else if (jtp == JumpType::DC_AL_CODA) { words += "\\DCalcoda"; } else if (jtp == JumpType::DS_AL_CODA) { words += "\\DSalcoda"; } else if (jtp == JumpType::DS_AL_FINE) { words += "\\DSalfine"; } else if (jtp == JumpType::DS) { words += "\\mark \\markup{Dal segno \\raise #2 \\halign#-1 \\musicglyph #\"scripts.segno\"}"; } else { qDebug("jump type=%d not implemented\n", jtp); } out << words << " "; } //--------------------------------------------------------- // instructionMarker -- write marker //--------------------------------------------------------- void ExportLy::instructionMarker(Marker* m) { MarkerType mtp = m->markerType(); QString words = ""; if (mtp == MarkerType::CODA) { words = "\\theCoda "; } else if (mtp == MarkerType::CODETTA) { words = "\\codetta"; } else if (mtp == MarkerType::SEGNO) { words = "\\thesegno"; } else if (mtp == MarkerType::FINE) { words = "\\fine"; } else if (mtp == MarkerType::TOCODA) { words = "\\gotocoda "; } else if (mtp == MarkerType::VARCODA) { words = "\\varcodasign "; } else if (mtp == MarkerType::USER) { qDebug("unknown user marker\n"); } else { qDebug("marker type=%d not implemented\n", mtp); } out << words << " "; } //--------------------------------------------------------------------- // primitiveJump -- write jumpsign without macros: to be combined with // rehearsalmark //--------------------------------------------------------------------- QString ExportLy::primitiveJump(Jump* jp) { JumpType jtp = jp->jumpType(); QString words = ""; cout << "primitivejump\n"; if (jtp == JumpType::DC) { words = "Da capo"; } else if (jtp == JumpType::DC_AL_FINE) { words = "D.C. al fine"; } else if (jtp == JumpType::DC_AL_CODA) { words = "D.C. al coda"; } else if (jtp == JumpType::DS_AL_CODA) { words = "D.S. al coda"; } else if (jtp == JumpType::DS_AL_FINE) { words = "D.S. al fine"; } else if (jtp == JumpType::DS) { words = "Dal segnoX \\musicglyph #\"scripts.segno\""; } else { qDebug("jump type=%d not implemented\n", jtp); } return words; } //------------------------------------------------------------------ // primitiveMarker -- write marker without macros: to be combined // with rehearsalmark. //------------------------------------------------------------------ QString ExportLy::primitiveMarker(Marker* m) { MarkerType mtp = m->markerType(); QString words = ""; if (mtp == MarkerType::CODA) { //the coda // words = "\\line{\\halign #-0.75\\noBreak \\codaspace \\resumeStaff \\showClefKey \musicglyph #\"scripts.coda\" \\musicglyph #\"scripts.coda\"}"; words = "\\line{\\halign #-0.75 \\musicglyph #\"scripts.coda\" \\musicglyph #\"scripts.coda\"}"; } else if (mtp == MarkerType::CODETTA) { words = "\\line {\\musicglyph #\"scripts.coda\" \\hspace #-1.3 \\musicglyph #\"scripts.coda\"} } \n"; } else if (mtp == MarkerType::SEGNO) { words = "\\musicglyph #\"scripts.segno\""; } else if (mtp == MarkerType::FINE) { words = "{\"Fine\"} \\mark \\markup {\\musicglyph #\"scripts.ufermata\" } \\bar \"\bar \"||\" } \n"; } else if (mtp == MarkerType::TOCODA) { words = "\\musicglyph #\"scripts.coda\""; } else if (mtp == MarkerType::VARCODA) { words = "\\musicglyph#\"scripts.varcoda\""; } else if (mtp == MarkerType::USER) { qDebug("unknown user marker\n"); } else { qDebug("marker type=%d not implemented\n", mtp); } return words; } //--------------------------------------------------------- // symbol //--------------------------------------------------------- void ExportLy::writeSymbol(QString name) { // QString name = symbols[sym->sym()].name(); //this needs rewriting probably because of some rewriting of sym.cpp/sym.h // cout << "symbolname: " << name.toLatin1().data() << "\n"; if (wholemeasurerest > 0) { writeMeasuRestNum(); } if (name == "clef eight") { out << "^\\markup \\tiny\\roman 8 "; } else if (name == "pedal ped") { out << " \\sustainOn "; } else if (name == "pedalasterisk") { out << " \\sustainOff "; } else if (name == "scripts.trill") { out << "\\trill "; } else if (name == "scripts.flageolet") { out << "\\flageolet "; } else if (name == "rcomma") { out << "\\mark \\markup {\\musicglyph #\"scripts.rcomma\"} "; } else if (name == "lcomma") { out << "\\mark \\markup {\\musicglyph #\"scripts.lcomma\"} "; } else { out << "^\\markup{\\musicglyph #\"" << name << "\"} "; } // else if (name == "acc discant") // out << "^\\markup{\\musicglyph #\"accordion.accDiscant\"} "; // else if (name == "acc dot") // //we need to place the dot on the correct place within the discant // //and other base symbols. Is this possible in mscore? The entire // //example in lily manual "2.2.3 Accordion" must be input as a // //macro? // out << "^\\markup{\\musicglyph #\"accordion.accDot\"} "; // else if (name == "acc freebase") // out << "^\\markup{\\musicglyph #\"accordion.accFreebase\"} "; // else if (name == "acc stdbase") // out << "^\\markup{\\musicglyph #\"accordion.accStdbase\"} "; // else if (name == "acc bayanbase") // out << "^\\markup{\\musicglyph #\"accordion.accBayanbase\"} "; // else if (name == "acc old ee") // out << "^\\markup{\\musicglyph #\"accordion.accOldEE\"} "; // else // { // qDebug("ExportLy::symbol(): %s not supported\n", name.toLatin1().data()); // return; // } } void ExportLy::resetChordData(struct chordData& CD) { CD.chrName=""; CD.extName=""; CD.alt=0; CD.bsnName=""; CD.tickpos=0; CD.bsnAlt=0; CD.ticklen=0; } void ExportLy::cleanupChordList() { chordPost* next; chordThis = chordHead; if (chordThis == 0) { return; } next = chordThis->next; while (next != NULL) { next->prev = NULL; delete chordThis; chordThis = next; next = next->next; } delete chordThis; } void ExportLy::writeChordLen(int ticks) { int dots = 0; int len = getLen(ticks, &dots); switch (len) { case -5: os << "1*5/4"; // 5/4 break; case -4: os << "2*5 "; // 5/2 ?? break; case -3: os << "1.*2 "; break; case -2://longa 16/4 os << "1*4 "; break; case -1: //brevis 8/4 os << "1*2"; break; default: os << len; for (int i = 0; i < dots; ++i) { os << "."; } break; } } void ExportLy::printChordList() { chordThis = chordHead; if (chordThis == 0) { // ws return; } struct chordPost* next; next = chordThis->next; int i=0; int dots=0; int lilylen=0; while (next != NULL) { i++; //insert spacer rests before first chord: if ((i == 1) and (chordThis->cd.tickpos > 0)) { int factor=1; // works at least if denominator is 4: if (timedenom == 2) { factor=2; } else { factor = 1; } int measnum = chordThis->cd.tickpos / (z1 * MScore::division * factor); qDebug("Measnum chord: \n"); int surplus = chordThis->cd.tickpos % MScore::division; if (measnum == 0) { surplus = chordThis->cd.tickpos; } level++; indentF(); if (surplus > 0) { lilylen= getLen(surplus, &dots); level++; indentF(); os << "s" << lilylen; while (dots > 0) { os << "."; dots--; } os << " "; } if (measnum > 0) { os << "s1*" << measnum << " \n"; } }// end if firstone is not on tick 0 print spacer rest. else { //compute ticklen for the rest of the chords: chordThis->cd.ticklen = next->cd.tickpos - chordThis->cd.tickpos; chordThis=next; next=next->next; } }//while not end of list. if (next == NULL) { chordThis->cd.ticklen= 480; } chordThis = chordHead; next = chordThis; // i=0; indentF(); while (next != NULL) { next=next->next; dots=0; lilylen=0; i++; // lilylen = getLen(chordThis->cd.ticklen, &dots); os << chordThis->cd.chrName; curTicks=0; writeChordLen(chordThis->cd.ticklen); //<< lilylen; while (dots > 0) { os << "."; dots--; } if (chordThis->cd.extName != "") { os << ":" << chordThis->cd.extName; } if (chordThis->cd.bsnName != "") { os << "/" << chordThis->cd.bsnName; } if (chordThis->cd.bsnAlt > 0) { os << chordThis->cd.bsnAlt; } os << " "; chordThis=next; }//end of while chordthis... os << "}%%end of chordlist \n\n"; }//end of printChordList //----------------------------------------------------------- // chord2Name //----------------------------------------------------------- QString ExportLy::chord2Name(int ch) { const char names[] = "fcgdaeb"; return QString(names[(ch + 1) % 7]); } //---------------------------------------------------------- // chordInsertList //---------------------------------------------------------- void ExportLy::chordInsertList(chordPost* newchord) { if (chordHead == NULL) { //first element: make head of list. chordcount++; chordHead = newchord; newchord->prev = NULL; newchord->next = NULL; } else { //at least one previous existent element chordcount++; chordThis = chordHead; while ((newchord->cd.tickpos >= chordThis->cd.tickpos) && (chordThis->next != NULL)) { chordThis = chordThis->next; } if ((chordThis->next == NULL) && (chordThis->cd.tickpos <= newchord->cd.tickpos)) { //we have reached end of list //insert new element as tail chordThis->next = newchord; newchord->prev = chordThis; } else { //insert somewhere in the middle newchord->next = chordThis; newchord->prev = chordThis->prev; if (chordHead != chordThis) { chordThis = chordThis->prev; chordThis->next = newchord; } else { // the middle is immediately after head and before the tail. chordThis->prev = newchord; chordHead = newchord; } }//middle }//at least one previous }//end of chordInsertList //----------------------------------------------------------------- // storeChord //----------------------------------------------------------------- void ExportLy::storeChord(struct InstructionAnchor chordanchor) { cout << "chords!!!\n"; //first create new element chordPost* aux; aux = new chordPost(); resetChordData(aux->cd); aux->next = NULL; aux->prev = NULL; //then fill it Harmony* harmelm = (Harmony*)chordanchor.instruct; int chordroot = harmelm->rootTpc(); QString n, app; if (chordroot != INVALID_TPC) { if (nochord == true) { nochord = false; } aux->cd.chrName = chord2Name(chordroot); n=thisHarmony.chrName; aux->cd.tickpos = harmelm->parent()->type() == Element::SEGMENT ? static_cast(harmelm->parent())->tick() : 0; if (!harmelm->xmlKind().isEmpty()) { aux->cd.extName = harmelm->extensionName(); aux->cd.extName = aux->cd.extName.toLower(); } int alter = tpc2alter(chordroot); if (alter == 1) { app = "is"; } else { if (alter == -1) { if (n == "e") { app = "s"; } else { app = "es"; } } } aux->cd.chrName = aux->cd.chrName + app; int bassnote = harmelm->baseTpc(); if (bassnote != INVALID_TPC) { aux->cd.bsnName = chord2Name(bassnote); int alter = tpc2alter(bassnote); n=aux->cd.bsnName; if (alter == 1) { app = "is"; } else if (alter == -1) { if (n == "e") { app = "s"; } else { app = "es"; } } aux->cd.bsnName = n + app; aux->cd.bsnAlt=alter; } //end if bassnote //and at last insert it in list: chordInsertList(aux); }//end if chordroot else { storeAnchor(anker); } } //--------------------------------------------------------- // tempoText //--------------------------------------------------------- void ExportLy::tempoText(TempoText* text) { QString temptekst = text->text(); double met = text->tempo(); int metronome; metronome = (int)(met * 60); out << "\\tempo \"" << text->text() << "\" " << timedenom << " = " << metronome << " "; } //--------------------------------------------------------- // words //--------------------------------------------------------- void ExportLy::words(Text* text) { QString style; int size; findTextProperties(text,style,size); //todo: find exact mscore-position of text and not only anchorpoint, and position accordingly in lily. //TODO if ((text->subtypeName() != "RehearsalMark")) // if (text->text() != "") out << "^\\markup {" << style << " \"" << text->text() << "\"} "; // qDebug("tekst %s\n", tekst.toLatin1().data()); } //--------------------------------------------------------- // hairpin //--------------------------------------------------------- void ExportLy::hairpin(Hairpin* hp, int tick) { // print hairpin from anchorlist // todo: find exact mscore-position of // hairpin start and end and not only anchorpoint, and position // accordingly in lily. int art=2; art=hp->hairpinType(); if (hp->tick() == tick) { if (art == 0) { //diminuendo out << "\\< "; } if (art == 1) { //crescendo out << "\\> "; } if (art > 1) { out << "\\!x "; } } //TODO-WS if (hp->tick2() == tick) out << "\\! "; //end of hairpin } //--------------------------------------------------------- // start ottava //--------------------------------------------------------- void ExportLy::ottava(Ottava* ot, int tick) { int st = ot->ottavaType(); if (ot->tick() == tick) { switch (st) { case 0: out << "\\ottva "; break; case 1: out << "\\ottva \\once\\override TextSpanner #'(bound-details left text) = \"15va\" \n"; indent(); break; case 2: out << "\\ottvabassa "; break; case 3: out << "\\ottvabassa \\once \\override TextSpanner #'(bound-details left text) = \"15vb\" \n"; indent(); break; default: qDebug("ottava subtype %d not understood\n", st); } } else { out << "\\ottvaend "; } } //--------------------------------------------------------- // pedal //--------------------------------------------------------- void ExportLy::pedal(Pedal* pd, int tick) { if (pd->tick() == tick) { out << "\\sustainOn "; } else { out << "\\sustainOff "; } } //--------------------------------------------------------- // dynamic //--------------------------------------------------------- void ExportLy::dynamic(Dynamic* dyn, int nop) { QString t = dyn->text(); if (t == "p" || t == "pp" || t == "ppp" || t == "pppp" || t == "ppppp" || t == "pppppp" || t == "f" || t == "ff" || t == "fff" || t == "ffff" || t == "fffff" || t == "ffffff" || t == "mp" || t == "mf" || t == "sf" || t == "sfp" || t == "sfpp" || t == "fp" || t == "rf" || t == "rfz" || t == "sfz" || t == "sffz" || t == "fz" || t == "sff") { switch (nop) { case 0: out << "\\" << t << " "; break; case 1: out << "_\\markup\{\\dynamic " << t.toLatin1().data() << " \\halign #-2 "; break; case 2: out << " \\dynamic " << t.toLatin1().data() << " } "; break; default: out << "\\" << t.toLatin1().data() << " "; break; } } else if (t == "m" || t == "z") { out << "\\" << t.toLatin1().data() << " "; } else { out << "_\\markup{\"" << t.toLatin1().data() << "\"} "; } }//end dynamic //----------------------------------------------------------------------------------- // findTextProperties //----------------------------------------------------------------------------------- void ExportLy::findTextProperties(Text* tekst, QString& tekststyle, int& fontsize) { QFont fontprops=tekst->font(); fontsize= fontprops.pointSizeF(); switch (fontprops.style()) { case QFont::StyleNormal: tekststyle = "\\upright "; break; case QFont::StyleItalic: case QFont::StyleOblique: tekststyle = "\\italic"; break; default: tekststyle = "\\upright "; break; } switch (fontprops.weight()) { case QFont::Light: case QFont::Normal: break; case QFont::DemiBold: case QFont::Bold: case QFont::Black: tekststyle += "\\bold "; break; default: break; } }//end findTextProperties //--------------------------------------------------------- // textLine //--------------------------------------------------------- void ExportLy::textLine(Element* instruction, int tick, bool pre) { qDebug("textline\n"); QString rest; QPointF point; QString lineEnd = "none"; QString type; // int lineoffset; QString lineType; // SLine* sl = (SLine*) instruction; int fontsize=0; TextLine* tekstlinje = (TextLine*)instruction; bool post = false; if (pre == false) { post = true; } //start of line: if (tekstlinje->tick() == tick) { if (pre) { switch (tekstlinje->lineStyle()) { case Qt::DashDotLine: case Qt::DashDotDotLine: case Qt::DashLine: out << " \\once\\override TextSpanner #'style = #'dashed-line \n"; indent(); break; case Qt::DotLine: out << " \\once\\override TextSpanner #'style = #'dotted-line \n"; indent(); break; default: break; } if (tekstlinje->endHook()) { double h = tekstlinje->endHookHeight().val(); if (h < 0.0) { out << "\\once\\override TextSpanner #'(bound-details right text) = \\markup{ \\draw-line #'(0 . 1) }\n"; indent(); } else { out << "\\once\\override TextSpanner #'(bound-details right text) = \\markup{ \\draw-line #'(0 . -1) }\n"; indent(); } } if (tekstlinje->beginText()) { QString linetext = tekstlinje->beginText()->text(); Text* tekst = (Text*)tekstlinje->beginText(); QString tekststyle = ""; findTextProperties(tekst, tekststyle, fontsize); out << "\\once\\override TextSpanner #'(bound-details left text) = \\markup{"; out << tekststyle << "\"" << linetext << "\"} \n"; indent(); } point = tekstlinje->frontSegment()->userOff(); if (point.y() > 0.0) { //below out << "\\textSpannerDown "; textspannerdown=true; } else if (textspannerdown) { out << "\\textSpannerNeutral "; textspannerdown = false; } }// end if pre else if (post) { //after note, start of line. out << "\\startTextSpan "; textspanswitch=true; }// end if post: after note, start of textline }//end of start-of-textline #if 0 // TODO-WS else if (sl->tick2() == tick) { //at end of textline. if (pre) { out << "\\stopTextSpan "; textspanswitch=false; // from exportxml.cpp: output of user offset from anchor: // userOff2 is relative to userOff in MuseScore // point = tekstlinje->lineSegments().last()->userOff2() + tekstlinje->lineSegments().first()->userOff(); // lineoffset = tekstlinje->mxmlOff2(); } else if (post) { //just relax for the moment. } }// end if tick2() #endif }// end of textLine() //--------------------------------------------------------------------- // anchortest //--------------------------------------------------------------------- void ExportLy::anchortest() { int i; for (i=0; i < nextAnchor; i++) { Element* instruction = anchors[i].instruct; ElementType instructiontype = instruction->type(); // Text* text = (Text*) instruction; qDebug("anker nr: %d ", i); switch (instructiontype) { case Element::STAFF_TEXT: qDebug("STAFF_TEXT "); //TODO if (text->subtypeName()== "RehearsalMark") qDebug(" rehearsal STAFF "); qDebug("\n"); break; case Element::TEXT: qDebug("TEXT "); // if (text->subtypeName()== "RehearsalMark") qDebug(" rehearsal MEASURE"); qDebug("\n"); break; case Element::MARKER: qDebug("MARKER\n"); break; case Element::JUMP: qDebug("JUMP\n"); break; case Element::SYMBOL: qDebug("SYMBOL\n"); break; case Element::TEMPO_TEXT: qDebug("TEMPOTEXT MEASURE\n"); break; case Element::DYNAMIC: qDebug("Dynamic\n"); break; case Element::HARMONY: qDebug("akkordnavn. \n"); break; case Element::HAIRPIN: qDebug("hairpin \n"); break; case Element::PEDAL: qDebug("pedal\n"); break; case Element::TEXTLINE: qDebug("textline\n"); break; case Element::OTTAVA: qDebug("ottava\n"); break; default: break; } } qDebug("Anchortest finished\n"); }//end anchortest //--------------------------------------------------------------------- // jumptest //--------------------------------------------------------------------- void ExportLy::jumptest() { qDebug("at jumptest A lastjump %d\n", lastJumpOrMarker); int i; for (i=0; i < lastJumpOrMarker; i++) { qDebug("jumptest 1\n"); Element* merke = jumpOrMarkerList[i].marker; qDebug("jumptest 2\n"); ElementType instructiontype = merke->type(); qDebug("jumptest 3\n"); // Text* text = (Text*) merke; qDebug("jumptest 4\n"); qDebug("marker nr: %d ", i); switch (instructiontype) { case Element::STAFF_TEXT: qDebug("STAFF_TEXT "); // if (text->subtypeName()== "RehearsalMark") qDebug(" rehearsal "); qDebug("\n"); break; case Element::TEXT: qDebug("TEXT "); // if (text->subtypeName()== "RehearsalMark") qDebug(" rehearsal "); qDebug("\n"); break; case Element::MARKER: qDebug("MARKER\n"); break; case Element::JUMP: qDebug("JUMP\n"); break; case Element::SYMBOL: qDebug("SYMBOL\n"); break; case Element::TEMPO_TEXT: qDebug("TEMPOTEXT MEASURE\n"); break; case Element::DYNAMIC: qDebug("Dynamic\n"); break; case Element::HARMONY: qDebug("akkordnavn. \n"); break; case Element::HAIRPIN: qDebug("hairpin \n"); break; case Element::PEDAL: qDebug("pedal\n"); break; case Element::TEXTLINE: qDebug("textline\n"); break; case Element::OTTAVA: qDebug("ottava\n"); break; default: break; } } }//end jumptest //-------------------------------------------------------- // initAnchors //-------------------------------------------------------- void ExportLy::initAnchors() { int i; for (i=0; i < 1024; i++) { resetAnchor(anchors[i]); } } //-------------------------------------------------------- // resetAnchor //-------------------------------------------------------- void ExportLy::resetAnchor(struct InstructionAnchor& ank) { ank.instruct=0; ank.anchor=0; ank.start=false; ank.tick=0; } //--------------------------------------------------------- // deleteAnchor //--------------------------------------------------------- void ExportLy::removeAnchor(int ankind) { int i; resetAnchor(anchors[ankind]); for (i=ankind; i <= nextAnchor; i++) { anchors[i]=anchors[i + 1]; } resetAnchor(anchors[nextAnchor]); nextAnchor=nextAnchor - 1; } //--------------------------------------------------------- // storeAnchor //--------------------------------------------------------- void ExportLy::storeAnchor(struct InstructionAnchor a) { if (nextAnchor < 1024) { anchors[nextAnchor++] = a; } else { qDebug("InstructionHandler: too many instructions\n"); } resetAnchor(anker); } void ExportLy::writeCombinedMarker(int foundJoM, Element* elm) { out << "\\mark\\markup\\column \{ \n"; level++; indent(); if (foundJoM == Element::MARKER) { QString primark = primitiveMarker((Marker*)elm); out << primark << "\n"; } if (foundJoM == Element::JUMP) { out << primitiveJump((Jump*)elm) << "\n"; } indent(); out << "\\box\\bold \""; if (rehearsalnumbers) { out << (int)privateRehearsalMark; //+n } else { out << privateRehearsalMark << "\" \n"; } indent(); level--; indent(); out << "} \n"; indent(); out << "\\set Score.rehearsalMark = #" << (int)privateRehearsalMark - 63 << "\n"; indent(); } //----------------------------------------------------------------- // handlePreInstruction -- handle the instructions attached to one // specific element and which are to be exported BEFORE the element // itself. // ----------------------------------------------------------------- void ExportLy::handlePreInstruction(Element* el) { int i = 0; int foundJoM = 0; Text* tekst; for (i = 0; i <= nextAnchor; i++) { //run thru anchorlist if ((anchors[i].anchor != 0) && (anchors[i].anchor == el)) { Element* instruction = anchors[i].instruct; ElementType instructiontype = instruction->type(); switch (instructiontype) { case Element::STAFF_TEXT: case Element::REHEARSAL_MARK: { tekst = (Text*)instruction; if (wholemeasurerest >= 1) { writeMeasuRestNum(); } bool ok = false; // int dec=0; QString c; c=tekst->text(); // dec = c.toInt(&ok, 10); if (ok) { rehearsalnumbers=true; } Element* elm = 0; foundJoM = checkJumpOrMarker(measurenumber, true, elm); //true means at the start of measure. if (foundJoM) { writeCombinedMarker(foundJoM,elm); } else { out << "\\mark\\default ";//xxx } privateRehearsalMark++; removeAnchor(i); //to use this caused trouble at another place. Maybe remove? break; } case Element::OTTAVA: if (wholemeasurerest >= 1) { writeMeasuRestNum(); } ottvaswitch=true; ottava((Ottava*)instruction, anchors[i].tick); removeAnchor(i); break; case Element::TEMPO_TEXT: tempoText((TempoText*)instruction); removeAnchor(i); break; case Element::TEXTLINE: textLine(instruction, anchors[i].tick, true); break; default: break; }//end switch }//end if anchors }//end for (i...) }//End of handlePreInstructiion //--------------------------------------------------------- // handleElement -- handle all instructions attached to one specific // element and which are to be exported AFTER the element itself. //--------------------------------------------------------- void ExportLy::handleElement(Element* el) { int i = 0; Symbol* sym; QString name; for (i = 0; i <= nextAnchor; i++) {//run thru filled part of list if (anchors[i].anchor != 0 and anchors[i].anchor == el) { // if anchored to this element Element* instruction = anchors[i].instruct; ElementType instructiontype = instruction->type(); switch (instructiontype) { case Element::MARKER: qDebug("MARKER\n"); instructionMarker((Marker*)instruction); break; case Element::JUMP: qDebug("JUMP\n"); instructionJump((Jump*)instruction); break; case Element::SYMBOL: { cout << "symbol in anchorlist tick: " << anchors[i].tick << " \n"; sym = (Symbol*)instruction; name = Sym::id2name(sym->sym()); writeSymbol(name); break; } case Element::TEMPO_TEXT: // qDebug("TEMPOTEXT MEASURE\n"); // tempoText((TempoText*) instruction); break; case Element::STAFF_TEXT: case Element::TEXT: cout << "anchored text \n"; if (wholemeasurerest) { Text* wmtx = (Text*)instruction; wholemeasuretext = wmtx->text(); } else { words((Text*)instruction); } break; case Element::DYNAMIC: { int nextorprev=0; if ((anchors[i + 1].anchor != 0) and (anchors[i + 1].anchor == el)) { Element* nextinstruct = anchors[i + 1].instruct; ElementType nextinstrtype = nextinstruct->type(); if (nextinstrtype == Element::DYNAMIC) { nextorprev = 1; } } else if ((anchors[i - 1].anchor != 0) and (anchors[i - 1].anchor == el)) { Element* previnstruct = anchors[i - 1].instruct; ElementType previnstrtype = previnstruct->type(); if (previnstrtype == Element::DYNAMIC) { nextorprev=2; } } dynamic((Dynamic*)instruction, nextorprev); break; } case Element::HAIRPIN: hairpin((Hairpin*)instruction, anchors[i].tick); break; case Element::HARMONY: words((Text*)instruction); break; case Element::PEDAL: pedal((Pedal*)instruction, anchors[i].tick); break; case Element::TEXTLINE: textLine(instruction, anchors[i].tick, false); break; case Element::OTTAVA: break; default: qDebug("post-InstructionHandler::handleElement: direction type %s at tick %d not implemented\n", Element::name(instruction->type()), anchors[i].tick); break; } // removeAnchor(i); } } //foreach position i anchor-array. } //-------------------------------------------------------- // resetMarkerLM //-------------------------------------------------------- void ExportLy::resetJumpOrMarkerLM(struct jumpOrMarkerLM& mlm) { mlm.marker=0; mlm.measurenum=0; mlm.start=false; } //-------------------------------------------------------- // initMarkerLMs //-------------------------------------------------------- void ExportLy::initJumpOrMarkerLMs() { int i; for (i=0; i < 100; i++) { resetJumpOrMarkerLM(jumpOrMarkerList[i]); } } //--------------------------------------------------------- // removeMarkerLM -- not used ?! //--------------------------------------------------------- void ExportLy::removeJumpOrMarkerLM(int markerind) { int i; resetJumpOrMarkerLM(jumpOrMarkerList[markerind]); for (i=markerind; i <= nextAnchor; i++) { jumpOrMarkerList[i]=jumpOrMarkerList[i + 1]; } resetJumpOrMarkerLM(jumpOrMarkerList[lastJumpOrMarker]); lastJumpOrMarker=lastJumpOrMarker - 1; } //--------------------------------------------------------------------- // preserveJumpOrMark //--------------------------------------------------------------------- void ExportLy::preserveJumpOrMarker(Element* dir, int mnum, bool start) { jumpswitch=true; jumpOrMarkerLM mlm; Marker* ma = (Marker*)dir; mlm.marker = ma; mlm.measurenum = mnum; mlm.start = start; if (lastJumpOrMarker < 100) { lastJumpOrMarker++; jumpOrMarkerList[lastJumpOrMarker] = mlm; } else { qDebug("PreserveMarker: Too many marksorjumps\n"); } } //-------------------------------------------------------------------- // checkJumpOrMarker //--------------------------------------------------------------------- int ExportLy::checkJumpOrMarker(int mnum, bool start, Element*& moj) { cout << "checkjumpormarker\n"; int tp=0; int i=0; if (start) { mnum--; //we place these things at the end of the previous measure } cout << "mnum: " << mnum << "\n"; while (jumpOrMarkerList[i].measurenum < mnum) { ++i; if (jumpOrMarkerList[i].measurenum == 0) { goto endofcheck; } } while ((jumpOrMarkerList[i].measurenum == mnum) and (i < 100)) { cout << "found measure " << jumpOrMarkerList[i].start << "\n"; if (jumpOrMarkerList[i].start == true) { moj = jumpOrMarkerList[i].marker; tp = moj->type(); cout << "moj->type: " << tp << "\n"; } i++; cout << i << "\n"; // if (i >= 100) // break; } endofcheck: cout << "checkjumpormarker, type: " << tp << "\n"; return tp; } //-------------------------------------------------------------------- // printJumpOrMarker //--------------------------------------------------------------------- void ExportLy::printJumpOrMarker(int mnum, bool start) { cout << "printjumpormarker 1\n"; int i=0; while (jumpOrMarkerList[i].measurenum < mnum) { i++; } cout << "test 2\n"; while ((jumpOrMarkerList[i].measurenum == mnum) and (i < 100)) { cout << "test 3\n"; if (jumpOrMarkerList[i].start == start) { cout << "test 4\n"; Element* moj = jumpOrMarkerList[i].marker; int tp = moj->type(); if (tp == Element::MARKER) { cout << "test 5\n"; Marker* ma = (Marker*)moj; instructionMarker(ma); } else if (tp == Element::JUMP) { cout << "test 6\n"; Jump* jp = (Jump*)moj; instructionJump(jp); } cout << "test 7\n"; } i++; cout << i << "\n"; // if (i >= 100) // break; } cout << "test 8\n"; } //--------------------------------------------------------------------- // findMarkerAtMeasureStart //--------------------------------------------------------------------- void ExportLy::findMarkerAtMeasureStart(Measure* m) { for (auto ci = m->el()->begin(); ci != m->el()->end(); ++ci) { Element* dir = *ci; int tp = dir->type(); if (tp == Element::MARKER) { //only markers, not jumps, are used at measure start. Marker* ma = (Marker*)dir; MarkerType mtp = ma->markerType(); //discard markers which belong at measure end: if (!(mtp == MarkerType::FINE || mtp == MarkerType::TOCODA)) { cout << "marker found at measure: " << measurenumber << "\n"; // instructionMarker(ma); preserveJumpOrMarker(dir, measurenumber, true); //true means start of measure } } } } //--------------------------------------------------------- // jumpAtMeasureStop -- write jumps at end of measure //--------------------------------------------------------- void ExportLy::jumpAtMeasureStop(Measure* m) { // loop over all measure relative elements in this measure // looking for JUMPS and MARKERS for (auto ci = m->el()->begin(); ci != m->el()->end(); ++ci) { Element* dir = *ci; int tp = dir->type(); bool end; // start; // start=true; end=false; if (tp == Element::JUMP) { // all jumps are handled at measure end Jump* jp = (Jump*)dir; //writing the jump-mark in part one of the score: instructionJump(jp); // in mscore jumps and markers are found only in the // first staff. If it shall be possible to extract // parts from the exported lilypond-score, jumps and // markers must be inserted in each and every part. We // will hence have to preserve those elements in a list // to be used when we write the parts other than the // first in our lilypond-score: preserveJumpOrMarker(dir, measurenumber, end); } else if (tp == Element::MARKER) { Marker* ma = (Marker*)dir; MarkerType mtp = ma->markerType(); //only print markers which belong at measure end: if (mtp == MarkerType::FINE || mtp == MarkerType::TOCODA) { //print the marker in part one instructionMarker(ma); //preserve the marker for later use in other parts: preserveJumpOrMarker(dir, measurenumber, end); } } } } //--------------------------------------------------------- // findMatchInMeasure -- find chord or rest in measure // starting or ending at tick //--------------------------------------------------------- bool ExportLy::findMatchInMeasure(int tick, Staff* stf, Measure* m, int strack, int etrack, bool rehearsalmark) { int iter=0; bool found = false; for (int st = strack; st < etrack; ++st) { for (Segment* seg = m->first(); seg; seg = seg->next()) { iter++; Element* el = seg->element(st); if (!el) { continue; } if ((el->isChordRest()) and ((el->staff() == stf) or (rehearsalmark == true)) && ((seg->tick() >= tick))) { if (seg->tick() > tick) { tick=prevElTick; } anker.anchor=el; found=true; anker.tick=tick; anker.start=true; goto fertig; } prevElTick = seg->tick(); } } fertig: return found; } //--------------------------------------------------------- // findMatchInPart -- find chord or rest in part // starting or ending at tick //--------------------------------------------------------- bool ExportLy::findMatchInPart(int tick, Staff* stav, Score* sc, int starttrack, int endtrack, bool rehearsalmark) { bool found=false; for (MeasureBase* mb = sc->measures()->first(); mb; mb = mb->next()) { if (mb->type() != Element::MEASURE) { continue; } Measure* m = (Measure*)mb; found = findMatchInMeasure(tick, stav, m, starttrack, endtrack, rehearsalmark); if (found) { break; } } return found; } //--------------------------------------------------------- // buildInstructionList -- associate instruction (measure relative elements) // with elements in segments to enable writing at the correct position // in the output stream. Called once for every part to handle all part-level elements. //--------------------------------------------------------- void ExportLy::buildInstructionListPart(int strack, int etrack) { // part-level elements stored in the score layout: at the global level prevElTick=0; #if 0 // TODO-WS implementation changed foreach (Element* instruction, * (score->gel())) { bool found=false; bool rehearsalm=false; switch (instruction->type()) { case Element::JUMP: //TODO-WS qDebug("score JUMP found at tick: %d\n", instruction->tick()); break; case Element::MARKER: /*TODO-WS qDebug("score MARKER found at tick: %d\n", instruction->tick()); */break; case Element::HAIRPIN: case Element::HARMONY: case Element::OTTAVA: case Element::PEDAL: case Element::DYNAMIC: case Element::TEXT: case Element::TEXTLINE: { SLine* sl = (SLine*)instruction; Text* tekst = (Text*)instruction; // if (tekst->subtypeName() == "System") qDebug("Systemtekst in part\n"); // if (tekst->subtypeName() == "Staff") qDebug("Stafftest in part\n"); if (tekst->subtypeName() == "RehearsalMark") { rehearsalm=true; qDebug("found rehearsalmark in part\n"); } //start of instruction: found=findMatchInPart(sl->tick(), sl->staff(), score, strack, etrack, rehearsalm); if (found) { anker.instruct=instruction; storeAnchor(anker); } //end of instruction: //TODO-WS found=findMatchInPart(sl->tick2(), sl->staff(), score, strack, etrack, rehearsalm); if (found) { anker.instruct=instruction; storeAnchor(anker); } break; } //end textline default: // all others ignored // qDebug(" instruction type %s not implemented\n", Element::name(instruction->type())); break; } }// end foreach element.... #endif // part-level elements stored in measures: for (MeasureBase* mb = score->measures()->first(); mb; mb = mb->next()) { if (mb->type() != Element::MEASURE) { continue; } Measure* m = (Measure*)mb; buildInstructionList(m, strack, etrack); } }//end: buildInstructionList //--------------------------------------------------------- // buildInstructionList -- associate instruction (measure relative elements) // with elements in segments to enable writing at the correct position // in the output stream. Called once for every measure to handle either // part-level or measure-level elements. //--------------------------------------------------------- void ExportLy::buildInstructionList(Measure* m, int strack, int etrack) { // loop over all measure relative elements in this measure for (auto ci = m->el()->begin(); ci != m->el()->end(); ++ci) { bool found=false; // bool rehearsal=false; Element* instruction = *ci; switch (instruction->type()) { case Element::DYNAMIC: case Element::SYMBOL: case Element::TEMPO_TEXT: case Element::TEXT: case Element::HAIRPIN: //case Element::HARMONY: case Element::OTTAVA: case Element::PEDAL: case Element::STAFF_TEXT: #if 0 // TODO-WS { // if (instruction->subtypeName() == "Staff") qDebug("stafftekst i measure\n"); // if (instruction->subtypeName() == "System") qDebug("systemtekst i measure\n"); if (instruction->subtypeName() == "RehearsalMark") { rehearsal=true; } found = findMatchInMeasure(instruction->tick(), instruction->staff(), m, strack, etrack, rehearsal); if (found) { anker.instruct=instruction; storeAnchor(anker); } } #endif break; case Element::HARMONY: { Harmony* h = static_cast(instruction); int tick = h->parent()->type() == Element::SEGMENT ? static_cast(h->parent())->tick() : 0; found = findMatchInMeasure(tick, instruction->staff(), m, strack, etrack, false); if ((found) && (staffInd == 0)) { //only save chords in first staff. anker.instruct=instruction; storeChord(anker); resetAnchor(anker); } break; } default: break; } } }// end buildinstructionlist(measure) void ExportLy::buildGlissandoList(int strack, int etrack) { //seems to be overkill to go thru entire score first to find //glissandos. Alternative would be to back up to the previous chord //in writeChordMeasure(). But I don't know how to do that. So I steal the //buildinstructionlist-functions to make a parallel //buildglissandolist-function. (og) for (MeasureBase* mb = score->measures()->first(); mb; mb = mb->next()) { if (mb->type() != Element::MEASURE) { continue; } Measure* m = (Measure*)mb; for (int st = strack; st < etrack; ++st) { for (Segment* seg = m->first(); seg; seg = seg->next()) { Element* el = seg->element(st);//(st); if (!el) { continue; } if (el->type() == Element::CHORD) { Chord* cd = (Chord*)el; if (cd->glissando()) { glisscount++; //this may cause trouble in multistaff-scores (??): Element* prevel = seg->prev()->element(st); //(st); Chord* prevchord = (Chord*)prevel; glisstable[glisscount].chord = prevchord; glisstable[glisscount].type = int(cd->glissando()->glissandoType()); glisstable[glisscount].glisstext = cd->glissando()->text(); glisstable[glisscount].tick = prevchord->tick(); } } } } } } //--------------------------------------------------------- // indent -- scorebuffer //--------------------------------------------------------- void ExportLy::indent() { for (int i = 0; i < level; ++i) { out << " "; } } //--------------------------------------------------------- // indent -- outputfile //--------------------------------------------------------- void ExportLy::indentF() { for (int i = 0; i < level; ++i) { os << " "; } } //------------------------------------- // Find tuplets Note //------------------------------------- void ExportLy::findTuplets(ChordRest* cr) { Tuplet* t = cr->tuplet(); if (t) { if (tupletcount == 0) { int actNotes = t->ratio().numerator(); int nrmNotes = t->ratio().denominator(); int baselength = t->duration().ticks() / nrmNotes; int thislength = cr->duration().ticks(); tupletcount = nrmNotes * baselength - thislength; out << "\\times " << nrmNotes << "/" << actNotes << "{"; } else if (tupletcount > 1) { int thislength = cr->duration().ticks(); tupletcount = tupletcount - thislength; if (tupletcount == 0) { tupletcount = -1; } } } } //----------------------------------------------------- // voltaCheckBar // // supplements findVolta and called from there: check barlinetypes in // addition to endings //------------------------------------------------------ int ExportLy::voltaCheckBar(Measure* meas, int i) { int barlinetype = meas->endBarLineType(); switch (barlinetype) { case START_REPEAT: i++; voltarray[i].voltart=startrepeat; voltarray[i].barno=taktnr; break; case END_REPEAT: i++; voltarray[i].voltart=endrepeat; voltarray[i].barno=taktnr; break; case END_START_REPEAT: i++; voltarray[i].voltart=bothrepeat; voltarray[i].barno=taktnr; break; case END_BAR: i++; voltarray[i].voltart=endbar; voltarray[i].barno=taktnr; break; case DOUBLE_BAR: i++; voltarray[i].voltart=doublebar; voltarray[i].barno=taktnr; break; case BROKEN_BAR: case DOTTED_BAR: i++; voltarray[i].voltart=brokenbar; voltarray[i].barno=taktnr; break; default: break; }//switch // find startrepeat which does not exist as endbarline: If // startrepeat is at beginning of line, and endrepeatbar ends this // first measure of the line, repeatFlag is not set to RepeatStart, // then this does not help, and I need "findStartRepNoBarline" if (meas->repeatFlags() == RepeatStart) { // we have to exclude startrepeats found as endbarlines in previous measure if ((voltarray[i].barno != taktnr - 1) and (voltarray[i].voltart != startrepeat) and (voltarray[i].voltart != bothrepeat)) { i++; voltarray[i].voltart=startrepeat; voltarray[i].barno=taktnr - 1; //set as last element in previous measure. } } return i; }//end voltacheckbarline //------------------------------------------------------------------------ // findStartRepNoBarline // helper routine for findVolta. //------------------------------------------------------------------------ void ExportLy::findStartRepNoBarline(int& i, Measure* m) { // loop over all measure relative segments in this measure for (Segment* seg = m->first(); seg; seg = seg->next()) { if (seg->segmentType() == SegmentType::StartRepeatBarLine) { i++; // insert at next slot of voltarray voltarray[i].voltart = startrepeat; voltarray[i].barno = taktnr - 1; break; } } } //------------------------------------------------------------------ // findVolta -- find and register volta and repeats in entire piece, // register them in voltarray for later use in writeVolta. //------------------------------------------------------------------ void ExportLy::findVolta() { taktnr=0; lastind=0; int i=0; for (i=0; i < 255; i++) { voltarray[i].voltart=none; voltarray[i].barno=0; } i=0; for (MeasureBase* m=score->first(); m; m=m->next()) {// for all measures if (m->type() != Element::MEASURE) { continue; } ++taktnr; //should really not be incremented in case of pickupbars. //needed because of problems with repeatflag and because there //are no readymade functions for finding startbarlines, and //because startbarlines are not at the global level: Measure* meas = (Measure*)m; findStartRepNoBarline(i,meas); #if 0 // TODO-WS implementation changed foreach (Element* el, * (m->score()->gel())) { //for each element at the global level relevant for this measure if (el->type() == Element::VOLTA) { Volta* v = (Volta*)el; if (v->tick() == m->tick()) { //If we are at the beginning of the measure i++; // if (v->subtype() == Volta::VOLTA_CLOSED) // { // Lilypond has second volta closed for all kinds of thin-tick or tick-thin double bars // with or without repeat dots. But not for thin-thin double bar or single barline. // The only way I know of to make volta closed for thin-thin double bar // and single bar is to put the following lines in the source code, the file // volta-bracket.cc, at approx line 133, and recompile Lilypond // && str != "||" // && str != "|" // But then closing becomes hardcoded and we have no choice. // There must be some \override or \set which fixes this stubbornness of the // Lilypond developers?? (olagunde@start.no) // } // else if (v->subtype() == Volta::VOLTA_OPEN) // { // } voltarray[i].voltart = startending; voltarray[i].barno=taktnr - 1; //register as last element i previous measure } #if 0 // TODO-WS if (v->tick2() == m->tick() + m->ticks()) { // if it is at the end of measure i++; voltarray[i].voltart = endending; voltarray[i].barno=taktnr;//last element of this measure // if (v->subtype() == Volta::VOLTA_CLOSED) // {// see comment above. // } // else if (v->subtype() == Volta::VOLTA_OPEN) // {// see comment above. // } } #endif }//if volta }// for all global elements #endif i=voltaCheckBar((Measure*)m, i); }//for all measures lastind=i; }// end findvolta void ExportLy::voltatest() { int i=0; for (i=0; i < lastind; i++) { qDebug("iter: %d\n", i); switch (voltarray[i].voltart) { case startrepeat: qDebug("startrepeat, bar %d\n", voltarray[i].barno); break; case endrepeat: qDebug("endrepeat, bar %d\n", voltarray[i].barno); break; case bothrepeat: qDebug("bothrepeat, bar %d\n", voltarray[i].barno); break; case endbar: qDebug("endbar, bar %d\n", voltarray[i].barno); break; case doublebar: qDebug("doublebar, bar %d\n", voltarray[i].barno); break; case startending: qDebug("startending, bar %d\n", voltarray[i].barno); break; case endending: qDebug("endending, bar %d\n", voltarray[i].barno); break; default: break; } } } //--------------------------------------------------------- // exportLilypond //--------------------------------------------------------- bool MuseScore::saveLilypond(Score* score, const QString& name) { ExportLy em(score); return em.write(name); } //--------------------------------------------------------- // writeClef //--------------------------------------------------------- void ExportLy::writeClef(int clef) { out << "\\clef "; switch (clef) { case ClefType::G: out << "treble\n"; break; case ClefType::F: out << "bass\n"; break; case ClefType::G1: out << "\"treble^8\"\n"; break; case ClefType::G2: out << "\"treble^15\"\n"; break; case ClefType::G3: out << "\"treble_8\"\n"; break; case ClefType::F8: out << "\"bass_8\"\n"; break; case ClefType::F15: out << "\"bass_15\"\n"; break; case ClefType::F_B: out << "bass\n"; break; case ClefType::F_C: out << "bass\n"; break; case ClefType::C1: out << "soprano\n"; break; case ClefType::C2: out << "mezzo-soprano\n"; break; case ClefType::C3: out << "alto\n"; break; case ClefType::C4: out << "tenor\n"; break; case ClefType::TAB2: case ClefType::TAB: out << "tab\n"; break; case ClefType::PERC: out << "percussion\n"; break; } } //--------------------------------------------------------- // writeTimeSig //--------------------------------------------------------- void ExportLy::writeTimeSig(TimeSig* sig) { int st = sig->timeSigType(); Fraction f = sig->sig(); timedenom = f.denominator(); z1 = f.numerator(); //lilypond writes 4/4 as C by default, so only check for cut. if (st == TSIG_ALLA_BREVE) { z1=2; timedenom=2; // 2/2 automatically written as alla breve by lily. } indent(); out << "\\time " << z1 << "/" << timedenom << " "; } //--------------------------------------------------------- // writeKeySig //--------------------------------------------------------- void ExportLy::writeKeySig(int st) { st = char(st & 0xff); out << "\\key "; switch (st) { case 7: out << "cis"; break; case 6: out << "fis"; break; case 5: out << "b"; break; case 4: out << "e"; break; case 3: out << "a"; break; case 2: out << "d"; break; case 1: out << "g"; break; case 0: out << "c"; break; case -7: out << "ces"; break; case -6: out << "ges"; break; case -5: out << "des"; break; case -4: out << "as"; break; case -3: out << "es"; break; case -2: out << "bes"; break; case -1: out << "f"; break; default: qDebug("illegal key %d\n", st); break; } out << " \\major \n"; } //--------------------------------------------------------- // tpc2name //--------------------------------------------------------- QString ExportLy::tpc2name(int tpc) { const char names[] = "fcgdaeb"; int acc = ((tpc + 1) / 7) - 2; QString s(names[(tpc + 1) % 7]); switch (acc) { case -2: s += "eses"; break; case -1: s += "es"; break; case 1: s += "is"; break; case 2: s += "isis"; break; case 0: break; default: s += "??"; break; } return s; } //--------------------------------------------------------- // tpc2purename //--------------------------------------------------------- QString ExportLy::tpc2purename(int tpc) { const char names[] = "fcgdaeb"; QString s(names[(tpc + 1) % 7]); return s; } //-------------------------------------------------------- // Slur functions, stolen from exportxml.cpp. I really // don't understand these functions, but they seem to work // (olav) // //--------------------------------------------------------- // findSlur -- get index of slur in slur table // return -1 if not found //--------------------------------------------------------- int ExportLy::findSlur(const Slur* s) const { for (int i = 0; i < 8; ++i) { if (slurre[i] == s) { return i; } } return -1; } //--------------------------------------------------------- // doSlurStart. Find start of slur connected to chord. //--------------------------------------------------------- void ExportLy::doSlurStart(Chord* chord, bool nextisrest) { #if 0 // TODO-S int slurcount=0; for (const Spanner* sp = chord->spannerFor(); sp; sp = sp->next()) { if (sp->type() != Element::SLUR) { continue; } const Slur* s = static_cast(sp); slurcount++; int i = findSlur(s); if (i >= 0) { slurstack++; slurre[i] = 0; started[i] = false; if (s->slurDirection() == MScore::UP) { out << "^"; } if (s->slurDirection() == MScore::DOWN) { out << "_"; } if (slurcount == 2) { phraseslur=slurstack; out << "\\"; } if (nextisrest) { out << "\\laissezVibrer "; } else { out << "("; } } else { i = findSlur(0); if (i >= 0) { slurstack++; slurre[i] = s; started[i] = true; if (s->slurDirection() == MScore::UP) { out << "^"; } if (s->slurDirection() == MScore::DOWN) { out << "_"; } if (slurcount == 2) { phraseslur=slurstack; out << "\\"; } if (nextisrest) { out << "\\laissezVibrer "; } else { out << "("; } } else { qDebug("no free slur slot"); } } } #endif } //--------------------------------------------------------- // doSlurStop // From exportxml.cpp: //------------------------------------------- void ExportLy::doSlurStop(Chord* chord) { #if 0 // TODO-S for (const Spanner* sp = chord->spannerBack(); sp; sp = sp->next()) { if (sp->type() != Element::SLUR) { continue; } const Slur* s = static_cast(sp); // check if on slur list int i = findSlur(s); if (i < 0) { // if not, find free slot to store it i = findSlur(0); if (i >= 0) { slurre[i] = s; started[i] = false; if (slurstack == phraseslur) { phraseslur=0; out << "\\"; } slurstack--; out << ")"; //why do we always end here?? } else { qDebug("no free slur slot"); } } } #endif for (int i = 0; i < 8; ++i) { if (slurre[i]) { #if 0 // TODO-S if (slurre[i]->endElement() == chord) { if (started[i]) { slurre[i] = 0; started[i] = false; if (phraseslur == slurstack) { out << "\\"; phraseslur = 0; } slurstack--; out << ")"; //why do we never end here?! } } #endif } } } //------------------------- // checkSlur //------------------------- void ExportLy::checkSlur(Chord* chord, bool nextisrest) { //init array: for (int i = 0; i < 8; ++i) { slurre[i] = 0; started[i] = false; } doSlurStop(chord); doSlurStart(chord, nextisrest); } //----------------------------------- // helper routine for writeScore // -- called from there //----------------------------------- void ExportLy::writeArticulation(ChordRest* c) { foreach (Articulation* a, c->articulations()) { switch (a->articulationType()) { case Articulation_Fermata: if (a->up()) { out << "\\fermata "; } else { out << "_\\fermata "; } break; case Articulation_Thumb: out << "\\thumb "; break; case Articulation_Sforzatoaccent: out << "-> "; break; case Articulation_Espressivo: out << "\\espressivo "; break; case Articulation_Staccato: out << "-. "; break; case Articulation_Staccatissimo: if (a->up()) { out << "-| "; } else { out << "_| "; } break; case Articulation_Tenuto: out << "-- "; break; //TODO: case Articulation_Flageolet: // out << "\\flageolet "; case Articulation_Portato: if (a->up()) { out << "-_ "; } else { out << "__ "; } break; case Articulation_Marcato: if (a->up()) { out << "-^ "; } else { out << "_^ "; } break; case Articulation_Ouvert: out << "\\open "; break; case Articulation_Plusstop: out << "-+ "; break; case Articulation_Upbow: out << "\\upbow "; break; case Articulation_Downbow: out << "\\downbow "; break; case Articulation_Reverseturn: out << "\\reverseturn "; break; case Articulation_Turn: out << "\\turn "; break; case Articulation_Trill: out << "\\trill "; break; case Articulation_Prall: out << "\\prall "; break; case Articulation_Mordent: out << "\\mordent "; break; case Articulation_PrallPrall: out << "\\prallprall "; break; case Articulation_PrallMordent: out << "\\prallmordent "; break; case Articulation_UpPrall: out << "\\prallup "; break; case Articulation_DownPrall: out << "\\pralldown "; break; case Articulation_UpMordent: out << "\\upmordent "; break; case Articulation_DownMordent: out << "\\downmordent "; break; default: qDebug("unsupported note attribute %d\n", int(a->articulationType())); break; }// end switch }// end foreach }// end writeArticulation(); //------------------------------------------ // write Tremolo. stolen from exportxml.cpp //------------------------------------------ void ExportLy::writeTremolo(Chord* chord) { if (chord->tremolo()) { Tremolo* tr = chord->tremolo(); int st = tr->tremoloType(); switch (st) { case TREMOLO_R8: out << ":8 "; break; case TREMOLO_R16: out << ":16 "; break; case TREMOLO_R32: out << ":32 "; break; case TREMOLO_R64: out << ":64 "; break; default: qDebug("unknown tremolo %d\n", st); break; } } } //------------------------------------------------------------------------------------------- // findFingerAndStringno //------------------------------------------------------------------------------------------ void ExportLy::findFingerAndStringno(Note* note, int& fingix, int& stringix, QString (& fingarray)[5], QString (& stringarray)[10]) { foreach (const Element* e, note->el()) { if (e->type() == Element::FINGERING) { const Text* text = static_cast(e); if (text->textStyleType() == TEXT_STYLE_FINGERING) { fingix++; Text* f = (Text*)e; fingarray[fingix] = f->text(); } else if (text->textStyleType() == TEXT_STYLE_STRING_NUMBER) { stringix++; Text* s = (Text*)e; stringarray[stringix] = s->text(); } } } } //end findfingerandstringno void ExportLy::writeStringInstruction(int& strgix, QString stringarr[10]) { if (strgix > 0) { //there should be only one stringinstruction, so this is possibly redundant. for (int i=0; i < strgix; i++) { out << "\\" << stringarr[strgix]; } } strgix = 0; } //--------------------------------------------------------- // writeFingering //--------------------------------------------------------- void ExportLy::writeFingering(int& fingr, QString fingering[5]) { if (fingr > 0) { if (fingr == 1) { out << "-" << fingering[1] << " "; } else if (fingr > 1) { out << "^\\markup {\\finger \""; out << fingering[1] << " - " << fingering[2] << "\"} "; } } fingr=0; } //---------------------------------------------------------------- // stemDirection //---------------------------------------------------------------- void ExportLy::stemDir(Chord* chord) { // For now, we only export stem directions for gracenotes. if (chord->beam() == 0 || chord->beam()->elements().front() == chord) { MScore::Direction d = chord->stemDirection(); if (d != stemDirection) { stemDirection = d; if ((d == MScore::UP) and (graceswitch == true)) { out << "\\stemUp "; } else if ((d == MScore::DOWN) and (graceswitch == true)) { out << "\\stemDown "; } // else if (d == MScore::AUTO) // { // if (graceswitch == true) // { // out << "\\stemNeutral "; // we set this at the end of graces anyway. // } // } } } }//end stemDirection //------------------------------------------------------------- // findGraceNotes //-------------------------------------------------------------- void ExportLy::findGraceNotes(Note* note, bool& chordstart, int streng) { NoteType gracen; gracen = note->noteType(); switch (gracen) { case NOTE_INVALID: case NOTE_NORMAL: if (graceswitch == true) { graceswitch=false; gracebeam=false; if (gracecount > 1) { out << " ] "; //single graces are not beamed } out << " } \\stemNeutral "; //end of grace gracecount=0; } if ((chordstart) or (streng > 0)) { out << "<"; chordstart=false; } break; case NOTE_ACCIACCATURA: case NOTE_APPOGGIATURA: case NOTE_GRACE4: case NOTE_GRACE16: case NOTE_GRACE32: if (graceswitch == false) { out << "\\grace{\\stemUp "; //as long as general stemdirecton is unsolved: graces always stemUp. graceswitch=true; gracebeam=false; gracecount=0; } gracecount++; break; } //end of switch(gracen) }//end findGraceNotes //--------------------------------------------------------------------------- // setOctave //--------------------------------------------------------------------------- void ExportLy::setOctave(int& purepitch, int& pitchidx, int (& pitchlist)[12]) { int oktavdiff=prevpitch - purepitch; int oktreit=numval(oktavdiff); while (oktreit > 0) { if ((oktavdiff < -6) or ((prevnote == "b") and (oktavdiff < -5))) { //up out << "'"; oktavdiff=oktavdiff + 12; } else if ((oktavdiff > 6) or ((prevnote == "f") and (oktavdiff > 5))) {//down out << ","; oktavdiff=oktavdiff - 12; } oktreit=oktreit - 12; } prevpitch=purepitch; pitchlist[pitchidx]=purepitch; pitchidx++; }//end setOctave bool ExportLy::arpeggioTest(Chord* chord) { bool arp=false; if (chord->arpeggio()) { arp=true; int subtype = int(chord->arpeggio()->arpeggioType()); switch (subtype) { case 0: out << "\\arpeggioNormal "; break; case 1: out << "\\arpeggioArrowUp "; break; case 2: out << "\\arpeggioArrowDown "; break; default: qDebug("unknown arpeggio subtype %d\n", subtype); break; } } return arp; } bool ExportLy::glissandotest(Chord* chord) { bool gliss=false; int i=0; for (i=0; i < glisscount; i++) { if (glisstable[i].chord == chord) { if (glisstable[i].type == 1) { out << "\\once\\override Glissando #'style = #'trill \n"; indent(); } gliss=true; } } return gliss; } //------------------------------------------------------------ // findNoteSymbol // Find symbols attached to note. //------------------------------------------------------------ bool ExportLy::findNoteSymbol(Note* n, QString& symbolname) { symbolname = ""; foreach (const Element* symbol, n->el()) { if (symbol->type() == Element::SYMBOL) { const Symbol* symb = static_cast(symbol); symbolname = Sym::id2name(symb->sym()); return true; // what about more symbols connected to one note? Return array of names? } } return false; } //end findNoteSymbol //--------------------------------------------------------- // writeChord //--------------------------------------------------------- void ExportLy::writeChord(Chord* c, bool nextisrest) { int purepitch; QString purename, chordnote; int pitchlist[12]; QString fingering[5]; QString stringno[10]; bool tie=false; bool symb=false; QList nl = c->notes(); bool chordstart=false; int fing=0; int streng=0; bool gliss=false; QString glisstext; QString symbolname; int j=0; for (j=0; j < 12; j++) { pitchlist[j]=0; } stemDir(c); if (nl.size() > 1) { chordstart = true; } int pitchidx=0; bool arpeggioswitch=false; arpeggioswitch=arpeggioTest(c); gliss = glissandotest(c); int iter=0; for (QList::iterator notesinchord = nl.begin();;) { iter++; Note* n = *notesinchord; //if fingering found on _previous_ chordnote, now is the time for writing it: if (fing > 0) { writeFingering(fing,fingering); } if (streng > 0) { writeStringInstruction(streng,stringno); } //find diverse elements and attributes connected to the note findFingerAndStringno(n, fing, streng, fingering, stringno); if (iter == 1) { findTuplets(n->chord()); } findGraceNotes(n, chordstart, streng);//also writes start of chord symbol "<" if necessary symb = findNoteSymbol(n, symbolname); if (n->tieFor()) { tie=true; } if (gracecount == 2) { out << " [ "; } out << tpc2name(n->tpc()).toUtf8().data(); //Output of The Notename Itself if ((chordstart) and (symb)) { cout << "symbol in chord\n"; writeSymbol(symbolname); } purepitch = n->pitch(); purename = tpc2name(n->tpc()); //with -es or -is prevnote=cleannote; //without -es or -is cleannote=tpc2purename(n->tpc());//without -es or -is if (purename.contains("eses") == 1) { purepitch=purepitch + 2; } else if (purename.contains("es") == 1) { purepitch=purepitch + 1; } else if (purename.contains("isis") == 1) { purepitch=purepitch - 2; } else if (purename.contains("is") == 1) { purepitch=purepitch - 1; } setOctave(purepitch, pitchidx, pitchlist); if (notesinchord == nl.begin()) { chordpitch=prevpitch; chordnote=cleannote; } ++notesinchord; //number of notes in chord, we progress to next chordnote if (notesinchord == nl.end()) { break; } out << " "; } //end of notelist = end of chord if ((nl.size() > 1) or (streng > 0)) { //if fingering found on previous chordnote, now is the time for writing it: if (fing > 0) { writeFingering(fing, fingering); } if (streng > 0) { writeStringInstruction(streng,stringno); } out << ">"; //endofchord sign cleannote=chordnote; //if this is a chord, use first note of chord as previous note //instead of actual previous note. } int ix=0; prevpitch=pitchlist[0]; while (pitchlist[ix] != 0) { if (pitchlist[ix] < prevpitch) { prevpitch=pitchlist[ix]; } ix++; } writeLen(c->actualTicks()); if ((symb) and (nl.size() == 1)) { writeSymbol(symbolname); } if (arpeggioswitch) { out << "\\arpeggio "; arpeggioswitch=false; } //if fingering found on a single note, now is the time for writing it: if (nl.size() == 1) { writeFingering(fing, fingering); } writeTremolo(c); if (gliss) { out << "\\glissando "; if (glisstable[glisscount].glisstext != "") { out << "^\\markup{" << glisstable[glisscount].glisstext << "} "; } //todo: make glisstext follow glissline } if (tie) { out << "~"; tie=false; } writeArticulation(c); checkSlur(c, nextisrest); out << " "; }// end of writechord //--------------------------------------------------------- // getLen //--------------------------------------------------------- int ExportLy::getLen(int l, int* dots) { int len = 4; if (l == 16 * MScore::division) { //longa, whole measure of 4/2-time len=-2; } else if (l == 12 * MScore::division) { // "6/2" "dotted brevis" used for whole-measure rest in 6/2 time. len=-3; } else if (l == 10 * MScore::division) { // "5/2"- time, used for whole-measure rest. len=-4; } else if (l == 8 * MScore::division) { //brevis len = -1; } else if (l == 7 * MScore::division) { //doubledotted whole len = 1; *dots = 2; } else if (l == 6 * MScore::division) { //dotted whole len = 1; *dots = 1; } else if (l == 5 * MScore::division) { // whole measure of 5/4-time len = -5; } else if (l == 4 * MScore::division) { //whole len = 1; } else if (l == 3 * MScore::division) { // dotted half len = 2; *dots = 1; } else if (l == ((MScore::division / 2) * 7)) { // double-dotted half: 7/8 used for \partial bar. len = 2; *dots=2; } else if (l == 2 * MScore::division) { len = 2; } else if (l == MScore::division) { //quarter len = 4; } else if (l == MScore::division*3 / 2) { //dotted quarter len=4; *dots=1; } else if (l == ((MScore::division / 4) * 7)) { // double-dotted quarter len = 4; *dots=2; } else if (l == MScore::division / 2) { //8th len = 8; } else if (l == MScore::division*3 / 4) { //dotted 8th len = 8; *dots=1; } else if (l == ((MScore::division / 8) * 7)) { // double-dotted 8th len = 8; *dots=2; } else if (l == MScore::division / 4) { len = 16; } else if (l == MScore::division / 8) { len = 32; } else if (l == MScore::division* 3 / 8) { //dotted 16th. len = 16; *dots = 1; } else if (l == ((MScore::division / 16) * 7)) { // double-dotted 16th. len = 16; *dots=2; } else if (l == MScore::division / 16) { len = 64; } else if (l == MScore::division / 32) { len = 128; } //triplets, lily uses nominal value surrounded by \times 2/3 { } //so we set len equal to nominal value else if (l == ((MScore::division* 8) / 3)) { len = 1; } else if (l == MScore::division* 4 / 3) { len = 2; } else if (l == (MScore::division* 2) / 3) { len = 4; } else if (l == MScore::division / 3) { len = 8; } else if (l == MScore::division / (3 * 2)) { len = 16; } else if (l == MScore::division / 3 * 4) { len = 32; } else if (l == MScore::division / 3 * 8) { len = 64; } else if (l == 0) { len = 1; } else { qDebug("measure: %d, unsupported len %d (%d,%d)\n", measurenumber, l, l / MScore::division, l % MScore::division); } return len; } //--------------------------------------------------------- // writeLen //--------------------------------------------------------- void ExportLy::writeLen(int ticks) { int dots = 0; int len = getLen(ticks, &dots); if (ticks != curTicks) { switch (len) { case -5: out << "1*5/4"; break; case -4: out << "2*5 "; break; case -3: out << "1.*2 "; break; case -2://longa out << "\\longa "; break; case -1: //brevis out << "\\breve"; break; default: out << len; for (int i = 0; i < dots; ++i) { out << "."; } break; } curTicks = ticks; if (dots > 0) { curTicks = -1; //first note after dotted: always explicit length } } } //--------------------------------------------------------- // writeRest // type = 0 normal rest // type = 1 whole measure rest // type = 2 spacer rest //--------------------------------------------------------- void ExportLy::writeRest(int l, int type) { if (type == 1) { //whole measure rest out << "R"; curTicks = -1; //whole measure rest always requires explicit length writeLen(l); wholemeasurerest=1; } else if (type == 2) { //invisible rest curTicks = -1; out << "s"; writeLen(l); } else { //normal rest out << "r"; writeLen(l); } out << " "; } //-------------------------------------------------------------- // write number of whole measure rests //------------------------------------------------------------- void ExportLy::writeMeasuRestNum() { if (wholemeasurerest > 1) { out << "*" << wholemeasurerest << " "; } if (wholemeasuretext != "") { out << "^\\markup{" << wholemeasuretext << "} \n"; indent(); } out << " | % \n"; indent(); wholemeasurerest=0; wholemeasuretext= ""; curTicks = -9; } //-------------------------------------------------------- // writeVolta //-------------------------------------------------------- void ExportLy::writeVolta(int measurenumber, int lastind) { bool utgang=false; int i=0; if (pickup) { measurenumber--; } while ((voltarray[i].barno < measurenumber) and (i <= lastind)) { //find the present measure i++; } if (measurenumber == voltarray[i].barno) { while (utgang == false) { switch (voltarray[i].voltart) { case startrepeat: if (wholemeasurerest > 0) { writeMeasuRestNum(); } indent(); out << "\\repeat volta 2 { %startrep \n"; firstalt=false; secondalt=false; repeatactive=true; curTicks=-1; break; case endrepeat: if ((repeatactive == true) and (secondalt == false)) { if (wholemeasurerest > 0) { writeMeasuRestNum(); } out << "} % end of repeatactive\n"; curTicks=-1; // repeatactive=false; } indent(); break; case bothrepeat: if (firstalt == false) { if (wholemeasurerest > 0) { writeMeasuRestNum(); } out << "} % end of repeat (both)\n"; indent(); out << "\\repeat volta 2 { % bothrep \n"; firstalt=false; secondalt=false; repeatactive=true; curTicks=-1; } break; case doublebar: if (wholemeasurerest > 0) { writeMeasuRestNum(); } out << "\n"; indent(); out << "\\bar \"||\""; curTicks=-1; break; case startending: if (firstalt == false) { if (wholemeasurerest > 0) { writeMeasuRestNum(); } out << "} % end of repeat except alternate endings\n"; indent(); out << "\\alternative{ { "; firstalt=true; curTicks=-1; } else { if (wholemeasurerest > 0) { writeMeasuRestNum(); //should not happen? } out << "{ "; indent(); firstalt=false; secondalt=true; curTicks=-1; } break; case endending: if (firstalt) { if (wholemeasurerest > 0) { writeMeasuRestNum(); } out << "} %close alt1\n"; secondalt=true; repeatactive=true; curTicks=-1; } else { if (wholemeasurerest > 0) { writeMeasuRestNum(); } out << "} } %close alternatives\n"; secondalt=false; firstalt=true; repeatactive=false; curTicks=-1; } break; case endbar: if (wholemeasurerest > 0) { writeMeasuRestNum(); } out << "\\bar \"|.\""; curTicks=-1; break; default: // case none: qDebug("strange voltarraycontents?\n"); break; }//end switch if (voltarray[i + 1].barno == measurenumber) { i++; } else { utgang=true; } }// end of while utgang false; }// if barno=measurenumber }// end writevolta //----------------------------------------------------------------------- // checkifnextisrest //----------------------------------------------------------------------- static void checkIfNextIsRest(MeasureBase* mb, Segment* s, bool& nextisrest, int track) { nextisrest = false; Segment* nextseg = s->next(); Element* nextelem; nextelem= nextseg->element(track); while (!(nextseg->segmentType() == SegmentType::EndBarLine))// and !(nextseg->segmentType() == SegmentType::EndBarLine))) { //go to next segment, check if it is chord or end of measure. if (nextseg->isChordRest()) { break; } nextseg = nextseg->next(); nextelem = nextseg->element(track); //check if it is on this track } //if it is not on this track, continue until end we find segment //containing element of this track, or end of measure while ((nextelem == 0) and (!(nextseg->segmentType() == SegmentType::EndBarLine))) { nextseg = nextseg->next(); nextelem = nextseg->element(track); } // if next segment contains element of this track, check for end of // measure and chordorrest. if ((nextseg->segmentType() != SegmentType::EndBarLine) && (nextseg->isChordRest())) { // probably superfluous as we have previously checked for // element on this track (!=0) if ((!(nextelem == 0 || nextelem->generated()))) { if (nextelem->type() == Element::REST) { nextisrest=true; } } } else { // if we have reached end of measure // go to next measure: if (mb->next()) { //if it is not the last one of the piece. mb = mb->next(); if (mb->type() == Element::MEASURE) { Measure* meas = (Measure*)mb; for (Segment* s = meas->first(); s; s = s->next()) { if (s->isChordRest()) { Element* elem = s->element(track); if (!(elem == 0 || elem->generated())) { if (elem->type() == Element::REST) { nextisrest=true; } else if (elem->type() == Element::CHORD) { //relax } } break; //do not check more segments. } } } } else { nextisrest=false; } } } void ExportLy::newLyricsRecord() { lyricsRecord* lyrrec; lyrrec = new lyricsRecord(); for (int i = 0; i < VERSES; i++) { lyrrec->lyrdat.tick[i]=0; lyrrec->lyrdat.verselyrics[i] = ""; lyrrec->lyrdat.segmentnumber[i] = 0; } lyrrec->lyrdat.staffname = staffname[staffInd].staffid; lyrrec->numberofverses=-1; lyrrec->next = NULL; lyrrec->prev = NULL; if (tailOfLyrics != NULL) { lyrrec->prev = tailOfLyrics; tailOfLyrics->next = lyrrec; } tailOfLyrics = lyrrec; thisLyrics = lyrrec; if (headOfLyrics == NULL) { headOfLyrics = lyrrec; } } //-------------------------------------------------------------------- // findLyrics //-------------------------------------------------------------------- void ExportLy::findLyrics() { int verse = 0; int track = 0; int vox = 0; int prevverse = 0; for (int staffno=0; staffno < staffInd; staffno++) { newLyricsRecord();//one record for each staff. Contains multiple voices and verses. for (MeasureBase* mb = score->first(); mb; mb = mb->next()) { if (mb->type() != Element::MEASURE) { continue; } Measure* meas = (Measure*)mb; SegmentType st = SegmentType::ChordRest | SegmentType::Grace; for (Segment* seg = meas->first(st); seg; seg = seg->next(st)) { const QList* lyrlist = seg->lyricsList(staffno * VOICES); if (!lyrlist) { continue; } foreach (const Lyrics* lix, * lyrlist) { if (lix) { verse = (lix)->no(); if ((verse - prevverse) > 1) { thisLyrics->lyrdat.verselyrics[verse - 1] += "__ _ "; } track = (lix)->track(); vox = track - (staffno * VOICES); thisLyrics->lyrdat.segmentnumber[verse]++; thisLyrics->lyrdat.tick[verse] = (lix)->segment()->tick(); if (verse > thisLyrics->numberofverses) { thisLyrics->numberofverses = verse; if (verse > 0) { int segdiff = (thisLyrics->lyrdat.segmentnumber[verse - 1] - thisLyrics->lyrdat.segmentnumber[verse]); if (segdiff > 0) { for (int i = 0; i < segdiff; i++) { thisLyrics->lyrdat.verselyrics[verse] += " _ "; } thisLyrics->lyrdat.segmentnumber[verse] += segdiff; } } } QString lyriks = (lix)->text(); // escape '"' character if (lyriks.contains('"')) { lyriks = "\"" + lyriks.replace("\"","\\\"") + "\""; } lyriks = lyriks.replace(" ", "_"); //bolton: if two words on one note. thisLyrics->lyrdat.verselyrics[verse] += lyriks; thisLyrics->lyrdat.staffname = staffname[staffno].staffid; thisLyrics->lyrdat.voicename[verse] = staffname[staffno].voicename[vox]; thisLyrics->lyrdat.tick[verse] = (lix)->segment()->tick(); int syl = (lix)->syllabic(); switch (syl) { case Lyrics::SINGLE: thisLyrics->lyrdat.verselyrics[verse] += " "; break; case Lyrics::BEGIN: thisLyrics->lyrdat.verselyrics[verse] += " -- "; break; case Lyrics::END: thisLyrics->lyrdat.verselyrics[verse] += " "; break; case Lyrics::MIDDLE: thisLyrics->lyrdat.verselyrics[verse] += " -- "; break; default: qDebug("unknown syllabic %d\n", syl); }//switch syllable cout << " lyrics endtick: " << (lix)->endTick() << "\n"; if ((lix)->ticks() > 0) { //more than one note on this syllable cout << " _ "; thisLyrics->lyrdat.verselyrics[verse] += " _ "; } } //if lyrics prevverse = verse; } // for each member of lyricslist if (verse < thisLyrics->numberofverses) { thisLyrics->lyrdat.verselyrics[thisLyrics->numberofverses] += "__ _ "; } } // for each segment } //for each staff } //for measurebase first to last }// end of findlyrics //------------------------------------------------------------- // writeLyrics //------------------------------------------------------------- void ExportLy::writeLyrics() { thisLyrics = headOfLyrics; tailOfLyrics->next = NULL;//??? int staffi=0; int stanza=0; while (thisLyrics != NULL) { staffi=0; while (staffname[staffi].staffid != "laststaff") { for (int j=0; j < staffname[staffi].numberofvoices; j++) { stanza=0; for (int ix = 0; ix < thisLyrics->numberofverses + 1; ix++) {//thisLyrics->numberofverses; ix++) if ((thisLyrics->lyrdat.staffname == staffname[staffi].staffid) and (thisLyrics->lyrdat.voicename[ix] == staffname[staffi].voicename[j])) { indentF(); stanza++; char verseno = (ix + 65); os << " " << thisLyrics->lyrdat.staffname; os << "verse" << verseno << " = \\lyricmode { \\set stanza = \" " << stanza << ". \" "; os << thisLyrics->lyrdat.verselyrics[ix] << "}\n"; } } } staffi++; } //if (thisLyrics->next != NULL) thisLyrics = thisLyrics->next; } thisLyrics = headOfLyrics; } //-------------------------------------------------------------- // connectLyricsToStaff //-------------------------------------------------------------- void ExportLy::connectLyricsToStaff() { /* if (lyrics attached to one of the voices in this staff)*/ thisLyrics =headOfLyrics; while (thisLyrics != NULL) { for (int j=0; j < staffname[indx].numberofvoices; j++) { for (int ix = 0; ix <= thisLyrics->numberofverses; ix++) {//; if (thisLyrics->lyrdat.staffname == staffname[indx].staffid) { if (thisLyrics->lyrdat.voicename[ix] == staffname[indx].voicename[j]) { indentF(); char verseno = ix + 65; os << " \\context Lyrics = " << staffname[indx].staffid; os << "verse" << verseno; os << "\\lyricsto "; os << thisLyrics->lyrdat.voicename[ix] << " \\"; os << thisLyrics->lyrdat.staffname << "verse" << verseno << "\n"; } } } } //if (thisLyrics->next != NULL) thisLyrics = thisLyrics->next; } os << "\n"; }//end connectlyricstostaff //-------------------------------------------------------------------- // cleanupLyrics //-------------------------------------------------------------------- void ExportLy::cleanupLyrics() { thisLyrics=headOfLyrics; while (thisLyrics != NULL) { headOfLyrics=headOfLyrics->next; delete thisLyrics; thisLyrics=headOfLyrics; } } //----------------------------------------------------------------------- // flatInInstrName //----------------------------------------------------------------------- QString ExportLy::flatInInstrName(QString name) { //(unecessarily?) big deal for handling the flat-sign in instrumentnames. int pt = 0; QChar kar; int unum; bool flat=false; QString newname=""; for (pt = 0; pt < name.size(); pt++) { kar=name.at(pt); unum = kar.unicode(); if (unum < 256) { newname.append(kar); } else if (unum == 57613) { // 57613 is the decimal value of ==hex e10d, the // unicode code point for "flat" in mscore's and // lilypond's fonts. How do I convert the QChar // directly to hex? newname.append("\\smaller \\flat "); flat=true; } } if (flat) { newname.prepend("\\markup{"); newname.append("}"); } else { newname = ""; } return newname; } //--------------------------------------------------------- // writeVoiceMeasure //--------------------------------------------------------- void ExportLy::writeVoiceMeasure(MeasureBase* mb, Staff* staff, int staffInd, int voice) { int i=0; char cvoicenum, cstaffnum; bool barempty=true; bool nextisrest=false; Measure* m = (Measure*)mb; //print barchecksign and barnumber for previous measure: if ((m->no() > 0) and (wholemeasurerest == 0) and (textspanswitch == false)) { indent(); out << " | % " << m->no() << "\n"; } measurenumber=m->no() + 1; // if (m->irregular()) // { // qDebug("irregular measure, number: %d\n", measurenumber); // } if ((measurenumber == 1) and (donefirst == false)) { // ^^^^if clause: to prevent doing these things for both pickup and first full measure donefirst=true; level=0; indent(); cvoicenum=voice + 65; cstaffnum= staffInd + 65; //there must be more elegant ways to do this, but whatever... staffname[staffInd].voicename[voice] = staffname[staffInd].partshort; staffname[staffInd].voicename[voice].append("voice"); staffname[staffInd].voicename[voice].append(cstaffnum); staffname[staffInd].voicename[voice].append(cvoicenum); staffname[staffInd].voicename[voice].prepend("A"); staffname[staffInd].voicename[voice].remove(QRegExp("[0-9]")); staffname[staffInd].voicename[voice].remove(QChar('.')); staffname[staffInd].voicename[voice].remove(QChar(' ')); out << staffname[staffInd].voicename[voice]; out << " = \\relative c" << relativ; indent(); out << "{\n"; level++; indent(); if (voice == 0) { QString flatpartn=""; QString flatshortn=""; cout << "X" << staffname[staffInd].partname.toUtf8().data() << "x\n"; flatpartn = flatInInstrName(staffname[staffInd].partname); flatshortn = flatInInstrName(staffname[staffInd].partshort); out << "\\set Staff.instrumentName = "; cout << "F" << flatpartn.toUtf8().data() << "f\n"; if (flatpartn == "") { out << "#\"" << staffname[staffInd].partname << "\""; } else { out << flatpartn; } out << "\n"; indent(); out << "\\set Staff.shortInstrumentName = "; if (flatshortn == "") { out << "#\"" << staffname[staffInd].partshort << "\""; } else { out << flatshortn; } out << "\n"; indent(); writeClef(staff->clef(0)); indent(); out << "%staffkeysig\n"; indent(); //done in first measure anyway: ?? writeKeySig(staff->keys()->key(0).accidentalType()); // score->sigmap->timesig(0, z1, timedenom); // out << "\\time " << z1<< "/" << timedenom << " \n"; } cout << "pianostaff: " << pianostaff << "\n"; if (pianostaff == false) { //voice settings does not work very well with pianostaffs. Use //\stemUp \stemNeutral \stemDown instead switch (voice) { case 0: break; // we don't want voiceOne-specific behaviour if there is only one // voice, so if there are more voices, we append "\voiceOne" later case 1: out << "\\voiceTwo" << "\n\n"; break; case 2: out << "\\voiceThree" << "\n\n"; break; case 3: out << "\\voiceFour" << "\n\n"; break; } } //check for implicit startrepeat before first measure: (could //this be done in findvolta()?) i=0; while ((voltarray[i].voltart != startrepeat) and (voltarray[i].voltart != endrepeat) and (voltarray[i].voltart != bothrepeat) and (i <= lastind)) { i++; } if (i <= lastind) { if ((voltarray[i].voltart == endrepeat) or (voltarray[i].voltart == bothrepeat)) { indent(); out << "\\repeat volta 2 { \n"; repeatactive=true; } } }// END if start of first measure if (wholemeasurerest < 1) { indent(); } int tick = m->tick(); int measuretick=0; Element* e; for (Segment* s = m->first(); s; s = s->next()) { // for each segment in measure. Get element: int track = staffInd * VOICES + voice; e = s->element(track); if (!(e == 0 || e->generated())) { voiceActive[voice] = true; barempty = false; } else { continue; } handlePreInstruction(e); // Handle instructions which are to be printed before the element itself barlen=m->ticks(); //handle element: switch (e->type()) { case Element::CLEF: if (wholemeasurerest >= 1) { writeMeasuRestNum(); } writeClef(static_cast(e)->clefType()); indent(); break; case Element::TIMESIG: { if (wholemeasurerest >= 1) { writeMeasuRestNum(); } out << "%bartimesig: \n"; writeTimeSig((TimeSig*)e); out << "\n"; int nombarlen=z1 * MScore::division; if (timedenom == 8) { nombarlen=nombarlen / 2; } if (timedenom == 2) { nombarlen = 2 * nombarlen; } if ((barlen < nombarlen) and (measurenumber == 1) and (voice == 0)) { pickup = true; partial = true; indent(); const SigEvent ev(m->score()->sigmap()->timesig(m->tick())); out << "\\partial " << ev.timesig().denominator() << "*" << ev.timesig().numerator() << "\n"; } curTicks=-1; //we always need explicit length after timesig. indent(); break; } case Element::KEYSIG: { if (wholemeasurerest >= 1) { writeMeasuRestNum(); } out << "%barkeysig: \n"; //this simple line did the job before mid-december // 2009: //writeKeySig(e->subtype()); //but then, some changes must have been made to // keysig.cpp and .h I then stole (as usual) the code // below from exportxml.cpp. It was, however marked // with a "todo". The check for not end of keylist // prevents some keychanges in the middle of the // piece from being written, so I had to comment it // out. (olav.) KeySig* ksig= (KeySig*)e; int keytick = ksig->tick(); cout << "at tick: " << keytick << "\n"; KeyList* kl = score->staff(staffInd)->keys(); KeySigEvent key = kl->key(keytick); // auto ci = kl->find(keytick); // // if (ci != kl->end()) // { cout << "barkeysig: " << key.accidentalType() << " measureno: " << measurenumber << "\n"; indent(); writeKeySig(key.accidentalType()); // } indent(); curTicks=-1; //feels safe to force explicit length after keysig break; } case Element::CHORD: { if (wholemeasurerest >= 1) { writeMeasuRestNum(); } int ntick = static_cast(e)->tick() - tick; if (ntick > 0) { writeRest(ntick, 2); //invisible rest: s curTicks=-1; } tick += ntick; measuretick=measuretick + ntick; checkIfNextIsRest(mb, s, nextisrest, track); writeChord((Chord*)e, nextisrest); tick += ((Chord*)e)->actualTicks(); measuretick=measuretick + ((Chord*)e)->actualTicks(); break; } case Element::REST: { bool articul=false; findTuplets((ChordRest*)e); QList a; ChordRest* CR = (ChordRest*)e; a = CR->articulations(); if (!(a.isEmpty())) { articul = true; } int l = ((Rest*)e)->actualTicks(); int mlen=((Rest*)e)->segment()->measure()->ticks(); int nombarl=z1 * MScore::division; if (((l == mlen) || (l == 0)) and (mlen == nombarl)) { //l == 0 ?? if (wholemeasurerest > 0) { if (articul) { writeMeasuRestNum(); writeRest(l,0); writeArticulation((ChordRest*)e); } else { wholemeasurerest++; } } else { //wholemeasurerest: on fermata, output of * and start of new count. l = ((Rest*)e)->segment()->measure()->ticks(); if (articul) { writeRest(l,0); writeArticulation((ChordRest*)e); } else { writeRest(l, 1); //wholemeasure rest: R } } } else { if (wholemeasurerest >= 1) { writeMeasuRestNum(); } writeRest(l, 0);//ordinary rest: r if (articul) { writeArticulation((ChordRest*)e); } } tick += l; measuretick=measuretick + l; } //end REST break; case Element::MARKER: qDebug("ordinary elements: Marker found\n"); break; case Element::BREATH: out << "\\breathe "; break; default: //qDebug("Export Lilypond: unsupported element <%s>\n", e->name()); break; } // end switch elementtype handleElement(e); //check for instructions anchored to element e. if (tupletcount == -1) { out << " } "; tupletcount=0; } } //end for all segments barlen=m->ticks(); if (barempty == true) { // no stuff in this bar in this voice: fill empty bar with silent rest if ((pickup) and (measurenumber == 1) and (voice == 0)) { const SigEvent ev(m->score()->sigmap()->timesig(m->tick())); out << "\\partial " << ev.timesig().denominator() << "*" << ev.timesig().numerator() << "\n"; indent(); writeRest(barlen,2); out << "\n"; }//end if pickup else { //if not pickupbar: full measure silent bar writeRest(barlen, 2); curTicks=-1; } }//end bar empty else { // voice bar not empty //we have to fill with spacer rests before and after nonsilent material if ((measuretick < barlen) and (measurenumber > 0)) { //fill rest of measure with silent rest int negative=barlen - measuretick; curTicks=-1; writeRest(negative, 2); curTicks=-1; } } int mno; if (!partial) { mno = measurenumber + 1; } else { mno = measurenumber; } writeVolta(mno, lastind); } //end write VoiceMeasure //--------------------------------------------------------- // writeScore //--------------------------------------------------------- void ExportLy::writeScore() { // init of some fundamental variables firstalt=false; secondalt=false; tupletcount=0; char cpartnum; chordpitch=41; repeatactive=false; staffInd = 0; graceswitch=false; int voice=0; cleannote="c"; prevnote="c"; gracecount=0; donefirst=false; lastJumpOrMarker = 0; initJumpOrMarkerLMs(); wholemeasuretext = ""; glisscount = 0; textspanswitch = false; textspannerdown=false; headOfLyrics = NULL; tailOfLyrics = NULL; privateRehearsalMark='A'; foreach (Part* part, score->parts()) { nextAnchor=0; initAnchors(); resetAnchor(anker); int n = part->staves()->size(); staffname[staffInd].partname = part->longName().toPlainText(); staffname[staffInd].partshort = part->shortName().toPlainText(); curTicks=-1; pickup=false; if (part->nstaves() == 2) { pianostaff = true; } else { pianostaff = false; } int strack = score->staffIdx(part) * VOICES; int etrack = strack + n * VOICES; buildInstructionListPart(strack, etrack); buildGlissandoList(strack,etrack); //ANCHORTEST: print instructionlist // qDebug("anchortest\n"); // anchortest(); // qDebug("jumptest\n"); // jumptest(); segfaults!?!? foreach (Staff* staff, * part->staves()) { out << "\n"; relativ=""; switch (staff->clef(0)) { case ClefType::G: relativ="'"; staffpitch=12 * 5; break; case ClefType::TAB: case ClefType::PERC: case ClefType::PERC2: case ClefType::G3: case ClefType::F: relativ=""; staffpitch=12 * 4; break; case ClefType::G1: case ClefType::G2: relativ="''"; staffpitch=12 * 6; break; case ClefType::F_B: case ClefType::F_C: case ClefType::F8: relativ=","; staffpitch=12 * 3; break; case ClefType::F15: relativ=",,"; staffpitch=12 * 2; break; case ClefType::C1: case ClefType::C2: case ClefType::C3: case ClefType::C4: relativ="'"; staffpitch=12 * 5; break; default: //?? break; } staffrelativ=relativ; cpartnum = staffInd + 65; staffname[staffInd].staffid = staffname[staffInd].partshort; staffname[staffInd].staffid.append("part"); staffname[staffInd].staffid.append(cpartnum); staffname[staffInd].staffid.prepend("A"); staffname[staffInd].staffid.remove(QRegExp("[0-9]")); staffname[staffInd].staffid.remove(QChar('.')); staffname[staffInd].staffid.remove(QChar(' ')); findVolta(); //qDebug("voltatest\n"); // voltatest(); for (voice = 0; voice < VOICES; ++voice) { voiceActive[voice] = false; } for (voice = 0; voice < VOICES; ++voice) { prevpitch=staffpitch; relativ=staffrelativ; donefirst=false; partial=0; //for all measures in this voice: for (MeasureBase* m = score->first(); m; m = m->next()) { if (m->type() != Element::MEASURE) { continue; } if (staffInd == 0) { findMarkerAtMeasureStart((Measure*)m); } //xxx else //xxx printJumpOrMarker(measurenumber, true); writeVoiceMeasure(m, staff, staffInd, voice); //really write the measure contents if (staffInd == 0) { jumpAtMeasureStop((Measure*)m); } //xxx else //xxx printJumpOrMarker(measurenumber, false); } level--; indent(); out << "\\bar \"|.\" \n"; //thin-thick barline as last. level=0; indent(); out << "}% end of last bar in partorvoice\n\n"; if (voiceActive[voice]) { scorout << voicebuffer; } voicebuffer = " \n"; } // for voice 0 to VOICES int voiceno=0; for (voice = 0; voice < VOICES; ++voice) { if (voiceActive[voice]) { voiceno++; } } if (voiceno == 1) { staffname[staffInd].simultaneousvoices=false; } if (voiceno > 1) { //if more than one voice must be combined into one staff. level=0; indent(); out << staffname[staffInd].staffid << " = << \n"; staffname[staffInd].simultaneousvoices=true; level++; indent(); out << "\\mergeDifferentlyHeadedOn\n"; indent(); out << "\\mergeDifferentlyDottedOn \n"; ++level; for (voice = 0; voice < voiceno; voice++) { if (voiceActive[voice]) { //have to go back to explicitly naming the voices, so that //it will be possible to attach lyrics to them. indent(); out << "\\context Voice = " << staffname[staffInd].voicename[voice]; if ((voice == 0) and (pianostaff == false)) { out << "{\\voiceOne "; } out << "\\" << staffname[staffInd].voicename[voice]; if ((voice == 0) and (pianostaff == false)) { out << "}"; } if (voice < voiceno - 1) { out << "\\\\ \n"; } else { out << "\n"; } } } indent(); out << ">> \n\n"; level=0; indent(); scorout << voicebuffer; voicebuffer = " \n"; } staffname[staffInd].numberofvoices=voiceno; ++staffInd; }// end of foreach staff staffname[staffInd].staffid="laststaff"; if (n > 1) { --level; indent(); } }// end for each part }// end of writeScore //------------------------------------------------------------------- // write score-block: combining parts and voices, drawing brackets and // braces, at end of lilypond file // ------------------------------------------------------------------- void ExportLy::writeScoreBlock() { thisLyrics = headOfLyrics; if (nochord == false) { // output the chords as a separate staff before the score-block os << "theChords = \\chordmode { \n"; printChordList(); cleanupChordList(); level--; } // bracktest(); level=0; os << "\n\\score { \n"; level++; indentF(); os << "<< \n"; indx=0; while (staffname[indx].staffid != "laststaff") { if (lybracks[indx].brakstart) { ++level; indentF(); os << "\\context StaffGroup = " << (char)(lybracks[indx].brakno + 64) << "<< \n"; } if (lybracks[indx].bracestart) { ++level; indentF(); if (lybracks[indx].piano) { os << "\\context PianoStaff <<\n"; indentF(); os << "\\set PianoStaff.instrumentName=\"Piano\" \n"; pianostaff=true; } else { os << "\\context GrandStaff = " << (char)(lybracks[indx].braceno + 64) << "<< \n"; } } if ((nochord == false) && (indx == 0)) { //insert chords as the first staff. indentF(); os << "\\new ChordNames { \\theChords } \n"; } ++level; indentF(); os << "\\context Staff = " << staffname[indx].staffid << " << \n"; ++level; indentF(); os << "\\"; if (staffname[indx].simultaneousvoices) { os << staffname[indx].staffid << "\n"; } else { // have to reintroduce explicit naming of voices because of "\lyricsto" os << "context Voice = " << staffname[indx].voicename[0] << " \\"; os << staffname[indx].voicename[0] << "\n"; //voices are counted from 0. } if (lybracks[indx].piano) { indentF(); os << "\\set Staff.instrumentName = #\"\"\n"; indentF(); os << "\\set Staff.shortInstrumentName = #\"\"\n"; } --level; indentF(); os << ">>\n\n"; // end of this staff connectLyricsToStaff(); if (((lybracks[indx].brakstart) and (lybracks[indx].brakend)) or ((lybracks[indx].bracestart) and (lybracks[indx ].braceend))) { //if bracket or brace starts and ends on same staff: one-staff brace/bracket. indentF(); os << "\\override StaffGroup.SystemStartBracket #'collapse-height = #1 \n"; indentF(); os << "\\override Score.SystemStartBar #'collapse-height = #1 \n"; } if (lybracks[indx].brakend) { --level; indentF(); os << ">> %end of StaffGroup" << (char)(lybracks[indx].brakno + 64) << "\n\n"; } if (lybracks[indx].braceend) { --level; indentF(); if (lybracks[indx].piano) { os << ">> %end of PianoStaff" << (char)(lybracks[indx].braceno + 64) << "\n"; } else { os << ">> %end of GrandStaff" << (char)(lybracks[indx].braceno + 64) << "\n"; } } --level; ++indx; }//while still more staves cleanupLyrics(); os << "\n"; os << "\n" " \\set Score.skipBars = ##t\n" " %%\\set Score.melismaBusyProperties = #'()\n" " \\override Score.BarNumber #'break-visibility = #end-of-line-invisible %%every bar is numbered.!!!\n" " %% remove previous line to get barnumbers only at beginning of system.\n" " #(set-accidental-style 'modern-cautionary)\n"; if (rehearsalnumbers) { os << " \\set Score.markFormatter = #format-mark-box-numbers %%boxed rehearsal-numbers \n"; } else { os << " \\set Score.markFormatter = #format-mark-box-letters %%boxed rehearsal-marks\n"; } if ((timedenom == 2) and (z1 == 2)) { os << "%% "; } os << " \\override Score.TimeSignature #'style = #'() %%makes timesigs always numerical\n" " %% remove previous line to get cut-time/alla breve or common time \n"; os << " \\set Score.pedalSustainStyle = #'mixed \n" " %% make spanners comprise the note it end on, so that there is no doubt that this note is included.\n" " \\override Score.TrillSpanner #'(bound-details right padding) = #-2\n" " \\override Score.TextSpanner #'(bound-details right padding) = #-1\n" " %% Lilypond's normal textspanners are too weak: \n" " \\override Score.TextSpanner #'dash-period = #1\n" " \\override Score.TextSpanner #'dash-fraction = #0.5\n" " %% lilypond chordname font, like mscore jazzfont, is both far too big and extremely ugly (olagunde@start.no):\n" " \\override Score.ChordName #'font-family = #'roman \n" " \\override Score.ChordName #'font-size =#0 \n" " %% In my experience the normal thing in printed scores is maj7 and not the triangle. (olagunde):\n" " \\set Score.majorSevenSymbol = \\markup {maj7}\n" " >>\n\n" " %% Boosey and Hawkes, and Peters, have barlines spanning all staff-groups in a score,\n" " %% Eulenburg and Philharmonia, like Lilypond, have no barlines between staffgroups.\n" " %% If you want the Eulenburg/Lilypond style, comment out the following line:\n" " \\layout {\\context {\\Score \\consists Span_bar_engraver}}\n" "}%% end of score-block \n\n"; if (((pianostaff) and (indx == 2)) or (indx < 2)) { os << "#(set-global-staff-size 20)\n"; } else if (indx > 2) { os << "#(set-global-staff-size 14)\n"; } }// end scoreblock //------------------------------------------------------------------------- // writeLilyMacros //------------------------------------------------------------------------- void ExportLy::writeLilyMacros() { if ((jumpswitch) || (ottvaswitch)) { os << " %%---------------MSCORE'S LILYPOND MACROS: -------------------------\n\n"; } if (ottvaswitch) { os << " %%-----------------replacement for the \\ottava command--------------------\n\n"; //The lilypond \ottava command moves the visual notes one octave //down, so that they will sound at their correct pitch when we //take account of the 8va instruction. Mscore adds the //8va-instruction and leave the notes in place on the staff. In //order to make the lilypond code exported from mscore reflect //mscore behavior, it was necessary to construct the macros \okt //and \oktend as substitutes for \ottava. A more elegant //solution would be to prevent lilypond's \ottava from temporarily //resetting the middleCPosition, but I did not understand how to //do that. (olav) os << "ottva =\n " "{ %% for explanation, see mscore source file exportly.cpp \n" " \\once\\override TextSpanner #'(bound-details left text) = \"8va\" \n" " \\once\\override TextSpanner #'(bound-details right text) = \\markup{ \\draw-line #'(0 . -1) }\n" " #(ly:export (make-event-chord (list (make-span-event 'TextSpanEvent START)))) \n" "}\n" "\n" "ottvaend ={ #(ly:export (make-event-chord (list (make-span-event 'TextSpanEvent STOP)))) \n" " \\textSpannerNeutral} \n" "ottvabassa = \n" "{ \n" " \\once \\override TextSpanner #'(bound-details left text) = \"8vb\" \n" " \\textSpannerDown \n" " \\once \\override TextSpanner #'(bound-details right text) = \\markup{ \\draw-line #'(0 . 1) } \n" " #(ly:export (make-event-chord (list (make-span-event 'TextSpanEvent START)))) \n" "}\n" "\n" "%%------------------end ottava macros ---------------------\n\n"; }// end of if ottva if (jumpswitch) { os << " %%------------------coda---segno---macros--------------------\n" " %% modified from lsr-snippets. Work in progress: \n" " %% These macros presupposes a difference between the use of the \n" " %% Coda-sign telling us to jump to the coda (\\gotocoda), and the \n" " %% Coda-sign telling us that this is actually the Coda (\\theCoda). \n" " %% This goes well if you use the mscore text: \"To Coda\" as a mark of \n" " %% of where to jump from, and the codawheel as the mark of where to jump to\n" " %% Otherwise (using codawheel for both) you have to edit the lilypond-file by hand.\n" " gotocoda = \\mark \\markup {\\musicglyph #\"scripts.coda\"} \n" " thecodasign = \\mark \\markup {\\musicglyph #\"scripts.coda\" \"Coda\"} \n" " thesegno = \\mark \\markup {\\musicglyph #\"scripts.segno\"} \n" " varcodasign = \\mark \\markup {\\musicglyph #\"scripts.varcoda\"} \n" " Radjust = \\once \\override Score.RehearsalMark #'self-alignment-X = #RIGHT \n" " blankClefKey = {\\once \\override Staff.KeySignature #'break-visibility = #all-invisible \n" " \\once \\override Staff.Clef #'break-visibility = #all-invisible \n" " } \n" " codetta = {\\mark \\markup \\line {\\musicglyph #\"scripts.coda\" \\hspace #-1.3 \\musicglyph #\"scripts.coda\"} } \n" " fine = {\\Radjust \\mark \\markup {\"Fine\"} \\mark \\markup {\\musicglyph #\"scripts.ufermata\" } \n" " \\bar \"||\" } \n" " DCalfine = {\\Radjust \\mark \\markup {\"D.C. al fine\"} \\bar \"||\" \\blankClefKey \\stopStaff \\cadenzaOn } \n" " DCalcoda = {\\Radjust \\mark \\markup {\"D.C. al coda\"} \\bar \"||\" \\blankClefKey \\stopStaff \\cadenzaOn } \n" " DSalfine = {\\Radjust \\mark \\markup {\"D.S. al fine\"} \\bar \"||\" \\blankClefKey \\stopStaff \\cadenzaOn } \n" " DSalcoda = {\\Radjust \\mark \\markup {\"D.S. al coda\"} \\bar \"||\" \\blankClefKey \\stopStaff \\cadenzaOn } \n" " showClefKey = {\\once \\override Staff.KeySignature #'break-visibility = #all-visible \n" " \\once \\override Staff.Clef #'break-visibility = #all-visible \n" " } \n" " resumeStaff = {\\cadenzaOff \\startStaff % Resume bar count and show staff lines again \n" " \\partial 32 s32 % Add a whee bit of staff before the clef! \n" " \\bar \"\" \n" " } \n" " %% whitespace between D.S./D.C. and the Coda: \n" " codaspace = {\\repeat unfold 2 {s4 s4 s4 s4 \\noBreak \\bar \"\" }} \n" " theCoda = {\\noBreak \\codaspace \\resumeStaff \\showClefKey \\thecodasign} \n" " %% -------------------end-of-coda-segno-macros------------------ \n\n "; } if ((jumpswitch) || ottvaswitch) { os << "%% --------------END MSCORE LILYPOND-MACROS------------------------\n\n\n\n\n"; } } //end of writelilymacros //------------------------------------------------------------- // writeLilyHeader //------------------------------------------------------------- void ExportLy::writeLilyHeader() { os << "%=============================================\n" "% created by MuseScore Version: " << VERSION << "\n" "% " << QDate::currentDate().toString( Qt::SystemLocaleLongDate); os << "\n"; os << "%=============================================\n" "\n" "\\version \"2.12.0\"\n\n"; // target lilypond version os << "\n\n"; } //--------------------------------------------------- // Page format //--------------------------------------------------- void ExportLy::writePageFormat() { const PageFormat* pf = score->pageFormat(); os << "#(set-default-paper-size "; os << "\"" << QString(pf->paperSize()->name).toLower() << "\""; if (pf->size().width() > pf->size().height()) { os << " 'landscape"; } os << ")\n\n"; // TODO/O.G.: choose between standard formats and specified paper // dimensions. We normally don't need both. double lw = pf->printableWidth(); os << "\\paper {\n"; os << " line-width = " << lw * INCH << "\\mm\n"; os << " left-margin = " << pf->evenLeftMargin() * INCH << "\\mm\n"; os << " top-margin = " << pf->evenTopMargin() * INCH << "\\mm\n"; os << " bottom-margin = " << pf->evenBottomMargin() * INCH << "\\mm\n"; os << " %%indent = 0 \\mm \n"; os << " %%set to ##t if your score is less than one page: \n"; os << " ragged-last-bottom = ##t \n"; os << " ragged-bottom = ##f \n"; os << " %% in orchestral scores you probably want the two bold slashes \n"; os << " %% separating the systems: so uncomment the following line: \n"; os << " %% system-separator-markup = \\slashSeparator \n"; os << " }\n\n"; }//end writepageformat //--------------------------------------------------- // writeScoreTitles //--------------------------------------------------- void ExportLy::writeScoreTitles() { #if 0 //TODOws os << "\\header {\n"; ++level; const MeasureBase* m = score->first(); foreach (const Element* e, * m->el()) { if (e->type() != TEXT) { continue; } QString s = ((Text*)e)->text(); indentF(); switch (e->subtype()) { case TEXT_TITLE: os << "title = "; break; case TEXT_SUBTITLE: os << "subtitle = "; break; case TEXT_COMPOSER: os << "composer = "; break; case TEXT_POET: os << "poet = "; break; default: qDebug("text-type %d not supported\n", e->subtype()); os << "subtitle = "; break; } os << "\"" << s << "\"\n"; } if (!score->metaTag("Copyright").isEmpty()) { indentF(); os << "copyright = \"" << score->metaTag("Copyright") << "\"\n"; } indentF(); os << "}\n"; #endif }// end writeScoreTitles //--------------------------------------------------------- //--------------------------------------------------------- // WRITE = the main function of exportly //--------------------------------------------------------- //--------------------------------------------------------- bool ExportLy::write(const QString& name) { //init of some fundamental variables. pianostaff=false; rehearsalnumbers=false; wholemeasurerest=0; f.setFileName(name); if (!f.open(QIODevice::WriteOnly)) { return false; } os.setDevice(&f); os.setCodec("utf8"); out.setCodec("utf8"); out.setString(&voicebuffer); voicebuffer = ""; scorout.setCodec("utf8"); scorout.setString(&scorebuffer); scorebuffer = ""; chordHead=NULL; chordcount = 0; slurstack=0; phraseslur=0; ottvaswitch = false; jumpswitch = false; nochord = true; writeLilyHeader(); writeScore(); findLyrics(); writeLilyMacros(); writePageFormat(); writeScoreTitles(); findBrackets(); os << scorebuffer; scorebuffer = ""; writeLyrics(); writeScoreBlock(); f.close(); return f.error() == QFile::NoError; }// end of function "write" /*----------------------- NEWS and HISTORY:-------------------- */ /* 02.feb 2010. If \voiceOne etc. is used in pianostaff, articulation signs are either placed on only above or only below staff, and not in any reasonable connection with the notehead. So \voiceOne etc. is no longer used in pianostaff. 27.dec.09 Two (and not more than two) dynamic signs on the same note. 26.dec.09 Fixed bug in triplets of chords. Tried to update outdated code on keychanges. 23,dec.09 Flat symbol in instrumentnames are now handled. Some progress on reconciling rehearsalmarks and segno/coda-symbols. 17. dec.09 Dynamics and text can now be connected to the same note. 09.dec.09 Fermatas on rests (wholemeasure and others). Fixed bugs in repeats/doblebars and in wholemeasurerests caused by pickupbar 20.nov.2009 Tried to repair reported crash on file Cronicas.mscz 7.nov. 2009: Lyrics. Works reasonably well on demo adeste. 1.nov. lefthandposition (roman numbers with line: violin, guitar), trill, pedal and general lines with/out text. 30.oct. Unterminated slurs: \laissezVibrer. Whole notes as part of triplets. Flageolets as symbol connected to the note. 28.oct. Arpeggios and glissandos. Fixed issue of 6.may in the issue tracker: incorrect export of polyphony. 25.oct. Implemented fingering and guitar string-number 24.oct Support for metronome marks. 22.oct conditional output of exportly's lilypond macros (\okt and \segno). bugfix for repeats. 13.oct fixed grace-note-troubles on demos: golliwogg, and troubles with wholemeasure rests in the shifting timesignatures in promenade. Started on lilypond \chordmode 08.okt. (olav) Tremolo. Segno and Coda. Correct insertion of s-rests in demo: adeste. 01.oct. 2009 (Olav) Improved export of whole measure rests. 29.sep.2009 (Olav) Rudiments of new 8va. Bugfix for repeats. Some support for Segno/Coda. 12.sep.2009 (Olav) Improved export of rehearsalmarks. 17.aug.2009 (db) add quotes around unparsed markup (since it can contain special characters), commented out the indent=0, fix spelling mistake for octave markings ("set-octaviation"), fix type of ottava mar. 2009 always explicit end-bar -> no need to declare last bar as incomplete, but writes two end-bars when last bar is complete. This doesn't show in print. 12.feb.2009. - Removed bug: double instrument definitions in pieces with pickup-measure (prev'ly defined both in measure 0 and measure 1). - Removed bug: nonrecognition of startrepeats. -Improved recognition of whole-measure rests. NEW 5.feb.2009: separated grandstaff (variable distance between staffs) from pianostaff: constant distance between staffs to prepare for cross-staff beaming (not implemented). Brackets/braces for single staffs. NEW 25.jan.2009: system brackets and braces for simple scores. Unsolved complications for multistaff instruments (piano, organ, harp), and for bracketing single staffs. NEW 22.jan. 2009 -- fixed a problem with beams on grace-notes, and some faults produced by the previous revision of exportly. NEW 18. jan. 2009 -- Now avoids export of empty voices. DELETED HISTORY PRE 2009. */ /*----------------------TODOS------------------------------------ -- Coda/Segno symbols collides with rehearsalmarks, which accordingly are not printed. Lilypond has automatic incrementation of rehearsalmarks. It is easy to input values to the variable in which the mark is stored. But I have not succeeded in finding an easy way to extract this value. Lilyponds \mark\default, which write the automatically incremented rehearsalmark is not reconcilable with segnos and coda, except thru very complex macros, which will make the exported lilypond code very ugly and not very perspicuous. If it was easy to extract the value of the reharsalmark variable from Lilypond, these macros would not be necessary. Because they clutter the lilypond-code I do not want to use them. I can then increment the rehearsalmarks in exportly.cpp, or I can extract the values of the rehearsalmarks in mscore. As mscore's manual insertion of rehearsalmarks is less elegant than than automatic incrementation, I chose to make exportly.cpp increment the rehearsalmarks. This gives the cleanest lilypond-code. -- all kinds of symbols at the notelevel. More symbols on the measurelevel -- odd noteheads and percussion staffs. See output from noteedit. -- breaks and spacers -- accordion symbols. -- dotted rests in timesignatures which do not subdivide in 3 (like 6/8, 12/8) are plain and simply wrong, and must be made impossible: translate to two separate rests. -- become clear on the difference between system text and staff text. -- octave-trouble in golliwogg. -- provide for more than one pianostaff in a score. -- Determine whether text goes above or below staff. -- correct export of chordsymbols: many faults. -- cross-staff beaming in pianostaff cross-voice slurs!?!?!? seems _very_ complex to implement (example demos:promenade, bar 6) Will \partcombine do it? -- difficult problem with hairpins: Beginning of hairpin and -- end of hairpin are anchored to different notes. This is done -- automatically when you drop an hairpin in the appropriate place -- in the score. exportly find these anchors and insert \< and \! -- after these notes. But the start of the hairpin protrudes to the -- left of the anchor. And often the end of the hairpin is anchored -- to a note which is too far to the right. The placement of the -- lily-symbols must take regard for the placement on the canvas -- and not to the anchorpoints alone. Check the procedure in the -- main program to see how the anchorpoints and the canvas-position -- is made and compensate for this when exporting the -- lily-symbols. -- check \set Score.hairpinToBarline = ##t -- close second volta: doesn't seem possible for single and thin-thin double barlines --Collisions in crowded multi-voice staffs (e.g. cello-suite). check \override RestCollision #'positioning-done = #'merge-rests-on-positioning. Use \partcombine instead of ord. polyphony -- General tuplets massive failure on demos: prelude_sr.mscz -- Many of the demos have voice 2 as the upper one => trouble. Exportly.cpp must be made to identify the uppermost voice as lilypond voice 1, whatever number it has in mscore. will \partcombine resolve this? -- Markups belonging to a multimeasure rest should be left-adjusted to the left barline, and not centered over the rest. No good solutions found. -- Bug in lilypond. See notation reference 1.2.6 for 2.12.1: gracenotes. At the end: issues and warnings: "Grace note synchronization can also lead to surprises. Staff notation, such as key signatures, bar lines, etc., are also synchronized. Take care when you mix staves with grace notes and staves without, for example, ....This can be remedied by inserting grace skips of the corresponding durations in the other staves." In earlier editions of the manual, this is rightly described as a bug. I am awaiting the correction of this in lilypond, which, given the promotion from bug to "issue", probably will be never, and I will not correct for it here. (olav) */ }