Skip to content

Commit

Permalink
Merge pull request #3530 from lvinken/255791-musicxml-relative-lyric-…
Browse files Browse the repository at this point in the history
…numbers-3

fix #255791 - [MusicXML import] use lyric number as relative instead …
  • Loading branch information
lasconic committed Mar 10, 2018
2 parents e1110cf + fdc0549 commit 422efca
Show file tree
Hide file tree
Showing 8 changed files with 918 additions and 121 deletions.
13 changes: 11 additions & 2 deletions mscore/importmxmlpass1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1899,10 +1899,14 @@ void MusicXMLParserPass1::part()
// set first instrument for multi-instrument part starting with rest
if (_firstInstrId != "" && _firstInstrSTime > Fraction(0, 1))
_parts[id]._instrList.setInstrument(_firstInstrId, Fraction(0, 1));
// determine the lyric numbers for this part
_parts[id].lyricNumberHandler().determineLyricNos();

// debug: print results
//qDebug("%s", qPrintable(_parts[id].toString()));

//qDebug("lyric numbers: %s", qPrintable(_parts[id].lyricNumberHandler().toString()));

/*
qDebug("instrument map:");
for (auto& instr: _parts[id]._instrList) {
Expand Down Expand Up @@ -2528,8 +2532,11 @@ void MusicXMLParserPass1::note(const QString& partId,
instrId = _e.attributes().value("id").toString();
_e.readNext();
}
else if (_e.name() == "lyric")
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "lyric") {
const auto number = _e.attributes().value("number").toString();
_parts[partId].lyricNumberHandler().addNumber(number);
_e.skipCurrentElement();
}
else if (_e.name() == "notations")
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "notehead")
Expand Down Expand Up @@ -2612,6 +2619,8 @@ void MusicXMLParserPass1::note(const QString& partId,
// TODO
vod.addNote(sTime.ticks(), (sTime + dura).ticks(), voice, staff);
}

Q_ASSERT(_e.isEndElement() && _e.name() == "note");
}

//---------------------------------------------------------
Expand Down
92 changes: 30 additions & 62 deletions mscore/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,38 +777,17 @@ static void addLyric(MxmlLogger* logger, const QXmlStreamReader* const xmlreader
static void addLyrics(MxmlLogger* logger, const QXmlStreamReader* const xmlreader,
ChordRest* cr,
QMap<int, Lyrics*>& numbrdLyrics,
QMap<int, Lyrics*>& defyLyrics,
QList<Lyrics*>& unNumbrdLyrics,
QSet<Lyrics*>& extLyrics,
MusicXmlLyricsExtend& extendedLyrics)
{
// first the lyrics with valid number
int lyricNo = -1;
for (QMap<int, Lyrics*>::const_iterator i = numbrdLyrics.constBegin(); i != numbrdLyrics.constEnd(); ++i) {
lyricNo = i.key(); // use number obtained from MusicXML file
lyricNo = i.key();
Lyrics* l = i.value();
addLyric(logger, xmlreader, cr, l, lyricNo, extendedLyrics);
if (extLyrics.contains(l))
extendedLyrics.addLyric(l);
}

// then the lyrics without valid number but with valid default-y
for (QMap<int, Lyrics*>::const_iterator i = defyLyrics.constBegin(); i != defyLyrics.constEnd(); ++i) {
lyricNo++; // use sequence number
Lyrics* l = i.value();
addLyric(logger, xmlreader, cr, l, lyricNo, extendedLyrics);
if (extLyrics.contains(l))
extendedLyrics.addLyric(l);
}

// finally the remaining lyrics, which are simply added in order they appear in the MusicXML file
for (QList<Lyrics*>::const_iterator i = unNumbrdLyrics.constBegin(); i != unNumbrdLyrics.constEnd(); ++i) {
lyricNo++; // use sequence number
Lyrics* l = *i;
addLyric(logger, xmlreader, cr, l, lyricNo, extendedLyrics);
if (extLyrics.contains(l))
extendedLyrics.addLyric(l);
}
}

//---------------------------------------------------------
Expand Down Expand Up @@ -4490,8 +4469,6 @@ Note* MusicXMLParserPass2::note(const QString& partId,
// at a StartElement instead of the usual EndElement

QMap<int, Lyrics*> numberedLyrics; // lyrics with valid number
QMap<int, Lyrics*> defaultyLyrics; // lyrics with valid default-y
QList<Lyrics*> unNumberedLyrics; // lyrics with neither
QSet<Lyrics*> extendedLyrics; // lyrics with the extend flag set
MusicXmlTupletDesc tupletDesc;
bool lastGraceAFter = false; // set by notations() if end of grace after sequence found
Expand All @@ -4502,7 +4479,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,
if (_e.name() == "lyric") {
// lyrics on grace notes not (yet) supported by MuseScore
if (!grace)
lyric(numberedLyrics, defaultyLyrics, unNumberedLyrics, extendedLyrics); // TODO: move track handling to addlyric
lyric(partId, numberedLyrics, extendedLyrics); // TODO: move track handling to addlyric
else {
_logger->logDebugInfo("ignoring lyrics on grace notes", &_e);
skipLogCurrElem();
Expand Down Expand Up @@ -4546,7 +4523,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,
// add lyrics found by lyric
if (cr) {
// add lyrics and stop corresponding extends
addLyrics(_logger, &_e, cr, numberedLyrics, defaultyLyrics, unNumberedLyrics, extendedLyrics, _extendedLyrics);
addLyrics(_logger, &_e, cr, numberedLyrics, extendedLyrics, _extendedLyrics);
if (rest) {
// stop all extends
_extendedLyrics.setExtend(-1, cr->track(), cr->tick());
Expand Down Expand Up @@ -5119,9 +5096,8 @@ void MusicXMLParserPass2::backup(Fraction& dura)
Parse the /score-partwise/part/measure/note/lyric node.
*/

void MusicXMLParserPass2::lyric(QMap<int, Lyrics*>& numbrdLyrics,
QMap<int, Lyrics*>& defyLyrics,
QList<Lyrics*>& unNumbrdLyrics,
void MusicXMLParserPass2::lyric(const QString& partId,
QMap<int, Lyrics*>& numbrdLyrics,
QSet<Lyrics*>& extLyrics)
{
Q_ASSERT(_e.isStartElement() && _e.name() == "lyric");
Expand All @@ -5130,10 +5106,9 @@ void MusicXMLParserPass2::lyric(QMap<int, Lyrics*>& numbrdLyrics,
// TODO in addlyrics: l->setTrack(trk);

bool hasExtend = false;
QString strLyricNo = _e.attributes().value("number").toString();
QString lyricNumber = _e.attributes().value("number").toString();
QColor lyricColor = QColor::Invalid;
lyricColor.setNamedColor(_e.attributes().value("color").toString());
QString strDefaultY = _e.attributes().value("default-y").toString();
QString extendType;
QString formattedText;

Expand Down Expand Up @@ -5178,33 +5153,26 @@ void MusicXMLParserPass2::lyric(QMap<int, Lyrics*>& numbrdLyrics,
return;
}

// put lyric on correct list to be able determine line number later
bool ok = true;
int lyricNo = strLyricNo.toInt(&ok) - 1;
if (ok) {
if (lyricNo < 0) {
qDebug("invalid lyrics number (<0)"); // TODO
delete l;
return;
}
else if (lyricNo > MAX_LYRICS) {
qDebug("too much lyrics (>%d)", MAX_LYRICS); // TODO
delete l;
return;
}
else {
numbrdLyrics[lyricNo] = l;
}
auto mxmlPart = _pass1.getMusicXmlPart(partId);
auto lyricNo = mxmlPart.lyricNumberHandler().getLyricNo(lyricNumber);
if (lyricNo < 0) {
_logger->logError("invalid lyrics number (<0)", &_e);
delete l;
return;
}
else {
int defaultY = strDefaultY.toInt(&ok);
if (ok)
// invert default-y as it decreases with increasing lyric number
defyLyrics[-defaultY] = l;
else
unNumbrdLyrics.append(l);
else if (lyricNo > MAX_LYRICS) {
_logger->logError(QString("too much lyrics (>%1)").arg(MAX_LYRICS), &_e);
delete l;
return;
}
else if (numbrdLyrics.contains(lyricNo)) {
_logger->logError(QString("duplicate lyrics number (%1)").arg(lyricNumber), &_e);
delete l;
return;
}

numbrdLyrics[lyricNo] = l;

if (hasExtend && (extendType == "" || extendType == "start"))
extLyrics.insert(l);

Expand Down Expand Up @@ -5840,13 +5808,13 @@ void MusicXMLParserPass2::notations(Note* note, ChordRest* cr, const int tick,
tuplet(tupletDesc);
}
else if (_e.name() == "dynamics") {
placement = _e.attributes().value("placement").toString();
if (preferences.getBool(PREF_IMPORT_MUSICXML_IMPORTLAYOUT)) {
// ry = ee.attribute(QString("relative-y"), "0").toDouble() * -.1;
// rx = ee.attribute(QString("relative-x"), "0").toDouble() * .1;
// yoffset = _e.attributes().value("default-y").toDouble(&hasYoffset) * -0.1;
// xoffset = ee.attribute("default-x", "0.0").toDouble() * 0.1;
}
placement = _e.attributes().value("placement").toString();
if (preferences.getBool(PREF_IMPORT_MUSICXML_IMPORTLAYOUT)) {
// ry = ee.attribute(QString("relative-y"), "0").toDouble() * -.1;
// rx = ee.attribute(QString("relative-x"), "0").toDouble() * .1;
// yoffset = _e.attributes().value("default-y").toDouble(&hasYoffset) * -0.1;
// xoffset = ee.attribute("default-x", "0.0").toDouble() * 0.1;
}
dynamics(placement, dynamicslist);
}
else if (_e.name() == "articulations") {
Expand Down
15 changes: 7 additions & 8 deletions mscore/importmxmlpass2.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ class MusicXMLParserPass2 {
void backup(Fraction& dura);
void timeModification(Fraction& timeMod, TDuration& normalType);
//void pitch(int& step, int& alter, int& oct, AccidentalType& accid);
void lyric(QMap<int, Lyrics*>& numbrdLyrics, QMap<int, Lyrics*>& defyLyrics,
QList<Lyrics*>& unNumbrdLyrics, QSet<Lyrics*>& extLyrics);
void lyric(const QString& partId, QMap<int, Lyrics*>& numbrdLyrics, QSet<Lyrics*>& extLyrics);
void slur(ChordRest* cr, const int tick, const int track, bool& lastGraceAFter);
void tied(Note* note, const int track);
void articulations(ChordRest* cr, SymId& breath, QString& chordLineType);
Expand Down Expand Up @@ -190,12 +189,12 @@ class MusicXMLParserPass2 {

QVector<Tuplet*> _tuplets; ///< Current tuplet for each track in the current part
QVector<bool> _tuplImpls; ///< Current tuplet implicit flag for each track in the current part
SlurStack _slurs {{}};
TrillStack _trills {{}}; ///< Current trills
BracketsStack _brackets {{}};
DashesStack _dashes {{}};
OttavasStack _ottavas {{}}; ///< Current ottavas
HairpinsStack _hairpins {{}}; ///< Current hairpins
SlurStack _slurs { {} };
TrillStack _trills { {} }; ///< Current trills
BracketsStack _brackets { {} };
DashesStack _dashes { {} };
OttavasStack _ottavas { {} }; ///< Current ottavas
HairpinsStack _hairpins { {} }; ///< Current hairpins

Glissando* _glissandi[MAX_NUMBER_LEVEL][2]; ///< Current slides ([0]) / glissandi ([1])

Expand Down
66 changes: 66 additions & 0 deletions mscore/importxmlfirstpass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,70 @@ void MusicXmlOctaveShiftList::calcOctaveShiftShifts()

}


//---------------------------------------------------------
// LyricNumberHandler
// collect lyric numbering information and determine order
//
// MusicXML lyrics may contain name and number attributes,
// plus position information (typically default-y).
// Name and number are simply tokens with no specified usage.
// Default-y cannot easily be used to determine the lyrics
// line, as it tends to differ per system depending on the
// actual notes present.
//
// Simply collecting all possible lyric number attributes
// within a MusicXML part and assigning lyrics position
// based on alphabetically sorting works well for all
// common MusicXML files.
//---------------------------------------------------------

//---------------------------------------------------------
// addNumber
//---------------------------------------------------------

void LyricNumberHandler::addNumber(const QString number)
{
if (_numberToNo.find(number) == _numberToNo.end())
_numberToNo[number] = -1; // unassiged
}

//---------------------------------------------------------
// toString
//---------------------------------------------------------

QString LyricNumberHandler::toString() const
{
QString res;
for (const auto& p : _numberToNo) {
if (!res.isEmpty())
res += " ";
res += QString("%1:%2").arg(p.first).arg(p.second);
}
return res;
}

//---------------------------------------------------------
// getLyricNo
//---------------------------------------------------------

int LyricNumberHandler::getLyricNo(const QString& number) const
{
const auto it = _numberToNo.find(number);
return it == _numberToNo.end() ? 0 : it->second;
}

//---------------------------------------------------------
// determineLyricNos
//---------------------------------------------------------

void LyricNumberHandler::determineLyricNos()
{
int i = 0;
for (auto& p : _numberToNo) {
p.second = i;
++i;
}
}

}
13 changes: 13 additions & 0 deletions mscore/importxmlfirstpass.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ class MusicXmlOctaveShiftList : public std::map<Fraction, int> {
void calcOctaveShiftShifts();
};

class LyricNumberHandler {
public:
LyricNumberHandler() {}
void addNumber(const QString number);
QString toString() const;
int getLyricNo(const QString& number) const;
void determineLyricNos();
private:
std::map<QString, int> _numberToNo;
};

class MusicXmlPart {
public:
MusicXmlPart(QString id = "", QString name = "");
Expand All @@ -56,6 +67,7 @@ class MusicXmlPart {
QString getAbbr() const { return abbr; }
void setPrintAbbr(bool b) { printAbbr = b; }
bool getPrintAbbr() const { return printAbbr; }
LyricNumberHandler& lyricNumberHandler() { return _lyricNumberHandler; }
private:
QString id;
QString name;
Expand All @@ -65,6 +77,7 @@ class MusicXmlPart {
QStringList measureNumbers; // MusicXML measure number attribute
QList<Fraction> measureDurations; // duration in fraction for every measure
QVector<MusicXmlOctaveShiftList> octaveShifts; // octave shift list for every staff
LyricNumberHandler _lyricNumberHandler;
};

} // namespace Ms
Expand Down
Binary file modified mtest/musicxml/io/testNumberedLyrics.pdf
Binary file not shown.
Loading

0 comments on commit 422efca

Please sign in to comment.