From 1fa8c4109ab6a3ec41230bbd61adcae24659c025 Mon Sep 17 00:00:00 2001 From: Leon Vinken Date: Tue, 1 Jan 2013 14:36:49 +0100 Subject: [PATCH] #15899: support figured bass extend elements --- libmscore/figuredbass.cpp | 61 +++--- libmscore/figuredbass.h | 8 +- mscore/exportxml.cpp | 80 ++++++-- mscore/importxml.cpp | 45 ++++- mscore/musicxml.h | 1 + mtest/musicxml/io/testFiguredBass2.xml | 245 +++++++++++++++++++++++++ test/musicxml/testFiguredBass2.xml | 245 +++++++++++++++++++++++++ 7 files changed, 640 insertions(+), 45 deletions(-) diff --git a/libmscore/figuredbass.cpp b/libmscore/figuredbass.cpp index 00754f804ce62..81bf44c47ab19 100644 --- a/libmscore/figuredbass.cpp +++ b/libmscore/figuredbass.cpp @@ -770,7 +770,7 @@ QString FiguredBassItem::Modifier2MusicXML(FiguredBassItem::Modifier prefix) con // node instead of for each individual
node. //--------------------------------------------------------- -void FiguredBassItem::readMusicXML(const QDomElement& de, bool paren) +void FiguredBassItem::readMusicXML(const QDomElement& de, bool paren, bool& extend) { // read the
node de for (QDomElement e = de.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { @@ -778,7 +778,7 @@ void FiguredBassItem::readMusicXML(const QDomElement& de, bool paren) const QString& val(e.text()); int iVal = val.toInt(); if (tag == "extend") - ; // TODO + extend = true; else if (tag == "figure-number") { // MusicXML spec states figure-number is a number // MuseScore can only handle single digit @@ -815,17 +815,22 @@ void FiguredBassItem::readMusicXML(const QDomElement& de, bool paren) // Write MusicXML //--------------------------------------------------------- -void FiguredBassItem::writeMusicXML(Xml& xml) const +void FiguredBassItem::writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const { xml.stag("figure"); - QString strPrefix = Modifier2MusicXML(_prefix); - if (strPrefix != "") - xml.tag("prefix", strPrefix); - if (_digit != FBIDigitNone) - xml.tag("figure-number", _digit); - QString strSuffix = Modifier2MusicXML(_suffix); - if (strSuffix != "") - xml.tag("suffix", strSuffix); + if (doFigure) { + QString strPrefix = Modifier2MusicXML(_prefix); + if (strPrefix != "") + xml.tag("prefix", strPrefix); + if (_digit != FBIDigitNone) + xml.tag("figure-number", _digit); + QString strSuffix = Modifier2MusicXML(_suffix); + if (strSuffix != "") + xml.tag("suffix", strSuffix); + } + if (doExtend) { + xml.tagE("extend"); + } xml.etag(); } @@ -1490,10 +1495,13 @@ bool FiguredBass::fontData(int nIdx, QString * pFamily, QString * pDisplayName, // Note that onNote and ticks must be set by the MusicXML importer, // as the required context is not present in the items DOM tree. // Exception: if a element is present, tick can be set. +// Return true if valid, non-empty figure(s) are found +// Set extend to true if extend elements were found //--------------------------------------------------------- -void FiguredBass::readMusicXML(const QDomElement& de, int divisions) +bool FiguredBass::readMusicXML(const QDomElement& de, int divisions, bool& extend) { + extend = false; bool parentheses = (de.attribute("parentheses") == "yes"); QString normalizedText; int idx = 0; @@ -1513,20 +1521,27 @@ void FiguredBass::readMusicXML(const QDomElement& de, int divisions) qPrintable(val)); } else if (tag == "figure") { + bool figureExtend = false; FiguredBassItem * pItem = new FiguredBassItem(score(), idx++); pItem->setTrack(track()); pItem->setParent(this); - pItem->readMusicXML(e, parentheses); + pItem->readMusicXML(e, parentheses, figureExtend); + if (figureExtend) + extend = true; items.append(*pItem); // add item normalized text if(!normalizedText.isEmpty()) normalizedText.append('\n'); normalizedText.append(pItem->normalizedText()); } - else + else { domError(e); + return false; + } } setText(normalizedText); // this is the text to show while editing + bool res = !normalizedText.isEmpty(); + return res; } //--------------------------------------------------------- @@ -1547,15 +1562,17 @@ bool FiguredBass::hasParentheses() const // Write MusicXML //--------------------------------------------------------- -void FiguredBass::writeMusicXML(Xml& xml) const +void FiguredBass::writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const { - QString stag = "figured-bass"; - if (hasParentheses()) - stag += " parentheses=\"yes\""; - xml.stag(stag); - foreach(FiguredBassItem item, items) - item.writeMusicXML(xml); - xml.etag(); + if (doFigure || doExtend) { + QString stag = "figured-bass"; + if (hasParentheses()) + stag += " parentheses=\"yes\""; + xml.stag(stag); + foreach(FiguredBassItem item, items) + item.writeMusicXML(xml, doFigure, doExtend); + xml.etag(); + } } //--------------------------------------------------------- diff --git a/libmscore/figuredbass.h b/libmscore/figuredbass.h index dc5bf3ca7f476..d2f9d3e01fdff 100644 --- a/libmscore/figuredbass.h +++ b/libmscore/figuredbass.h @@ -152,8 +152,8 @@ class FiguredBassItem : public Element { virtual void write(Xml& xml) const; // read / write MusicXML - void readMusicXML(const QDomElement& de, bool paren); - void writeMusicXML(Xml& xml) const; + void readMusicXML(const QDomElement& de, bool paren, bool& extend); + void writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const; bool startsWithParenthesis() const; // specific API @@ -255,8 +255,8 @@ class FiguredBass : public Text { virtual void write(Xml& xml) const; // read / write MusicXML - void readMusicXML(const QDomElement& de, int divisions); - void writeMusicXML(Xml& xml) const; + bool readMusicXML(const QDomElement& de, int divisions, bool& extend); + void writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const; //DEBUG Q_INVOKABLE FiguredBassItem* addItem(); diff --git a/mscore/exportxml.cpp b/mscore/exportxml.cpp index 930455cf03aab..6561d1f394846 100644 --- a/mscore/exportxml.cpp +++ b/mscore/exportxml.cpp @@ -102,6 +102,12 @@ // #define DEBUG_REPEATS true // #define DEBUG_TICK true +//--------------------------------------------------------- +// typedefs +//--------------------------------------------------------- + +typedef QMap FigBassMap; + //--------------------------------------------------------- // attributes -- prints tag when necessary //--------------------------------------------------------- @@ -292,7 +298,6 @@ class ExportMusicXml { void symbol(Symbol const* const sym, int staff); void tempoText(TempoText const* const text, int staff); void harmony(Harmony const* const); - void figuredBass(FiguredBass const* const); }; //--------------------------------------------------------- @@ -3376,14 +3381,11 @@ static void annotations(ExportMusicXml* exp, int strack, int etrack, int track, case Element::HARMONY: exp->harmony(static_cast(e) /*, sstaff */); break; - case Element::FIGURED_BASS: - exp->figuredBass(static_cast(e) /*, sstaff */); - break; case Element::REHEARSAL_MARK: exp->rehearsal(static_cast(e), sstaff); break; - case Element::JUMP: - // ignore + case Element::FIGURED_BASS: // handled separately by figuredBass() + case Element::JUMP: // ignore break; default: qDebug("annotations: direction type %s at tick %d not implemented\n", @@ -3395,6 +3397,58 @@ static void annotations(ExportMusicXml* exp, int strack, int etrack, int track, } } +//--------------------------------------------------------- +// figuredBass +//--------------------------------------------------------- + +static void figuredBass(Xml& xml, int strack, int etrack, int track, const ChordRest* cr, FigBassMap& fbMap) + { + Segment* seg = cr->segment(); + if (seg->subtype() == Segment::SegChordRest) { + foreach(const Element* e, seg->annotations()) { + + int wtrack = -1; // track to write annotation + + if (strack <= e->track() && e->track() < etrack) + wtrack = findTrackForAnnotations(e->track(), seg); + + if (track == wtrack) { + if (e->type() == Element::FIGURED_BASS) { + const FiguredBass* fb = static_cast(e); + //qDebug("figuredbass() track %d seg %p fb %p seg %p tick %d ticks %d cr %p tick %d ticks %d", + // track, seg, fb, fb->segment(), fb->segment()->tick(), fb->ticks(), cr, cr->tick(), cr->actualTicks()); + bool extend = fb->ticks() > cr->actualTicks(); + if (extend) { + //qDebug("figuredbass() extend to %d + %d = %d", + // cr->tick(), fb->ticks(), cr->tick() + fb->ticks()); + fbMap.insert(strack, fb); + } + else + fbMap.remove(strack); + fb->writeMusicXML(xml, true, extend); + // there can only be one FB, if one was found + // no extend can be pending + return; + } + } + } + // check for extend pending + if (fbMap.contains(strack)) { + const FiguredBass* fb = fbMap.value(strack); + int endTick = fb->segment()->tick() + fb->ticks(); + if (cr->tick() < endTick) { + //qDebug("figuredbass() at tick %d extend only", cr->tick()); + // write figured bass element with extend only + fb->writeMusicXML(xml, false, true); + } + if (endTick <= cr->tick() + cr->actualTicks()) { + //qDebug("figuredbass() at tick %d extend done", cr->tick() + cr->actualTicks()); + fbMap.remove(strack); + } + } + } + } + //--------------------------------------------------------- // spannerStart //--------------------------------------------------------- @@ -3733,6 +3787,8 @@ void ExportMusicXml::write(QIODevice* dev) int irregularMeasureNo = 1; // number of next irregular measure int pickupMeasureNo = 1; // number of next pickup measure + FigBassMap fbMap; // pending figure base extends + for (MeasureBase* mb = score->measures()->first(); mb; mb = mb->next()) { if (mb->type() != Element::MEASURE) continue; @@ -4020,6 +4076,7 @@ void ExportMusicXml::write(QIODevice* dev) if (el->isChordRest()) { attr.doAttr(xml, false); annotations(this, strack, etrack, st, sstaff, seg); + figuredBass(xml, strack, etrack, st, static_cast(el), fbMap); spannerStop(this, strack, etrack, st, sstaff, seg); spannerStart(this, strack, etrack, st, sstaff, seg); } @@ -4203,17 +4260,6 @@ double ExportMusicXml::getTenthsFromDots(double dots) return dots / MScore::DPMM / millimeters * tenths; } - -//--------------------------------------------------------- -// figuredBass -//--------------------------------------------------------- - -void ExportMusicXml::figuredBass(FiguredBass const* const fb) - { - fb->writeMusicXML(xml); - } - - //--------------------------------------------------------- // harmony //--------------------------------------------------------- diff --git a/mscore/importxml.cpp b/mscore/importxml.cpp index bfc64ac4c009d..a2320469ae156 100644 --- a/mscore/importxml.cpp +++ b/mscore/importxml.cpp @@ -758,6 +758,7 @@ void MusicXml::import(Score* s) tremStart = 0; hairpin = 0; figBass = 0; + figBassExtend = false; // TODO only if multi-measure rests used ??? // score->style()->set(ST_createMultiMeasureRests, true); @@ -2367,8 +2368,15 @@ Measure* MusicXml::xmlMeasure(Part* part, QDomElement e, int number, int measure } else { // read figured bass element to attach to next note + figBassExtend = false; + bool mustkeep = false; figBass = new FiguredBass(score); - figBass->readMusicXML(e, divisions); + mustkeep = figBass->readMusicXML(e, divisions, figBassExtend); + // qDebug("xmlMeaure: fb mustkeep %d extend %d", mustkeep, figBassExtend); + if (!mustkeep) { + delete figBass; + figBass = 0; + } } } else @@ -4551,6 +4559,31 @@ static void handleBeamAndStemDir(ChordRest* cr, const BeamMode bm, const MScore: } +//--------------------------------------------------------- +// findLastFiguredBass +//--------------------------------------------------------- + +/** + * Find last figured bass on \a track before \a seg + */ + +static FiguredBass* findLastFiguredBass(int track, Segment* seg) + { + // qDebug("findLastFiguredBass(track %d seg %p)", track, seg); + while ((seg = seg->prev1(Segment::SegChordRest))) { + // qDebug("findLastFiguredBass seg %p", seg); + foreach(Element* e, seg->annotations()) { + if (e->track() == track && e->type() == Element::FIGURED_BASS) { + FiguredBass* fb = static_cast(e); + // qDebug("findLastFiguredBass found fb %p at seg %p", fb, seg); + return fb; + } + } + } + return 0; + } + + //--------------------------------------------------------- // xmlNote //--------------------------------------------------------- @@ -5039,7 +5072,7 @@ void MusicXml::xmlNote(Measure* measure, int staff, const QString& partId, QDomE // add figured bass element if (figBass) { - qDebug("add figured bass %p at tick %d ticks %d trk %d", figBass, tick, ticks, trk); + // qDebug("add figured bass %p at tick %d ticks %d trk %d", figBass, tick, ticks, trk); figBass->setTrack(trk); figBass->setTicks(ticks); // TODO: set correct onNote value @@ -5048,6 +5081,14 @@ void MusicXml::xmlNote(Measure* measure, int staff, const QString& partId, QDomE s->add(figBass); figBass = 0; } + else if (figBassExtend) { + // extend last figured bass to end of this chord + // qDebug("extend figured bass at tick %d ticks %d trk %d end %d", tick, ticks, trk, tick + ticks); + FiguredBass* fb = findLastFiguredBass((trk / VOICES) * VOICES, cr->segment()); + if (fb) + fb->setTicks(tick + ticks - fb->segment()->tick()); + } + figBassExtend = false; if (!chord) prevtick = tick; // remember tick where last chordrest was inserted diff --git a/mscore/musicxml.h b/mscore/musicxml.h index dd8ad274a1f34..4954408c9acf1 100644 --- a/mscore/musicxml.h +++ b/mscore/musicxml.h @@ -238,6 +238,7 @@ class MusicXml { Hairpin* hairpin; ///< Current hairpin (obsoletes wedgelist) Chord* tremStart; ///< Starting chord for current tremolo FiguredBass* figBass; ///< Current figured bass element (to attach to next note) + bool figBassExtend; ///< Current figured bass extend BeamMode beamMode; ///< Current beam mode Beam* beam; ///< Current beam mode diff --git a/mtest/musicxml/io/testFiguredBass2.xml b/mtest/musicxml/io/testFiguredBass2.xml index 1e96d1ff2d38d..a3ee9630611f9 100644 --- a/mtest/musicxml/io/testFiguredBass2.xml +++ b/mtest/musicxml/io/testFiguredBass2.xml @@ -85,6 +85,97 @@ 4 1 + + + + +
+ 1 + +
+
+ + + G + 4 + + 1 + 1 + quarter + up + + +
+ +
+
+ + + A + 4 + + 1 + 1 + quarter + up + + +
+ +
+
+ + + B + 4 + + 1 + 1 + quarter + down + + +
+ +
+
+ + + C + 5 + + 1 + 1 + quarter + down + +
+ + +
+ +
+
+ + + G + 4 + + 2 + 1 + half + up + + + + A + 4 + + 2 + 1 + half + up + light-heavy @@ -179,6 +270,160 @@ whole 2 +
+ + + + + G + 4 + + 1 + 1 + quarter + up + 1 + + + + A + 4 + + 1 + 1 + quarter + up + 1 + + +
+ 2 + +
+
+ + + B + 4 + + 1 + 1 + quarter + down + 1 + + +
+ +
+
+ + + C + 5 + + 1 + 1 + quarter + down + 1 + + + 4 + + + + G + 2 + + 1 + 5 + quarter + up + 2 + + +
+ 3 + +
+
+ + + A + 2 + + 1 + 5 + quarter + up + 2 + + +
+ +
+
+ + + B + 2 + + 1 + 5 + quarter + up + 2 + + +
+ +
+
+ + + C + 3 + + 1 + 5 + quarter + up + 2 + +
+ + + + G + 4 + + 2 + 1 + half + up + 1 + + + + A + 4 + + 2 + 1 + half + up + 1 + + + 4 + + + + 4 + 5 + 2 + light-heavy diff --git a/test/musicxml/testFiguredBass2.xml b/test/musicxml/testFiguredBass2.xml index 1e96d1ff2d38d..a3ee9630611f9 100644 --- a/test/musicxml/testFiguredBass2.xml +++ b/test/musicxml/testFiguredBass2.xml @@ -85,6 +85,97 @@ 4 1 + + + + +
+ 1 + +
+
+ + + G + 4 + + 1 + 1 + quarter + up + + +
+ +
+
+ + + A + 4 + + 1 + 1 + quarter + up + + +
+ +
+
+ + + B + 4 + + 1 + 1 + quarter + down + + +
+ +
+
+ + + C + 5 + + 1 + 1 + quarter + down + +
+ + +
+ +
+
+ + + G + 4 + + 2 + 1 + half + up + + + + A + 4 + + 2 + 1 + half + up + light-heavy @@ -179,6 +270,160 @@ whole 2 +
+ + + + + G + 4 + + 1 + 1 + quarter + up + 1 + + + + A + 4 + + 1 + 1 + quarter + up + 1 + + +
+ 2 + +
+
+ + + B + 4 + + 1 + 1 + quarter + down + 1 + + +
+ +
+
+ + + C + 5 + + 1 + 1 + quarter + down + 1 + + + 4 + + + + G + 2 + + 1 + 5 + quarter + up + 2 + + +
+ 3 + +
+
+ + + A + 2 + + 1 + 5 + quarter + up + 2 + + +
+ +
+
+ + + B + 2 + + 1 + 5 + quarter + up + 2 + + +
+ +
+
+ + + C + 3 + + 1 + 5 + quarter + up + 2 + +
+ + + + G + 4 + + 2 + 1 + half + up + 1 + + + + A + 4 + + 2 + 1 + half + up + 1 + + + 4 + + + + 4 + 5 + 2 + light-heavy