Skip to content

Commit

Permalink
Merge pull request #1733 from DDMAL/MusicXML_advFigBass
Browse files Browse the repository at this point in the history
Fix #15899: MusicXML figured bass support
  • Loading branch information
lasconic committed Feb 17, 2015
2 parents 06d18e8 + d327b29 commit 1f70682
Show file tree
Hide file tree
Showing 9 changed files with 808 additions and 104 deletions.
95 changes: 61 additions & 34 deletions libmscore/figuredbass.cpp
Expand Up @@ -775,8 +775,6 @@ void FiguredBassItem::undoSetParenth5(Parenthesis par)
// Convert MusicXML prefix/suffix to Modifier
//---------------------------------------------------------

// TODO add missing non-accidental types

FiguredBassItem::Modifier FiguredBassItem::MusicXML2Modifier(const QString prefix) const
{
if (prefix == "sharp")
Expand All @@ -791,6 +789,10 @@ FiguredBassItem::Modifier FiguredBassItem::MusicXML2Modifier(const QString prefi
return Modifier::DOUBLEFLAT;
else if (prefix == "sharp-sharp")
return Modifier::DOUBLESHARP;
else if (prefix == "cross")
return Modifier::CROSS;
else if (prefix == "backslash")
return Modifier::BACKSLASH;
else if (prefix == "slash")
return Modifier::SLASH;
else
Expand All @@ -801,8 +803,6 @@ FiguredBassItem::Modifier FiguredBassItem::MusicXML2Modifier(const QString prefi
// Convert Modifier to MusicXML prefix/suffix
//---------------------------------------------------------

// TODO add missing non-accidental types

QString FiguredBassItem::Modifier2MusicXML(FiguredBassItem::Modifier prefix) const
{
switch (prefix) {
Expand All @@ -812,8 +812,8 @@ QString FiguredBassItem::Modifier2MusicXML(FiguredBassItem::Modifier prefix) con
case Modifier::NATURAL: return "natural";
case Modifier::SHARP: return "sharp";
case Modifier::DOUBLESHARP: return "double-sharp";
case Modifier::CROSS: return ""; // TODO TBD
case Modifier::BACKSLASH: return ""; // TODO TBD
case Modifier::CROSS: return "cross";
case Modifier::BACKSLASH: return "backslash";
case Modifier::SLASH: return "slash";
case Modifier::NUMOF: return ""; // prevent gcc "‘FBINumOfAccid’ not handled in switch" warning
}
Expand All @@ -829,14 +829,12 @@ QString FiguredBassItem::Modifier2MusicXML(FiguredBassItem::Modifier prefix) con
//---------------------------------------------------------

#if 0
void FiguredBassItem::readMusicXML(XmlReader& e, bool paren, bool& extend)
void FiguredBassItem::readMusicXML(XmlReader& e, bool paren)
{
// read the <figure> node de
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "extend")
extend = true;
else if (tag == "figure-number") {
if (tag == "figure-number") {
// MusicXML spec states figure-number is a number
// MuseScore can only handle single digit
int iVal = e.readInt();
Expand Down Expand Up @@ -872,12 +870,25 @@ void FiguredBassItem::readMusicXML(XmlReader& e, bool paren, bool& extend)

//---------------------------------------------------------
// Write MusicXML
//---------------------------------------------------------

void FiguredBassItem::writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const
//
// Writes the portion within the <figure> tag.
//
// NOTE: Both MuseScore and MusicXML provide two ways of altering the (temporal) length of a
// figured bass object: extension lines and duration. The convention is that an EXTENSION is
// used if the figure lasts LONGER than the note (i.e., it "extends" to the following notes),
// whereas DURATION is used if the figure lasts SHORTER than the note (e.g., when notating a
// figure change under a note). However, MuseScore does not restrict durations in this way,
// allowing them to act as extensions themselves. As a result, a few more branches are
// required in the decision tree to handle everything correctly.
//---------------------------------------------------------

void FiguredBassItem::writeMusicXML(Xml& xml, bool isOriginalFigure, int crEndTick, int fbEndTick) const
{
xml.stag("figure");
if (doFigure) {

// The first figure of each group is the "original" figure. Practically, it is one inserted manually
// by the user, rather than automatically by the "duration" extend method.
if (isOriginalFigure) {
QString strPrefix = Modifier2MusicXML(_prefix);
if (strPrefix != "")
xml.tag("prefix", strPrefix);
Expand All @@ -886,9 +897,30 @@ void FiguredBassItem::writeMusicXML(Xml& xml, bool doFigure, bool doExtend) cons
QString strSuffix = Modifier2MusicXML(_suffix);
if (strSuffix != "")
xml.tag("suffix", strSuffix);

// Check if the figure ends before or at the same time as the current note. Otherwise, the figure
// extends to the next note, and so carries an extension type "start" by definition.
if (fbEndTick <= crEndTick) {
if (_contLine == ContLine::SIMPLE)
xml.tagE("extend type=\"stop\" ");
else if (_contLine == ContLine::EXTENDED) {
bool hasFigure = (strPrefix != "" || _digit != FBIDigitNone || strSuffix != "");
if (hasFigure)
xml.tagE("extend type=\"start\" ");
else
xml.tagE("extend type=\"continue\" ");
}
}
else
xml.tagE("extend type=\"start\" ");
}
if (doExtend) {
xml.tagE("extend");
// If the figure is not "original", it must have been created using the "duration" feature of figured bass.
// In other words, the original figure belongs to a previous note rather than the current note.
else {
if (crEndTick < fbEndTick)
xml.tagE("extend type=\"continue\" ");
else
xml.tagE("extend type=\"stop\" ");
}
xml.etag();
}
Expand Down Expand Up @@ -1344,7 +1376,7 @@ FiguredBassItem * FiguredBass::addItem()
// STATIC FUNCTION
// adding a new FiguredBass to a Segment;
// the main purpose of this function is to ensure that ONLY ONE F.b. element exists for each Segment/staff;
// it either re-uses an existing FiguredBass or creates a new one if none if found;
// it either re-uses an existing FiguredBass or creates a new one if none is found;
// returns the FiguredBass and sets pNew to true if it has been newly created.
//
// Sets an initial duration of the element up to the next ChordRest of the same staff.
Expand Down Expand Up @@ -1608,13 +1640,11 @@ bool FiguredBass::fontData(int nIdx, QString * pFamily, QString * pDisplayName,
// as the required context is not present in the items DOM tree.
// Exception: if a <duration> 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
//---------------------------------------------------------

#if 0
bool FiguredBass::readMusicXML(XmlReader& e, int divisions, bool& extend)
bool FiguredBass::readMusicXML(XmlReader& e, int divisions)
{
extend = false;
bool parentheses = e.attribute("parentheses") == "yes";
QString normalizedText;
int idx = 0;
Expand All @@ -1634,13 +1664,10 @@ bool FiguredBass::readMusicXML(XmlReader& e, int divisions, bool& extend)
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, figureExtend);
if (figureExtend)
extend = true;
pItem->readMusicXML(e, parentheses);
items.append(*pItem);
// add item normalized text
if (!normalizedText.isEmpty())
Expand Down Expand Up @@ -1676,17 +1703,17 @@ bool FiguredBass::hasParentheses() const
// Write MusicXML
//---------------------------------------------------------

void FiguredBass::writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const
void FiguredBass::writeMusicXML(Xml& xml, bool isOriginalFigure, int crEndTick, int fbEndTick, bool writeDuration, int divisions) const
{
if (doFigure || doExtend) {
QString stag = "figured-bass";
if (hasParentheses())
stag += " parentheses=\"yes\"";
xml.stag(stag);
for(FiguredBassItem* item : items)
item->writeMusicXML(xml, doFigure, doExtend);
xml.etag();
}
QString stag = "figured-bass";
if (hasParentheses())
stag += " parentheses=\"yes\"";
xml.stag(stag);
for(FiguredBassItem* item : items)
item->writeMusicXML(xml, isOriginalFigure, crEndTick, fbEndTick);
if (writeDuration)
xml.tag("duration", ticks() / divisions);
xml.etag();
}

//---------------------------------------------------------
Expand Down
9 changes: 5 additions & 4 deletions libmscore/figuredbass.h
Expand Up @@ -177,8 +177,8 @@ class FiguredBassItem : public Element {
virtual void write(Xml& xml) const override;

// read / write MusicXML
// void readMusicXML(XmlReader& de, bool paren, bool& extend);
void writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const;
// void readMusicXML(XmlReader& de, bool paren);
void writeMusicXML(Xml& xml, bool isOriginalFigure, int crEndTick, int fbEndTick) const;
bool startsWithParenthesis() const;

// specific API
Expand All @@ -196,6 +196,7 @@ class FiguredBassItem : public Element {
void setSuffix(const Modifier& v) { _suffix = v; }
void undoSetSuffix(Modifier suff);
ContLine contLine() const { return _contLine; }
void setContLine(const ContLine& v){ _contLine = v; }
void undoSetContLine(ContLine val);
Parenthesis parenth1() { return parenth[0]; }
Parenthesis parenth2() { return parenth[1]; }
Expand Down Expand Up @@ -290,8 +291,8 @@ class FiguredBass : public Text {
virtual void write(Xml& xml) const override;

// read / write MusicXML
bool readMusicXML(XmlReader& de, int divisions, bool& extend);
void writeMusicXML(Xml& xml, bool doFigure, bool doExtend) const;
// bool readMusicXML(XmlReader& de, int divisions);
void writeMusicXML(Xml& xml, bool isOriginalFigure, int crEndTick, int fbEndTick, bool writeDuration, int divisions) const;

//DEBUG
//Q_INVOKABLE Ms::FiguredBassItem* addItem();
Expand Down
33 changes: 23 additions & 10 deletions mscore/exportxml.cpp
Expand Up @@ -3862,7 +3862,7 @@ static void annotations(ExportMusicXml* exp, Xml&, int strack, int etrack, int t
// figuredBass
//---------------------------------------------------------

static void figuredBass(Xml& xml, int strack, int etrack, int track, const ChordRest* cr, FigBassMap& fbMap)
static void figuredBass(Xml& xml, int strack, int etrack, int track, const ChordRest* cr, FigBassMap& fbMap, int divisions)
{
Segment* seg = cr->segment();
if (seg->segmentType() == Segment::Type::ChordRest) {
Expand All @@ -3886,8 +3886,20 @@ static void figuredBass(Xml& xml, int strack, int etrack, int track, const Chord
}
else
fbMap.remove(strack);
fb->writeMusicXML(xml, true, extend);
// there can only be one FB, if one was found
int crEndTick = cr->tick() + cr->actualTicks();
int fbEndTick = fb->segment()->tick() + fb->ticks();
bool writeDuration = fb->ticks() < cr->actualTicks();
fb->writeMusicXML(xml, true, crEndTick, fbEndTick, writeDuration, divisions);

// Check for changing figures under a single note (each figure stored in a separate segment)
for (Segment* segNext = seg->next(); segNext && segNext->element(track) == NULL; segNext = segNext->next()) {
foreach (Element* annot, segNext->annotations()) {
if (annot->type() == Element::Type::FIGURED_BASS && annot->track() == track) {
const FiguredBass* fb = static_cast<const FiguredBass*>(annot);
fb->writeMusicXML(xml, true, 0, 0, true, divisions);
}
}
}
// no extend can be pending
return;
}
Expand All @@ -3896,13 +3908,14 @@ static void figuredBass(Xml& xml, int strack, int etrack, int track, const Chord
// 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) {
int crEndTick = cr->tick() + cr->actualTicks();
int fbEndTick = fb->segment()->tick() + fb->ticks();
bool writeDuration = fb->ticks() < cr->actualTicks();
if (cr->tick() < fbEndTick) {
//qDebug("figuredbass() at tick %d extend only", cr->tick());
// write figured bass element with extend only
fb->writeMusicXML(xml, false, true);
fb->writeMusicXML(xml, false, crEndTick, fbEndTick, writeDuration, divisions);
}
if (endTick <= cr->tick() + cr->actualTicks()) {
if (fbEndTick <= crEndTick) {
//qDebug("figuredbass() at tick %d extend done", cr->tick() + cr->actualTicks());
fbMap.remove(strack);
}
Expand Down Expand Up @@ -4403,7 +4416,7 @@ 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
FigBassMap fbMap; // pending figured bass extends

for (MeasureBase* mb = _score->measures()->first(); mb; mb = mb->next()) {
if (mb->type() != Element::Type::MEASURE)
Expand Down Expand Up @@ -4718,7 +4731,7 @@ void ExportMusicXml::write(QIODevice* dev)
}
}
}
figuredBass(xml, strack, etrack, st, static_cast<const ChordRest*>(el), fbMap);
figuredBass(xml, strack, etrack, st, static_cast<const ChordRest*>(el), fbMap, div);
spannerStart(this, strack, etrack, st, sstaff, seg);
}

Expand Down

0 comments on commit 1f70682

Please sign in to comment.