Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve XML instrument import and XML ottava fixes #648

Open
wants to merge 2 commits into
base: 3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 47 additions & 11 deletions importexport/musicxml/importmxmlpass1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,22 @@ void MusicXMLParserPass1::addError(const QString& error)
}
}

void MusicXMLParserPass1::setExporterSoftware(QString& exporter)
{
if (exporter.contains("sibelius")) {
if (exporter.contains("dolet 6"))
_exporterSoftware = MusicXMLExporterSoftware::DOLET6;
else if (exporter.contains("dolet 8"))
_exporterSoftware = MusicXMLExporterSoftware::DOLET8;
else
_exporterSoftware = MusicXMLExporterSoftware::SIBELIUS;
}
else if (exporter.contains("finale"))
_exporterSoftware = MusicXMLExporterSoftware::FINALE;
else if (exporter.contains("noteflight"))
_exporterSoftware = MusicXMLExporterSoftware::NOTEFLIGHT;
}

//---------------------------------------------------------
// initPartState
//---------------------------------------------------------
Expand Down Expand Up @@ -885,7 +901,7 @@ void MusicXMLParserPass1::createMeasuresAndVboxes(Score* const score,
if (pageStartMeasureNrs.count(i) || i == 0) {
++pageNr;
if (pageNr == 1) {
vbox = addCreditWords(score, crWords, pageSize, true, _exporterString.contains("sibelius"));
vbox = addCreditWords(score, crWords, pageSize, true, sibOrDolet());
//if (i == 0 && vbox)
// vbox->setExcludeFromOtherParts(false);
}
Expand All @@ -908,10 +924,20 @@ void MusicXMLParserPass1::createMeasuresAndVboxes(Score* const score,

// add a footer vbox if the next measure is on a new page or end of score has been reached
if ((pageStartMeasureNrs.count(i + 1) || i == (ml.size() - 1)) && pageNr == 1)
addCreditWords(score, crWords, pageSize, false, _exporterString.contains("sibelius"));
addCreditWords(score, crWords, pageSize, false, sibOrDolet());
}
}

bool MusicXMLParserPass1::sibOrDolet() const
{
return _exporterSoftware == MusicXMLExporterSoftware::SIBELIUS || dolet();
}

bool MusicXMLParserPass1::dolet() const
{
return _exporterSoftware == MusicXMLExporterSoftware::DOLET6 || _exporterSoftware == MusicXMLExporterSoftware::DOLET8;
}

//---------------------------------------------------------
// determineMeasureStart
//---------------------------------------------------------
Expand Down Expand Up @@ -1242,8 +1268,10 @@ void MusicXMLParserPass1::identification()
else if (_e.name() == "encoding") {
// TODO
while (_e.readNextStartElement()) {
if (_e.name() == "software")
_exporterString += _e.readElementText().toLower();
if (_e.name() == "software") {
QString exporterString = _e.readElementText().toLower();
setExporterSoftware(exporterString);
}
else if (_e.name() == "supports" && _e.attributes().value("element") == "beam" && _e.attributes().value("type") == "yes") {
_hasBeamingInfo = true;
_e.skipCurrentElement();
Expand Down Expand Up @@ -1916,12 +1944,13 @@ void MusicXMLParserPass1::partList(MusicXmlPartGroupList& partGroupList)

int scoreParts = 0; // number of score-parts read sofar
MusicXmlPartGroupMap partGroups;
QString curPartGroupName;

while (_e.readNextStartElement()) {
if (_e.name() == "part-group")
partGroup(scoreParts, partGroupList, partGroups);
partGroup(scoreParts, partGroupList, partGroups, curPartGroupName);
else if (_e.name() == "score-part") {
scorePart();
scorePart(curPartGroupName);
scoreParts++;
}
else
Expand Down Expand Up @@ -2036,7 +2065,7 @@ static void partGroupStop(MusicXmlPartGroupMap& pgs, int n, int p,

void MusicXMLParserPass1::partGroup(const int scoreParts,
MusicXmlPartGroupList& partGroupList,
MusicXmlPartGroupMap& partGroups)
MusicXmlPartGroupMap& partGroups, QString& curPartGroupName)
{
_logger->logDebugTrace("MusicXMLParserPass1::partGroup", &_e);
bool barlineSpan = true;
Expand All @@ -2048,7 +2077,7 @@ void MusicXMLParserPass1::partGroup(const int scoreParts,

while (_e.readNextStartElement()) {
if (_e.name() == "group-name")
_e.skipCurrentElement(); // skip but don't log
curPartGroupName = _e.readElementText();
else if (_e.name() == "group-abbreviation")
symbol = _e.readElementText();
else if (_e.name() == "group-symbol")
Expand Down Expand Up @@ -2122,7 +2151,7 @@ void MusicXMLParserPass1::addInferredTranspose(const QString& partId)
which is invalid MusicXML but is (sometimes ?) generated by NWC2MusicXML.
*/

void MusicXMLParserPass1::scorePart()
void MusicXMLParserPass1::scorePart(const QString& curPartGroupName)
{
_logger->logDebugTrace("MusicXMLParserPass1::scorePart", &_e);
QString id = _e.attributes().value("id").toString().trimmed();
Expand Down Expand Up @@ -2186,7 +2215,7 @@ void MusicXMLParserPass1::scorePart()
else if (_e.name() == "group") // TODO
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "score-instrument")
scoreInstrument(id);
scoreInstrument(id, curPartGroupName);
else if (_e.name() == "player") // unsupported
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "midi-device") {
Expand Down Expand Up @@ -2222,7 +2251,7 @@ void MusicXMLParserPass1::scorePart()
Parse the /score-partwise/part-list/score-part/score-instrument node.
*/

void MusicXMLParserPass1::scoreInstrument(const QString& partId)
void MusicXMLParserPass1::scoreInstrument(const QString& partId, const QString& curPartGroupName)
{
_logger->logDebugTrace("MusicXMLParserPass1::scoreInstrument", &_e);
QString instrId = _e.attributes().value("id").toString();
Expand All @@ -2239,6 +2268,13 @@ void MusicXMLParserPass1::scoreInstrument(const QString& partId)
qPrintable(instrName)
);
*/

// Finale exports all instrument names as 'Grand Piano' - use part name
if (exporterSoftware() == MusicXMLExporterSoftware::FINALE) {
instrName = _parts[partId].getName();
if (instrName.size() <= 1)
instrName = curPartGroupName;
}
_instruments[partId].insert(instrId, MusicXMLInstrument(instrName));
// Element instrument-name is typically not displayed in the score,
// but used only internally
Expand Down
22 changes: 17 additions & 5 deletions importexport/musicxml/importmxmlpass1.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ enum class MxmlTupletFlag : char {
STOP_CURRENT = 8
};

enum class MusicXMLExporterSoftware : char {
SIBELIUS,
DOLET6,
DOLET8,
FINALE,
NOTEFLIGHT,
OTHER
};

typedef QFlags<MxmlTupletFlag> MxmlTupletFlags;

struct MxmlTupletState {
Expand Down Expand Up @@ -128,10 +137,10 @@ class MusicXMLParserPass1 {
void defaults();
void pageLayout(MxmlPageFormat& pf, const qreal conversion);
void partList(MusicXmlPartGroupList& partGroupList);
void partGroup(const int scoreParts, MusicXmlPartGroupList& partGroupList, MusicXmlPartGroupMap& partGroups);
void scorePart();
void partGroup(const int scoreParts, MusicXmlPartGroupList& partGroupList, MusicXmlPartGroupMap& partGroups, QString& curPartGroupName);
void scorePart(const QString& curPartGroupName);
void setStyle(const QString& type, const double val);
void scoreInstrument(const QString& partId);
void scoreInstrument(const QString& partId, const QString& curPartGroupName);
void midiInstrument(const QString& partId);
void part();
void measure(const QString& partId, const Fraction cTime, Fraction& mdur, VoiceOverlapDetector& vod, const int measureNr);
Expand Down Expand Up @@ -191,15 +200,18 @@ class MusicXMLParserPass1 {
void addInferredTranspose(const QString& partId);
void setHasInferredHeaderText(bool b) { _hasInferredHeaderText = b; }
bool hasInferredHeaderText() const { return _hasInferredHeaderText; }
QString exporterString() const { return _exporterString; }
MusicXMLExporterSoftware exporterSoftware() const { return _exporterSoftware; }
bool sibOrDolet() const;
bool dolet() const;

private:
// functions
void addError(const QString& error); ///< Add an error to be shown in the GUI
void setExporterSoftware(QString& exporter);

// generic pass 1 data
QXmlStreamReader _e;
QString _exporterString; ///< Name of the software which exported the file
MusicXMLExporterSoftware _exporterSoftware = MusicXMLExporterSoftware::OTHER; // Software which exported the file
int _divs; ///< Current MusicXML divisions value
QMap<QString, MusicXmlPart> _parts; ///< Parts data, mapped on part id
std::set<int> _systemStartMeasureNrs; ///< Measure numbers of measures starting a page
Expand Down
46 changes: 30 additions & 16 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2298,7 +2298,7 @@ void MusicXMLParserPass2::part()
// Clean up unterminated ties
for (auto tie : _ties) {
if (tie.second) {
cleanupUnterminatedTie(tie.second, _score, _pass1.exporterString().contains("dolet 6"));
cleanupUnterminatedTie(tie.second, _score, _pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6);
_ties[tie.first] = nullptr;
}
}
Expand Down Expand Up @@ -3188,7 +3188,7 @@ void MusicXMLParserDirection::direction(const QString& partId,
int track = _pass1.trackForPart(partId);
bool isVocalStaff = _pass1.isVocalStaff(partId);
bool isExpressionText = false;
bool delayOttava = _pass1.exporterString().contains("sibelius");
bool delayOttava = _pass1.exporterSoftware() == MusicXMLExporterSoftware::SIBELIUS;
_systemDirection = _e.attributes().value("system").toString() == "only-top";
//qDebug("direction track %d", track);
QList<MusicXmlSpannerDesc> starts;
Expand Down Expand Up @@ -3463,21 +3463,25 @@ void MusicXMLParserDirection::direction(const QString& partId,
}
else {
if (spdesc._isStarted) {
// Adjustments to ottavas by the offset value are unwanted
const Fraction spTick = spdesc._sp && spdesc._sp->isOttava() ? tick : tick + _offset;
if (spdesc._sp && spdesc._sp->isOttava() && delayOttava) {
// Sibelius writes ottava ends 1 note too early
_pass2.setDelayedOttava(spdesc._sp);
_pass2.delayedOttava()->setTrack2(track);
_pass2.delayedOttava()->setTick2(tick + _offset);
_pass2.delayedOttava()->setTick2(spTick);
// need to set tick again later
_pass2.clearSpanner(desc);
}
else {
handleSpannerStop(spdesc._sp, track, tick + _offset, spanners);
handleSpannerStop(spdesc._sp, track, spTick, spanners);
_pass2.clearSpanner(desc);
}
}
else {
spdesc._sp = desc._sp;
spdesc._tick2 = tick + _offset;
const Fraction spTick = spdesc._sp && spdesc._sp->isOttava() ? tick : tick + _offset;
spdesc._tick2 = spTick;
spdesc._track2 = track;
spdesc._isStopped = true;
}
Expand Down Expand Up @@ -3667,7 +3671,7 @@ void MusicXMLParserDirection::otherDirection()
// TODO: Multiple sets of maps for exporters other than Dolet 6/Sibelius
// TODO: Add more symbols from Sibelius
QMap<QString, QString> otherDirectionStrings;
if (_pass1.exporterString().contains("dolet")) {
if (_pass1.dolet()) {
otherDirectionStrings = {
{ QString("To Coda"), QString("To Coda") },
{ QString("Segno"), QString("<sym>segno</sym>") },
Expand Down Expand Up @@ -4479,12 +4483,20 @@ void MusicXMLParserDirection::octaveShift(const QString& type, const int number,
else {
Ottava* o = spdesc._isStopped ? toOttava(spdesc._sp) : new Ottava(_score);

// if (placement.isEmpty()) placement = "above"; // TODO ? set default

if (type == "down" && ottavasize == 8) o->setOttavaType(OttavaType::OTTAVA_8VA);
if (type == "down" && ottavasize == 15) o->setOttavaType(OttavaType::OTTAVA_15MA);
if (type == "up" && ottavasize == 8) o->setOttavaType(OttavaType::OTTAVA_8VB);
if (type == "up" && ottavasize == 15) o->setOttavaType(OttavaType::OTTAVA_15MB);
if (type == "down") {
_placement = _placement.isEmpty() ? "above" : _placement;
if (ottavasize == 8)
o->setOttavaType(OttavaType::OTTAVA_8VA);
else if (ottavasize == 15)
o->setOttavaType(OttavaType::OTTAVA_15MA);
}
else /*if (type == "up")*/ {
_placement = _placement.isEmpty() ? "below" : _placement;
if (ottavasize == 8)
o->setOttavaType(OttavaType::OTTAVA_8VB);
else if (ottavasize == 15)
o->setOttavaType(OttavaType::OTTAVA_15MB);
}

const QColor color { _e.attributes().value("color").toString() };
if (color.isValid())
Expand Down Expand Up @@ -6095,7 +6107,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,
QString noteheadValue = _e.readElementText();
if (noteheadValue == "none")
hasHead = false;
else if (noteheadValue == "named" && _pass1.exporterString().contains("noteflight"))
else if (noteheadValue == "named" && _pass1.exporterSoftware() == MusicXMLExporterSoftware::NOTEFLIGHT)
headScheme = NoteHead::Scheme::HEAD_PITCHNAME;
else
headGroup = convertNotehead(noteheadValue);
Expand Down Expand Up @@ -6390,7 +6402,8 @@ Note* MusicXMLParserPass2::note(const QString& partId,
Notation notation { "tied" };
const QString ctype { "type" };
notation.addAttribute(&ctype, &tieType);
addTie(notation, _score, note, cr->track(), _ties, _logger, &_e, _pass1.exporterString().contains("dolet 6"));
addTie(notation, _score, note, cr->track(), _ties, _logger, &_e,
_pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6);
}
}

Expand Down Expand Up @@ -7001,7 +7014,7 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const

const HarmonyDesc newHarmonyDesc(track, ha, fd);
bool insert = true;
if (_pass1.exporterString().contains("dolet")) {
if (_pass1.sibOrDolet()) {
const int ticks = (sTime + offset).ticks();
for (auto itr = harmonyMap.begin(); itr != harmonyMap.end(); itr++) {
if (itr->first != ticks)
Expand Down Expand Up @@ -8171,7 +8184,8 @@ void MusicXMLParserNotations::addToScore(ChordRest* const cr, Note* const note,
addGlissandoSlide(notation, note, glissandi, spanners, _logger, &_e);
}
else if (note && notation.name() == "tied") {
addTie(notation, _score, note, cr->track(), ties, _logger, &_e, _pass1.exporterString().contains("dolet 6"));
addTie(notation, _score, note, cr->track(), ties, _logger, &_e,
_pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6);
}
else if (note && notation.parent() == "technical") {
addTechnical(notation, note);
Expand Down
Binary file added mtest/musicxml/io/testDoletOttavas.xml
Binary file not shown.
Loading
Loading