diff --git a/src/importexport/musicxml/imusicxmlconfiguration.h b/src/importexport/musicxml/imusicxmlconfiguration.h
index 106aabe314039..cd4df3cecb515 100644
--- a/src/importexport/musicxml/imusicxmlconfiguration.h
+++ b/src/importexport/musicxml/imusicxmlconfiguration.h
@@ -19,8 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef MU_IMPORTEXPORT_IMUSICXMLCONFIGURATION_H
-#define MU_IMPORTEXPORT_IMUSICXMLCONFIGURATION_H
+#pragma once
#include "modularity/imoduleinterface.h"
#include "io/path.h"
@@ -67,5 +66,3 @@ class IMusicXmlConfiguration : MODULE_EXPORT_INTERFACE
virtual void setInferTextTypeOverride(std::optional value) = 0;
};
}
-
-#endif // MU_IMPORTEXPORT_IMUSICXMLCONFIGURATION_H
diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.cpp b/src/importexport/musicxml/internal/musicxml/exportxml.cpp
index 00fab5c16acdd..eb5ea6a487bed 100644
--- a/src/importexport/musicxml/internal/musicxml/exportxml.cpp
+++ b/src/importexport/musicxml/internal/musicxml/exportxml.cpp
@@ -124,9 +124,9 @@
#include "engraving/dom/volta.h"
#include "engraving/dom/whammybar.h"
-#include "musicxml.h"
#include "musicxmlfonthandler.h"
#include "musicxmlsupport.h"
+#include "musicxmltypes.h"
#include "modularity/ioc.h"
#include "../../imusicxmlconfiguration.h"
@@ -157,6 +157,8 @@ namespace mu::engraving {
#define clefDebug(...) {}
#endif
+constexpr int MAX_PART_GROUPS = 8;
+
//---------------------------------------------------------
// typedefs
//---------------------------------------------------------
diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.h b/src/importexport/musicxml/internal/musicxml/exportxml.h
index d6d488ae71fa6..1c29d85a83994 100644
--- a/src/importexport/musicxml/internal/musicxml/exportxml.h
+++ b/src/importexport/musicxml/internal/musicxml/exportxml.h
@@ -20,18 +20,14 @@
* along with this program. If not, see .
*/
-#ifndef MU_IMPORTEXPORT_EXPORTXML_H
-#define MU_IMPORTEXPORT_EXPORTXML_H
+#pragma once
#include "io/iodevice.h"
#include "global/types/string.h"
namespace mu::engraving {
class Score;
-
bool saveMxl(Score*, muse::io::IODevice*);
bool saveXml(Score*, muse::io::IODevice*);
bool saveXml(Score*, const muse::String&);
}
-
-#endif // MU_IMPORTEXPORT_EXPORTXML_H
diff --git a/src/importexport/musicxml/internal/musicxml/importmxml.h b/src/importexport/musicxml/internal/musicxml/importmxml.h
index f7813d4c1be56..7a476d085ec48 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxml.h
+++ b/src/importexport/musicxml/internal/musicxml/importmxml.h
@@ -20,8 +20,7 @@
* along with this program. If not, see .
*/
-#ifndef __IMPORTMXML_H__
-#define __IMPORTMXML_H__
+#pragma once
#include "global/types/string.h"
#include "engravingerrors.h"
@@ -31,5 +30,3 @@ class Score;
Err importMusicXMLfromBuffer(Score* score, const muse::String&, const muse::ByteArray& data);
}
-
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmllogger.h b/src/importexport/musicxml/internal/musicxml/importmxmllogger.h
index 71ba0885b57f2..fbca301eae13d 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmllogger.h
+++ b/src/importexport/musicxml/internal/musicxml/importmxmllogger.h
@@ -20,8 +20,7 @@
* along with this program. If not, see .
*/
-#ifndef __IMPORTMXMLLOGGER_H__
-#define __IMPORTMXMLLOGGER_H__
+#pragma once
#include "global/types/string.h"
@@ -45,5 +44,3 @@ class MxmlLogger
Level m_level = Level::MXML_INFO;
};
} // namespace Ms
-
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h
index 50610a48917cf..f343c7b84e110 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h
+++ b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h
@@ -20,8 +20,7 @@
* along with this program. If not, see .
*/
-#ifndef __IMPORTMXMLNOTEDURATION_H__
-#define __IMPORTMXMLNOTEDURATION_H__
+#pragma once
#include "engraving/dom/durationtype.h"
#include "engraving/types/fraction.h"
@@ -66,5 +65,3 @@ class MxmlNoteDuration
MxmlLogger* m_logger = nullptr; ///< Error logger
};
} // namespace Ms
-
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h b/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h
index 3bdb09e32d97a..dacec58ab7ccf 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h
+++ b/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h
@@ -20,8 +20,7 @@
* along with this program. If not, see .
*/
-#ifndef __IMPORTMXMLNOTEPITCH_H__
-#define __IMPORTMXMLNOTEPITCH_H__
+#pragma once
#include "global/serialization/xmlstreamreader.h"
#include "engraving/dom/accidental.h"
@@ -69,5 +68,3 @@ class MxmlNotePitch
MxmlLogger* m_logger = nullptr; // Error logger
};
} // namespace Ms
-
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp
index 41febe37340ec..422386fa0fd4f 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp
+++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp
@@ -43,6 +43,7 @@
#include "importmxmllogger.h"
#include "importmxmlnoteduration.h"
#include "importmxmlpass1.h"
+#include "musicxmltypes.h"
#include "modularity/ioc.h"
#include "importexport/musicxml/imusicxmlconfiguration.h"
@@ -78,6 +79,170 @@ static bool musicxmlImportLayout()
}
namespace mu::engraving {
+//---------------------------------------------------------
+// NoteList
+//---------------------------------------------------------
+
+/**
+ List of note start/stop times in a voice in all staves.
+*/
+
+class NoteList
+{
+public:
+ NoteList();
+ void addNote(const int startTick, const int endTick, const size_t staff);
+ void dump(const int& voice) const;
+ bool stavesOverlap(const int staff1, const int staff2) const;
+ bool anyStaffOverlaps() const;
+private:
+ std::vector _staffNoteLists; // The note start/stop times in all staves
+ bool notesOverlap(const StartStop& n1, const StartStop& n2) const;
+};
+
+//---------------------------------------------------------
+// VoiceOverlapDetector
+//---------------------------------------------------------
+
+/**
+ Detect overlap in a voice, which is when a voice has two or more notes
+ active at the same time. In theory this should not happen, as voices
+ only move forward in time, but Sibelius 7 reuses voice numbers in multi-
+ staff parts, which leads to overlap.
+
+ Current implementation does not detect voice overlap within a staff,
+ but only between staves.
+*/
+
+class VoiceOverlapDetector
+{
+public:
+ VoiceOverlapDetector();
+ void addNote(const int startTick, const int endTick, const int& voice, const int staff);
+ void dump() const;
+ void newMeasure();
+ bool stavesOverlap(const int& voice) const;
+private:
+ std::map _noteLists; // The notelists for all the voices
+};
+
+NoteList::NoteList()
+{
+ _staffNoteLists.reserve(MAX_STAVES);
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ _staffNoteLists.push_back(StartStopList());
+ }
+}
+
+void NoteList::addNote(const int startTick, const int endTick, const size_t staff)
+{
+ if (staff < _staffNoteLists.size()) {
+ _staffNoteLists[staff].push_back(StartStop(startTick, endTick));
+ }
+}
+
+void NoteList::dump(const int& voice) const
+{
+ // dump contents
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ printf("voice %d staff %d:", voice, i);
+ for (size_t j = 0; j < _staffNoteLists.at(i).size(); ++j) {
+ printf(" %d-%d", _staffNoteLists.at(i).at(j).first, _staffNoteLists.at(i).at(j).second);
+ }
+ printf("\n");
+ }
+ // show overlap
+ printf("overlap voice %d:", voice);
+ for (int i = 0; i < MAX_STAVES - 1; ++i) {
+ for (int j = i + 1; j < MAX_STAVES; ++j) {
+ stavesOverlap(i, j);
+ }
+ }
+ printf("\n");
+}
+
+/**
+ Determine if notes n1 and n2 overlap.
+ This is NOT the case if
+ - n1 starts when or after n2 stops
+ - or n2 starts when or after n1 stops
+ */
+
+bool NoteList::notesOverlap(const StartStop& n1, const StartStop& n2) const
+{
+ return !(n1.first >= n2.second || n1.second <= n2.first);
+}
+
+/**
+ Determine if any note in staff1 and staff2 overlaps.
+ */
+
+bool NoteList::stavesOverlap(const int staff1, const int staff2) const
+{
+ for (size_t i = 0; i < _staffNoteLists.at(staff1).size(); ++i) {
+ for (size_t j = 0; j < _staffNoteLists.at(staff2).size(); ++j) {
+ if (notesOverlap(_staffNoteLists.at(staff1).at(i), _staffNoteLists.at(staff2).at(j))) {
+ //printf(" %d-%d", staff1, staff2);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ Determine if any note in any staff overlaps.
+ */
+
+bool NoteList::anyStaffOverlaps() const
+{
+ for (int i = 0; i < MAX_STAVES - 1; ++i) {
+ for (int j = i + 1; j < MAX_STAVES; ++j) {
+ if (stavesOverlap(i, j)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+VoiceOverlapDetector::VoiceOverlapDetector()
+{
+ // LOGD("VoiceOverlapDetector::VoiceOverlapDetector(staves %d)", MAX_STAVES);
+}
+
+void VoiceOverlapDetector::addNote(const int startTick, const int endTick, const int& voice, const int staff)
+{
+ // if necessary, create the note list for voice
+ if (!muse::contains(_noteLists, voice)) {
+ _noteLists.insert({ voice, NoteList() });
+ }
+ _noteLists[voice].addNote(startTick, endTick, staff);
+}
+
+void VoiceOverlapDetector::dump() const
+{
+ // LOGD("VoiceOverlapDetector::dump()");
+ for (auto& p : _noteLists) {
+ p.second.dump(p.first);
+ }
+}
+
+void VoiceOverlapDetector::newMeasure()
+{
+ // LOGD("VoiceOverlapDetector::newMeasure()");
+ _noteLists.clear();
+}
+
+bool VoiceOverlapDetector::stavesOverlap(const int& voice) const
+{
+ if (muse::contains(_noteLists, voice)) {
+ return _noteLists.at(voice).anyStaffOverlaps();
+ } else {
+ return false;
+ }
+}
+
//---------------------------------------------------------
// allocateStaves
//---------------------------------------------------------
@@ -3100,252 +3265,6 @@ void MusicXMLParserPass1::notations(MxmlStartStop& tupletStartStop)
}
}
-//---------------------------------------------------------
-// smallestTypeAndCount
-//---------------------------------------------------------
-
-/**
- Determine the smallest note type and the number of those
- present in a ChordRest.
- For a note without dots the type equals the note type
- and count is one.
- For a single dotted note the type equals half the note type
- and count is three.
- A double dotted note is similar.
- Note: code assumes when duration().type() is incremented,
- the note length is divided by two, checked by tupletAssert().
- */
-
-static void smallestTypeAndCount(const TDuration durType, int& type, int& count)
-{
- type = int(durType.type());
- count = 1;
- switch (durType.dots()) {
- case 0:
- // nothing to do
- break;
- case 1:
- type += 1; // next-smaller type
- count = 3;
- break;
- case 2:
- type += 2; // next-next-smaller type
- count = 7;
- break;
- default:
- LOGD("smallestTypeAndCount() does not support more than 2 dots");
- }
-}
-
-//---------------------------------------------------------
-// matchTypeAndCount
-//---------------------------------------------------------
-
-/**
- Given two note types and counts, if the types are not equal,
- make them equal by successively doubling the count of the
- largest type.
- */
-
-static void matchTypeAndCount(int& type1, int& count1, int& type2, int& count2)
-{
- while (type1 < type2) {
- type1++;
- count1 *= 2;
- }
- while (type2 < type1) {
- type2++;
- count2 *= 2;
- }
-}
-
-//---------------------------------------------------------
-// addDurationToTuplet
-//---------------------------------------------------------
-
-/**
- Add duration to tuplet duration
- Determine type and number of smallest notes in the tuplet
- */
-
-void MxmlTupletState::addDurationToTuplet(const Fraction dur, const Fraction timeMod)
-{
- /*
- LOGD("1 duration %s timeMod %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d",
- muPrintable(duration.print()),
- muPrintable(timeMod.print()),
- m_tupletType,
- m_tupletCount,
- m_actualNotes,
- m_normalNotes
- );
- */
- if (duration <= Fraction(0, 1)) {
- // first note: init variables
- actualNotes = timeMod.denominator();
- normalNotes = timeMod.numerator();
- smallestTypeAndCount(dur / timeMod, tupletType, tupletCount);
- } else {
- int noteType = 0;
- int noteCount = 0;
- smallestTypeAndCount(dur / timeMod, noteType, noteCount);
- // match the types
- matchTypeAndCount(tupletType, tupletCount, noteType, noteCount);
- tupletCount += noteCount;
- }
- duration += dur;
- /*
- LOGD("2 duration %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d",
- muPrintable(duration.print()),
- m_tupletType,
- m_tupletCount,
- m_actualNotes,
- m_normalNotes
- );
- */
-}
-
-//---------------------------------------------------------
-// determineTupletFractionAndFullDuration
-//---------------------------------------------------------
-
-/**
- Split duration into two factors where fullDuration is note sized
- (i.e. the denominator is a power of 2), 1/2 < fraction <= 1/1
- and fraction * fullDuration equals duration.
- */
-
-void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration)
-{
- fraction = duration;
- fullDuration = Fraction(1, 1);
- // move denominator's powers of 2 from fraction to fullDuration
- while (fraction.denominator() % 2 == 0) {
- fraction *= 2;
- fraction.reduce();
- fullDuration *= Fraction(1, 2);
- }
- // move numerator's powers of 2 from fraction to fullDuration
- while (fraction.numerator() % 2 == 0) {
- fraction *= Fraction(1, 2);
- fraction.reduce();
- fullDuration *= 2;
- fullDuration.reduce();
- }
- // make sure 1/2 < fraction <= 1/1
- while (fraction <= Fraction(1, 2)) {
- fullDuration *= Fraction(1, 2);
- fraction *= 2;
- }
- fullDuration.reduce();
- fraction.reduce();
-
- /*
- Examples (note result when denominator is not a power of two):
- 3:2 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/2
- 2:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4
- 4:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4
- 3:4 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/1
-
- Bring back fraction in 1/2 .. 1/1 range.
- */
-
- if (fraction > Fraction(1, 1) && fraction.denominator() == 1) {
- fullDuration *= fraction;
- fullDuration.reduce();
- fraction = Fraction(1, 1);
- }
-
- /*
- LOGD("duration %s fraction %s fullDuration %s",
- muPrintable(duration.toString()),
- muPrintable(fraction.toString()),
- muPrintable(fullDuration.toString())
- );
- */
-}
-
-//---------------------------------------------------------
-// isTupletFilled
-//---------------------------------------------------------
-
-/**
- Determine if the tuplet is completely filled,
- because either (1) it is at least the same duration
- as the specified number of the specified normal type notes
- or (2) the duration adds up to a normal note duration.
-
- Example (1): a 3:2 tuplet with a 1/4 and a 1/8 note
- is filled if normal type is 1/8,
- it is not filled if normal type is 1/4.
-
- Example (2): a 3:2 tuplet with a 1/4 and a 1/8 note is filled.
- */
-
-static bool isTupletFilled(const MxmlTupletState& state, const TDuration normalType, const Fraction timeMod)
-{
- UNUSED(timeMod);
- bool res = false;
- const int actualNotes = state.actualNotes;
- /*
- const auto normalNotes = state.m_normalNotes;
- LOGD("duration %s normalType %s timeMod %s normalNotes %d actualNotes %d",
- muPrintable(state.m_duration.toString()),
- muPrintable(normalType.fraction().toString()),
- muPrintable(timeMod.toString()),
- normalNotes,
- actualNotes
- );
- */
-
- int tupletType = state.tupletType;
- int tupletCount = state.tupletCount;
-
- if (normalType.isValid()) {
- int matchedNormalType = int(normalType.type());
- int matchedNormalCount = actualNotes;
- // match the types
- matchTypeAndCount(tupletType, tupletCount, matchedNormalType, matchedNormalCount);
- // ... result scenario (1)
- res = tupletCount >= matchedNormalCount;
- /*
- LOGD("normalType valid tupletType %d tupletCount %d matchedNormalType %d matchedNormalCount %d res %d",
- tupletType,
- tupletCount,
- matchedNormalType,
- matchedNormalCount,
- res
- );
- */
- } else {
- // ... result scenario (2)
- res = tupletCount >= actualNotes;
- /*
- LOGD("normalType not valid tupletCount %d actualNotes %d res %d",
- tupletCount,
- actualNotes,
- res
- );
- */
- }
- return res;
-}
-
-//---------------------------------------------------------
-// missingTupletDuration
-//---------------------------------------------------------
-
-Fraction missingTupletDuration(const Fraction duration)
-{
- Fraction tupletFraction;
- Fraction tupletFullDuration;
-
- determineTupletFractionAndFullDuration(duration, tupletFraction, tupletFullDuration);
- Fraction missing = (Fraction(1, 1) - tupletFraction) * tupletFullDuration;
-
- return missing;
-}
-
//---------------------------------------------------------
// voiceToInt
//---------------------------------------------------------
@@ -3369,103 +3288,6 @@ int MusicXMLParserPass1::voiceToInt(const String& voice)
return voiceInt;
}
-//---------------------------------------------------------
-// determineTupletAction
-//---------------------------------------------------------
-
-/**
- Update tuplet state using parse result tupletDesc.
- Tuplets with and but without
- are handled correctly.
- TODO Nested tuplets are not (yet) supported.
- */
-
-MxmlTupletFlags MxmlTupletState::determineTupletAction(const Fraction noteDuration,
- const Fraction timeMod,
- const MxmlStartStop tupletStartStop,
- const TDuration normalType,
- Fraction& missingPreviousDuration,
- Fraction& missingCurrentDuration)
-{
- const int actNotes = timeMod.denominator();
- const int norNotes = timeMod.numerator();
- MxmlTupletFlags res = MxmlTupletFlag::NONE;
-
- // check for unexpected termination of previous tuplet
- if (inTuplet && timeMod == Fraction(1, 1)) {
- // recover by simply stopping the current tuplet first
- if (!isTupletFilled(*this, normalType, timeMod)) {
- missingPreviousDuration = missingTupletDuration(duration);
- //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print()));
- }
- *this = {};
- res |= MxmlTupletFlag::STOP_PREVIOUS;
- }
-
- // check for obvious errors
- if (inTuplet && tupletStartStop == MxmlStartStop::START) {
- LOGD("tuplet already started");
- // recover by simply stopping the current tuplet first
- if (!isTupletFilled(*this, normalType, timeMod)) {
- missingPreviousDuration = missingTupletDuration(duration);
- //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print()));
- }
- *this = {};
- res |= MxmlTupletFlag::STOP_PREVIOUS;
- }
- if (tupletStartStop == MxmlStartStop::STOP && !inTuplet) {
- LOGD("tuplet stop but no tuplet started"); // TODO
- // recovery handled later (automatically, no special case needed)
- }
-
- // Tuplet are either started by the tuplet start
- // or when the time modification is first found.
- if (!inTuplet) {
- if (tupletStartStop == MxmlStartStop::START
- || (!inTuplet && (actNotes != 1 || norNotes != 1))) {
- if (tupletStartStop != MxmlStartStop::START) {
- implicit = true;
- } else {
- implicit = false;
- }
- // create a new tuplet
- inTuplet = true;
- res |= MxmlTupletFlag::START_NEW;
- }
- }
-
- // Add chord to the current tuplet.
- // Must also check for actual/normal notes to prevent
- // adding one chord too much if tuplet stop is missing.
- if (inTuplet && !(actNotes == 1 && norNotes == 1)) {
- addDurationToTuplet(noteDuration, timeMod);
- res |= MxmlTupletFlag::ADD_CHORD;
- }
-
- // Tuplets are stopped by the tuplet stop
- // or when the tuplet is filled completely
- // (either with knowledge of the normal type
- // or as a last resort calculated based on
- // actual and normal notes plus total duration)
- // or when the time-modification is not found.
-
- if (inTuplet) {
- if (tupletStartStop == MxmlStartStop::STOP
- || (implicit && isTupletFilled(*this, normalType, timeMod))
- || (actNotes == 1 && norNotes == 1)) { // incorrect ??? check scenario incomplete tuplet w/o start
- if (actNotes > norNotes && !isTupletFilled(*this, normalType, timeMod)) {
- missingCurrentDuration = missingTupletDuration(duration);
- LOGD("current tuplet incomplete, missing %s", muPrintable(missingCurrentDuration.toString()));
- }
-
- *this = {};
- res |= MxmlTupletFlag::STOP_CURRENT;
- }
- }
-
- return res;
-}
-
//---------------------------------------------------------
// note
//---------------------------------------------------------
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h
index a3f6465a7d4cf..acbc98cc720a9 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h
+++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h
@@ -20,22 +20,22 @@
* along with this program. If not, see .
*/
-#ifndef __IMPORTMXMLPASS1_H__
-#define __IMPORTMXMLPASS1_H__
+#pragma once
#include "global/serialization/xmlstreamreader.h"
#include "global/containers.h"
-#include "global/types/flags.h"
#include "draw/types/geometry.h"
-#include "importxmlfirstpass.h"
-#include "musicxml.h" // for the creditwords and MusicXmlPartGroupList definitions
#include "musicxmlsupport.h"
+#include "musicxmltypes.h"
+#include "musicxmltupletstate.h"
+#include "musicxmlpart.h"
#include "engraving/engravingerrors.h"
namespace mu::engraving {
class Score;
+class VoiceOverlapDetector;
//---------------------------------------------------------
// PageFormat
@@ -53,8 +53,8 @@ struct PageFormat {
bool twosided = false;
};
-typedef std::map PartMap;
-typedef std::map MusicXmlPartGroupMap;
+typedef std::pair StartStop;
+typedef std::vector StartStopList;
//---------------------------------------------------------
// MxmlOctaveShiftDesc
@@ -75,53 +75,54 @@ struct MxmlOctaveShiftDesc {
};
//---------------------------------------------------------
-// MxmlStartStop (also used in pass 2)
+// MusicXmlPartGroup
//---------------------------------------------------------
-enum class MxmlStartStop : char {
- NONE, START, STOP
-};
-
-enum class MxmlTupletFlag : char {
- NONE = 0,
- STOP_PREVIOUS = 1,
- START_NEW = 2,
- ADD_CHORD = 4,
- STOP_CURRENT = 8
+struct MusicXmlPartGroup {
+ int span = 0;
+ int start = 0;
+ BracketType type = BracketType::NO_BRACKET;
+ bool barlineSpan = false;
+ int column = 0;
};
+typedef std::vector MusicXmlPartGroupList;
+typedef std::map PartMap;
+typedef std::map MusicXmlPartGroupMap;
-enum class MusicXMLExporterSoftware : char {
- SIBELIUS,
- DOLET6,
- DOLET8,
- FINALE,
- NOTEFLIGHT,
- OTHER
-};
+//---------------------------------------------------------
+// CreditWords
+// a single parsed MusicXML credit-words element
+//---------------------------------------------------------
-typedef muse::Flags MxmlTupletFlags;
-
-struct MxmlTupletState {
- void addDurationToTuplet(const Fraction duration, const Fraction timeMod);
- MxmlTupletFlags determineTupletAction(const Fraction noteDuration, const Fraction timeMod, const MxmlStartStop tupletStartStop,
- const TDuration normalType, Fraction& missingPreviousDuration, Fraction& missingCurrentDuration);
- bool inTuplet = false;
- bool implicit = false;
- int actualNotes = 1;
- int normalNotes = 1;
- Fraction duration { 0, 1 };
- int tupletType = 0; // smallest note type in the tuplet // TODO_NOW rename ?
- int tupletCount = 0; // number of smallest notes in the tuplet // TODO_NOW rename ?
+struct CreditWords {
+ int page = 0;
+ String type;
+ double defaultX = 0.0;
+ double defaultY = 0.0;
+ double fontSize = 0.0;
+ String justify;
+ String hAlign;
+ String vAlign;
+ String words;
+ CreditWords(int p, String tp, double dx, double dy, double fs, String j, String ha, String va, String w)
+ {
+ page = p;
+ type = tp;
+ defaultX = dx;
+ defaultY = dy;
+ fontSize = fs;
+ justify = j;
+ hAlign = ha;
+ vAlign = va;
+ words = w;
+ }
};
-
-using MxmlTupletStates = std::map;
+typedef std::vector CreditWordsList;
//---------------------------------------------------------
// declarations
//---------------------------------------------------------
-void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration);
-Fraction missingTupletDuration(const Fraction duration);
bool isLikelyCreditText(const String& text, const bool caseInsensitive);
bool isLikelySubtitleText(const String& text, const bool caseInsensitive);
@@ -241,4 +242,3 @@ class MusicXMLParserPass1
std::set m_seenDenominators; // Denominators seen. Used for rounding errors.
};
} // namespace Ms
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp
index d3594d09fc6d9..9f4bf2c0ba1e1 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp
+++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp
@@ -91,6 +91,7 @@
#include "importmxmlpass2.h"
#include "musicxmlfonthandler.h"
#include "musicxmlsupport.h"
+#include "musicxmltypes.h"
#include "modularity/ioc.h"
#include "importexport/musicxml/imusicxmlconfiguration.h"
@@ -121,6 +122,8 @@ static std::shared_ptr engravingFonts()
//#define DEBUG_VOICE_MAPPER true
+constexpr int MAX_LYRICS = 16;
+
//---------------------------------------------------------
// function declarations
//---------------------------------------------------------
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h
index 2b3dc6608be0f..11af49954a51a 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h
+++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h
@@ -20,15 +20,14 @@
* along with this program. If not, see .
*/
-#ifndef __IMPORTMXMLPASS2_H__
-#define __IMPORTMXMLPASS2_H__
+#pragma once
#include
#include "importmxmlpass1.h"
-#include "importxmlfirstpass.h"
#include "internal/musicxml/musicxmlsupport.h"
-#include "musicxml.h" // a.o. for Slur
+#include "musicxmltypes.h"
+#include "musicxmltupletstate.h"
#include "engraving/dom/instrument.h"
#include "engraving/dom/types.h"
@@ -46,15 +45,6 @@ using FiguredBassList = std::vector;
using Tuplets = std::map;
using Beams = std::map;
-//---------------------------------------------------------
-// MxmlStartStop
-//---------------------------------------------------------
-/*
-enum class MxmlStartStop : char {
- START, STOP, NONE
- };
- */
-
//---------------------------------------------------------
// MusicXmlSlash
//---------------------------------------------------------
@@ -80,6 +70,33 @@ struct MusicXmlTupletDesc {
TupletNumberType shownumber;
};
+//---------------------------------------------------------
+// SlurDesc
+//---------------------------------------------------------
+
+/**
+ The description of Slurs being handled
+ */
+
+class SlurDesc
+{
+public:
+ enum class State : char {
+ NONE, START, STOP
+ };
+ SlurDesc()
+ : m_slur(0), m_state(State::NONE) {}
+ Slur* slur() const { return m_slur; }
+ void start(Slur* slur) { m_slur = slur; m_state = State::START; }
+ void stop(Slur* slur) { m_slur = slur; m_state = State::STOP; }
+ bool isStart() const { return m_state == State::START; }
+ bool isStop() const { return m_state == State::STOP; }
+private:
+ Slur* m_slur = nullptr;
+ State m_state;
+};
+typedef std::map > MusicXmlSpannerMap;
+
//---------------------------------------------------------
// MusicXmlSpannerDesc
//---------------------------------------------------------
@@ -109,6 +126,30 @@ struct MusicXmlExtendedSpannerDesc {
String toString() const;
};
+//---------------------------------------------------------
+// HarmonyDesc
+//---------------------------------------------------------
+
+/**
+ The description of a chord symbol with or without a fret diagram
+ */
+
+struct HarmonyDesc
+{
+ track_idx_t m_track;
+ bool fretDiagramVisible() const { return m_fretDiagram ? m_fretDiagram->visible() : false; }
+ Harmony* m_harmony;
+ FretDiagram* m_fretDiagram;
+
+ HarmonyDesc(track_idx_t m_track, Harmony* m_harmony, FretDiagram* m_fretDiagram)
+ : m_track(m_track), m_harmony(m_harmony),
+ m_fretDiagram(m_fretDiagram) {}
+
+ HarmonyDesc()
+ : m_track(0), m_harmony(nullptr), m_fretDiagram(nullptr) {}
+};
+using HarmonyMap = std::multimap;
+
//---------------------------------------------------------
// MusicXmlLyricsExtend
//---------------------------------------------------------
@@ -134,6 +175,22 @@ struct GraceNoteLyrics {
: lyric(lyric), extend(extend), no(no) {}
};
+struct InferredPercInstr {
+ int pitch;
+ track_idx_t track;
+ String name;
+ Fraction tick;
+
+ InferredPercInstr(int pitch, track_idx_t track, String name, Fraction tick)
+ : pitch(pitch), track(track), name(name), tick(tick) {}
+
+ InferredPercInstr()
+ : pitch(-1), track(muse::nidx), name(u""), tick(Fraction(0, -1)) {}
+};
+typedef std::vector InferredPercList;
+
+typedef std::map > MetronomeTextMap;
+
//---------------------------------------------------------
// MusicXMLParserLyric
//---------------------------------------------------------
@@ -242,6 +299,9 @@ using SpannerSet = std::set;
using DelayedArpMap = std::map;
using SegnoStack = std::map;
using SystemElements = std::multimap;
+
+// Ties are identified by the pitch and track of their first note
+typedef std::pair TieLocation;
using MusicXMLTieMap = std::map;
//---------------------------------------------------------
@@ -613,4 +673,3 @@ class MusicXMLInferredFingering
void addToNotes(std::vector& notes) const;
};
} // namespace Ms
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/importxml.cpp b/src/importexport/musicxml/internal/musicxml/importxml.cpp
index a6a6009e26b7f..a5b1bc9153160 100644
--- a/src/importexport/musicxml/internal/musicxml/importxml.cpp
+++ b/src/importexport/musicxml/internal/musicxml/importxml.cpp
@@ -183,72 +183,4 @@ Err importCompressedMusicXml(MasterScore* score, const String& name, bool forceM
// and import it
return doValidateAndImport(score, name, data, forceMode);
}
-
-//---------------------------------------------------------
-// VoiceDesc
-//---------------------------------------------------------
-
-// TODO: move somewhere else
-
-VoiceDesc::VoiceDesc()
- : m_staff(-1), m_voice(-1), m_overlaps(false)
-{
- for (int i = 0; i < MAX_STAVES; ++i) {
- m_chordRests[i] = 0;
- m_staffAlloc[i] = -1;
- m_voices[i] = -1;
- }
-}
-
-void VoiceDesc::incrChordRests(int s)
-{
- if (0 <= s && s < MAX_STAVES) {
- m_chordRests[s]++;
- }
-}
-
-int VoiceDesc::numberChordRests() const
-{
- int res = 0;
- for (int i = 0; i < MAX_STAVES; ++i) {
- res += m_chordRests[i];
- }
- return res;
-}
-
-int VoiceDesc::preferredStaff() const
-{
- int max = 0;
- int res = 0;
- for (int i = 0; i < MAX_STAVES; ++i) {
- if (m_chordRests[i] > max) {
- max = m_chordRests[i];
- res = i;
- }
- }
- return res;
-}
-
-String VoiceDesc::toString() const
-{
- String res = u"[";
- for (int i = 0; i < MAX_STAVES; ++i) {
- res += String(u" %1").arg(m_chordRests[i]);
- }
- res += String(u" ] overlaps %1").arg(m_overlaps);
- if (m_overlaps) {
- res += u" staffAlloc [";
- for (int i = 0; i < MAX_STAVES; ++i) {
- res += String(u" %1").arg(m_staffAlloc[i]);
- }
- res += u" ] voices [";
- for (int i = 0; i < MAX_STAVES; ++i) {
- res += String(u" %1").arg(m_voices[i]);
- }
- res += u" ]";
- } else {
- res += String(u" staff %1 voice %2").arg(m_staff + 1).arg(m_voice + 1);
- }
- return res;
-}
} // namespace Ms
diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.cmake b/src/importexport/musicxml/internal/musicxml/musicxml.cmake
index 09e0c1145a2da..ec89cc56654b4 100644
--- a/src/importexport/musicxml/internal/musicxml/musicxml.cmake
+++ b/src/importexport/musicxml/internal/musicxml/musicxml.cmake
@@ -15,13 +15,18 @@ set (MUSICXML_SRC
${CMAKE_CURRENT_LIST_DIR}/importmxmlpass2.cpp
${CMAKE_CURRENT_LIST_DIR}/importmxmlpass2.h
${CMAKE_CURRENT_LIST_DIR}/importxml.cpp
- ${CMAKE_CURRENT_LIST_DIR}/importxmlfirstpass.cpp
- ${CMAKE_CURRENT_LIST_DIR}/importxmlfirstpass.h
- ${CMAKE_CURRENT_LIST_DIR}/musicxml.h
${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.cpp
${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.h
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmlpart.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmlpart.h
${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.cpp
${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.h
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmltupletstate.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmltupletstate.h
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmltypes.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmltypes.h
${CMAKE_CURRENT_LIST_DIR}/musicxmlvalidation.cpp
${CMAKE_CURRENT_LIST_DIR}/musicxmlvalidation.h
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmlvoicedesc.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/musicxmlvoicedesc.h
)
diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.h b/src/importexport/musicxml/internal/musicxml/musicxml.h
deleted file mode 100644
index 58eaa374fd689..0000000000000
--- a/src/importexport/musicxml/internal/musicxml/musicxml.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-3.0-only
- * MuseScore-Studio-CLA-applies
- *
- * MuseScore Studio
- * Music Composition & Notation
- *
- * Copyright (C) 2021 MuseScore Limited
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3 as
- * published by the Free Software Foundation.
- *
- * 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, see .
- */
-
-#ifndef __MUSICXML_H__
-#define __MUSICXML_H__
-
-/**
- \file
- Definition of class MusicXML
-*/
-
-#include "engraving/types/fraction.h"
-#include "engraving/dom/mscore.h"
-#include "engraving/dom/pitchspelling.h"
-#include "engraving/dom/line.h"
-#include "importxmlfirstpass.h"
-#include "musicxmlsupport.h"
-
-namespace mu::engraving {
-//---------------------------------------------------------
-// MusicXmlPartGroup
-//---------------------------------------------------------
-
-struct MusicXmlPartGroup {
- int span = 0;
- int start = 0;
- BracketType type = BracketType::NO_BRACKET;
- bool barlineSpan = false;
- int column = 0;
-};
-
-const int MAX_LYRICS = 16;
-const int MAX_PART_GROUPS = 8;
-const int MAX_NUMBER_LEVEL = 16; // maximum number of overlapping MusicXML objects
-
-//---------------------------------------------------------
-// CreditWords
-// a single parsed MusicXML credit-words element
-//---------------------------------------------------------
-
-struct CreditWords {
- int page = 0;
- String type;
- double defaultX = 0.0;
- double defaultY = 0.0;
- double fontSize = 0.0;
- String justify;
- String hAlign;
- String vAlign;
- String words;
- CreditWords(int p, String tp, double dx, double dy, double fs, String j, String ha, String va, String w)
- {
- page = p;
- type = tp;
- defaultX = dx;
- defaultY = dy;
- fontSize = fs;
- justify = j;
- hAlign = ha;
- vAlign = va;
- words = w;
- }
-};
-
-typedef std::vector CreditWordsList;
-typedef CreditWordsList::iterator iCreditWords;
-typedef CreditWordsList::const_iterator ciCreditWords;
-
-//---------------------------------------------------------
-// JumpMarkerDesc
-//---------------------------------------------------------
-
-/**
- The description of Jumps and Markers to be added later
-*/
-
-class JumpMarkerDesc
-{
-public:
- JumpMarkerDesc(EngravingItem* el, Measure* meas)
- : m_el(el), m_meas(meas) {}
- EngravingItem* el() const { return m_el; }
- Measure* meas() const { return m_meas; }
-
-private:
- EngravingItem* m_el = nullptr;
- Measure* m_meas = nullptr;
-};
-
-typedef std::vector JumpMarkerDescList;
-
-//---------------------------------------------------------
-// SlurDesc
-//---------------------------------------------------------
-
-/**
- The description of Slurs being handled
- */
-
-class SlurDesc
-{
-public:
- enum class State : char {
- NONE, START, STOP
- };
- SlurDesc()
- : m_slur(0), m_state(State::NONE) {}
- Slur* slur() const { return m_slur; }
- void start(Slur* slur) { m_slur = slur; m_state = State::START; }
- void stop(Slur* slur) { m_slur = slur; m_state = State::STOP; }
- bool isStart() const { return m_state == State::START; }
- bool isStop() const { return m_state == State::STOP; }
-private:
- Slur* m_slur = nullptr;
- State m_state;
-};
-
-// Ties are identified by the pitch and track of their first note
-typedef std::pair TieLocation;
-
-//---------------------------------------------------------
-// MusicXml
-//---------------------------------------------------------
-
-typedef std::vector MusicXmlPartGroupList;
-typedef std::map > MusicXmlSpannerMap;
-} // namespace Ms
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h b/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h
index b015d0f9675e0..370ab3aa03c33 100644
--- a/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h
@@ -20,8 +20,7 @@
* along with this program. If not, see .
*/
-#ifndef __MUSICXMLFONTHANDLER_H__
-#define __MUSICXMLFONTHANDLER_H__
+#pragma once
#include "engraving/dom/text.h"
@@ -50,5 +49,3 @@ class MScoreTextToMXML
String musicalTextFont;
};
} // namespace Ms
-
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlpart.cpp
similarity index 63%
rename from src/importexport/musicxml/internal/musicxml/importxmlfirstpass.cpp
rename to src/importexport/musicxml/internal/musicxml/musicxmlpart.cpp
index 5938522275e2d..ba99ab14bdb05 100644
--- a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.cpp
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlpart.cpp
@@ -5,7 +5,7 @@
* MuseScore Studio
* Music Composition & Notation
*
- * Copyright (C) 2021 MuseScore Limited
+ * Copyright (C) 2024 MuseScore Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
@@ -20,12 +20,10 @@
* along with this program. If not, see .
*/
-#include "importxmlfirstpass.h"
+#include "musicxmlpart.h"
+#include "dom/mscore.h"
-#include "log.h"
-
-namespace mu::engraving {
-// TODO: move somewhere else
+using namespace mu::engraving;
static const std::vector vocalInstrumentNames = {
u"Voice",
@@ -59,6 +57,57 @@ static const std::vector percussionInstrumentNames = {
u"Stamp"
};
+int MusicXmlOctaveShiftList::octaveShift(const Fraction f) const
+{
+ if (empty()) {
+ return 0;
+ }
+ auto i = upper_bound(f);
+ if (i == begin()) {
+ return 0;
+ }
+ --i;
+ return i->second;
+}
+
+void MusicXmlOctaveShiftList::addOctaveShift(const int shift, const Fraction f)
+{
+ IF_ASSERT_FAILED(Fraction(0, 1) <= f) {
+ return;
+ }
+
+ //LOGD("addOctaveShift(shift %d f %s)", shift, muPrintable(f.print()));
+ auto i = find(f);
+ if (i == end()) {
+ //LOGD("addOctaveShift: not found, inserting");
+ insert({ f, shift });
+ } else {
+ //LOGD("addOctaveShift: found %d, adding", (*this)[f]);
+ (*this)[f] += shift;
+ //LOGD("addOctaveShift: res %d", (*this)[f]);
+ }
+}
+
+void MusicXmlOctaveShiftList::calcOctaveShiftShifts()
+{
+ /*
+ for (auto i = cbegin(); i != cend(); ++i)
+ LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second);
+ */
+
+ // to each MusicXmlOctaveShiftList entry, add the sum of all previous ones
+ int currentShift = 0;
+ for (auto i = begin(); i != end(); ++i) {
+ currentShift += i->second;
+ i->second = currentShift;
+ }
+
+ /*
+ for (auto i = cbegin(); i != cend(); ++i)
+ LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second);
+ */
+}
+
MusicXmlPart::MusicXmlPart(String id, String name)
: m_id(id), m_name(name)
{
@@ -181,171 +230,3 @@ bool MusicXmlPart::isPercussionStaff() const
}
return false;
}
-
-//---------------------------------------------------------
-// interval
-//---------------------------------------------------------
-
-Interval MusicXmlIntervalList::interval(const Fraction f) const
-{
- if (empty()) {
- return {};
- }
- auto i = upper_bound(f);
- if (i == begin()) {
- return {};
- }
- --i;
- return i->second;
-}
-
-//---------------------------------------------------------
-// instrument
-//---------------------------------------------------------
-
-const String MusicXmlInstrList::instrument(const Fraction f) const
-{
- if (empty()) {
- return String();
- }
- auto i = upper_bound(f);
- if (i == begin()) {
- return String();
- }
- --i;
- return i->second;
-}
-
-//---------------------------------------------------------
-// setInstrument
-//---------------------------------------------------------
-
-void MusicXmlInstrList::setInstrument(const String instr, const Fraction f)
-{
- // TODO determine how to handle multiple instrument changes at the same time
- // current implementation keeps the first one
- if (!insert({ f, instr }).second) {
- LOGD() << "element already exists, instr: " << instr
- << ", tick: " << f.toString() << "(" << f.ticks() << ")";
- }
- //(*this)[f] = instr;
-}
-
-int MusicXmlOctaveShiftList::octaveShift(const Fraction f) const
-{
- if (empty()) {
- return 0;
- }
- auto i = upper_bound(f);
- if (i == begin()) {
- return 0;
- }
- --i;
- return i->second;
-}
-
-void MusicXmlOctaveShiftList::addOctaveShift(const int shift, const Fraction f)
-{
- IF_ASSERT_FAILED(Fraction(0, 1) <= f) {
- return;
- }
-
- //LOGD("addOctaveShift(shift %d f %s)", shift, muPrintable(f.print()));
- auto i = find(f);
- if (i == end()) {
- //LOGD("addOctaveShift: not found, inserting");
- insert({ f, shift });
- } else {
- //LOGD("addOctaveShift: found %d, adding", (*this)[f]);
- (*this)[f] += shift;
- //LOGD("addOctaveShift: res %d", (*this)[f]);
- }
-}
-
-void MusicXmlOctaveShiftList::calcOctaveShiftShifts()
-{
- /*
- for (auto i = cbegin(); i != cend(); ++i)
- LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second);
- */
-
- // to each MusicXmlOctaveShiftList entry, add the sum of all previous ones
- int currentShift = 0;
- for (auto i = begin(); i != end(); ++i) {
- currentShift += i->second;
- i->second = currentShift;
- }
-
- /*
- for (auto i = cbegin(); i != cend(); ++i)
- LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second);
- */
-}
-
-//---------------------------------------------------------
-// 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 String& number)
-{
- if (m_numberToNo.find(number) == m_numberToNo.end()) {
- m_numberToNo[number] = -1; // unassigned
- }
-}
-
-//---------------------------------------------------------
-// toString
-//---------------------------------------------------------
-
-String LyricNumberHandler::toString() const
-{
- String res;
- for (const auto& p : m_numberToNo) {
- if (!res.isEmpty()) {
- res += u" ";
- }
- res += String(u"%1:%2").arg(p.first).arg(p.second);
- }
- return res;
-}
-
-//---------------------------------------------------------
-// getLyricNo
-//---------------------------------------------------------
-
-int LyricNumberHandler::getLyricNo(const String& number) const
-{
- const auto it = m_numberToNo.find(number);
- return it == m_numberToNo.end() ? 0 : it->second;
-}
-
-//---------------------------------------------------------
-// determineLyricNos
-//---------------------------------------------------------
-
-void LyricNumberHandler::determineLyricNos()
-{
- int i = 0;
- for (auto& p : m_numberToNo) {
- p.second = i;
- ++i;
- }
-}
-}
diff --git a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h
similarity index 76%
rename from src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h
rename to src/importexport/musicxml/internal/musicxml/musicxmlpart.h
index 370dd694a2a48..9404973697fe3 100644
--- a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h
@@ -5,7 +5,7 @@
* MuseScore Studio
* Music Composition & Notation
*
- * Copyright (C) 2021 MuseScore Limited
+ * Copyright (C) 2024 MuseScore Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
@@ -19,33 +19,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+#pragma once
-#ifndef __IMPORTXMLFIRSTPASS_H__
-#define __IMPORTXMLFIRSTPASS_H__
-
-#include "engraving/types/fraction.h"
-#include "engraving/dom/interval.h"
-#include "musicxmlsupport.h"
+#include "types/fraction.h"
+#include "dom/interval.h"
+#include "musicxmltypes.h"
+#include "musicxmlvoicedesc.h"
namespace mu::engraving {
-typedef std::map VoiceList;
-//using Intervals = std::map;
-
-class MusicXmlIntervalList : public std::map
-{
-public:
- MusicXmlIntervalList() {}
- Interval interval(const Fraction f) const;
-};
-
-class MusicXmlInstrList : public std::map
-{
-public:
- MusicXmlInstrList() {}
- const String instrument(const Fraction f) const;
- void setInstrument(const String instr, const Fraction f);
-};
-
class MusicXmlOctaveShiftList : public std::map
{
public:
@@ -55,18 +36,6 @@ class MusicXmlOctaveShiftList : public std::map
void calcOctaveShiftShifts();
};
-class LyricNumberHandler
-{
-public:
- LyricNumberHandler() {}
- void addNumber(const String& number);
- String toString() const;
- int getLyricNo(const String& number) const;
- void determineLyricNos();
-private:
- std::map m_numberToNo;
-};
-
class MusicXmlPart
{
public:
@@ -114,7 +83,6 @@ class MusicXmlPart
int m_maxStaff = -1; // maximum staff value found (0 based), -1 = none
bool m_hasLyrics = false;
std::map m_staffNumberToIndex; // Mapping from staff number to index in staff list.
- // Only for when staves are discarded in MusicXMLParserPass1::attributes.
+ // Only for when staves are discarded in MusicXMLParserPass1::attributes.
};
-} // namespace Ms
-#endif
+}
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp
index bdbfe65ab7717..dd83f835a409e 100644
--- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp
@@ -27,198 +27,13 @@
#include "global/serialization/xmlstreamreader.h"
#include "translation.h"
-#include "engraving/dom/accidental.h"
#include "engraving/dom/articulation.h"
#include "engraving/dom/chord.h"
#include "types/symnames.h"
-
+#include "musicxmltypes.h"
#include "musicxmlsupport.h"
-#include "log.h"
-
-using AccidentalType = mu::engraving::AccidentalType;
-using SymId = mu::engraving::SymId;
-
-static const std::map smuflAccidentalTypes {
- { u"accidentalDoubleFlatOneArrowDown", AccidentalType::DOUBLE_FLAT_ONE_ARROW_DOWN },
- { u"accidentalFlatOneArrowDown", AccidentalType::FLAT_ONE_ARROW_DOWN },
- { u"accidentalNaturalOneArrowDown", AccidentalType::NATURAL_ONE_ARROW_DOWN },
- { u"accidentalSharpOneArrowDown", AccidentalType::SHARP_ONE_ARROW_DOWN },
- { u"accidentalDoubleSharpOneArrowDown", AccidentalType::DOUBLE_SHARP_ONE_ARROW_DOWN },
- { u"accidentalDoubleFlatOneArrowUp", AccidentalType::DOUBLE_FLAT_ONE_ARROW_UP },
- { u"accidentalFlatOneArrowUp", AccidentalType::FLAT_ONE_ARROW_UP },
- { u"accidentalNaturalOneArrowUp", AccidentalType::NATURAL_ONE_ARROW_UP },
- { u"accidentalSharpOneArrowUp", AccidentalType::SHARP_ONE_ARROW_UP },
- { u"accidentalDoubleSharpOneArrowUp", AccidentalType::DOUBLE_SHARP_ONE_ARROW_UP },
- { u"accidentalDoubleFlatTwoArrowsDown", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_DOWN },
- { u"accidentalFlatTwoArrowsDown", AccidentalType::FLAT_TWO_ARROWS_DOWN },
- { u"accidentalNaturalTwoArrowsDown", AccidentalType::NATURAL_TWO_ARROWS_DOWN },
- { u"accidentalSharpTwoArrowsDown", AccidentalType::SHARP_TWO_ARROWS_DOWN },
- { u"accidentalDoubleSharpTwoArrowsDown", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_DOWN },
- { u"accidentalDoubleFlatTwoArrowsUp", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_UP },
- { u"accidentalFlatTwoArrowsUp", AccidentalType::FLAT_TWO_ARROWS_UP },
- { u"accidentalNaturalTwoArrowsUp", AccidentalType::NATURAL_TWO_ARROWS_UP },
- { u"accidentalSharpTwoArrowsUp", AccidentalType::SHARP_TWO_ARROWS_UP },
- { u"accidentalDoubleSharpTwoArrowsUp", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_UP },
- { u"accidentalDoubleFlatThreeArrowsDown", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_DOWN },
- { u"accidentalFlatThreeArrowsDown", AccidentalType::FLAT_THREE_ARROWS_DOWN },
- { u"accidentalNaturalThreeArrowsDown", AccidentalType::NATURAL_THREE_ARROWS_DOWN },
- { u"accidentalSharpThreeArrowsDown", AccidentalType::SHARP_THREE_ARROWS_DOWN },
- { u"accidentalDoubleSharpThreeArrowsDown", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_DOWN },
- { u"accidentalDoubleFlatThreeArrowsUp", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_UP },
- { u"accidentalFlatThreeArrowsUp", AccidentalType::FLAT_THREE_ARROWS_UP },
- { u"accidentalNaturalThreeArrowsUp", AccidentalType::NATURAL_THREE_ARROWS_UP },
- { u"accidentalSharpThreeArrowsUp", AccidentalType::SHARP_THREE_ARROWS_UP },
- { u"accidentalDoubleSharpThreeArrowsUp", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_UP },
- { u"accidentalLowerOneSeptimalComma", AccidentalType::LOWER_ONE_SEPTIMAL_COMMA },
- { u"accidentalRaiseOneSeptimalComma", AccidentalType::RAISE_ONE_SEPTIMAL_COMMA },
- { u"accidentalLowerTwoSeptimalCommas", AccidentalType::LOWER_TWO_SEPTIMAL_COMMAS },
- { u"accidentalRaiseTwoSeptimalCommas", AccidentalType::RAISE_TWO_SEPTIMAL_COMMAS },
- { u"accidentalLowerOneUndecimalQuartertone", AccidentalType::LOWER_ONE_UNDECIMAL_QUARTERTONE },
- { u"accidentalRaiseOneUndecimalQuartertone", AccidentalType::RAISE_ONE_UNDECIMAL_QUARTERTONE },
- { u"accidentalLowerOneTridecimalQuartertone", AccidentalType::LOWER_ONE_TRIDECIMAL_QUARTERTONE },
- { u"accidentalRaiseOneTridecimalQuartertone", AccidentalType::RAISE_ONE_TRIDECIMAL_QUARTERTONE },
- { u"accidentalDoubleFlatEqualTempered", AccidentalType::DOUBLE_FLAT_EQUAL_TEMPERED },
- { u"accidentalFlatEqualTempered", AccidentalType::FLAT_EQUAL_TEMPERED },
- { u"accidentalNaturalEqualTempered", AccidentalType::NATURAL_EQUAL_TEMPERED },
- { u"accidentalSharpEqualTempered", AccidentalType::SHARP_EQUAL_TEMPERED },
- { u"accidentalDoubleSharpEqualTempered", AccidentalType::DOUBLE_SHARP_EQUAL_TEMPERED },
- { u"accidentalQuarterFlatEqualTempered", AccidentalType::QUARTER_FLAT_EQUAL_TEMPERED },
- { u"accidentalQuarterSharpEqualTempered", AccidentalType::QUARTER_SHARP_EQUAL_TEMPERED }
-};
-
namespace mu::engraving {
-NoteList::NoteList()
-{
- _staffNoteLists.reserve(MAX_STAVES);
- for (int i = 0; i < MAX_STAVES; ++i) {
- _staffNoteLists.push_back(StartStopList());
- }
-}
-
-void NoteList::addNote(const int startTick, const int endTick, const size_t staff)
-{
- if (staff < _staffNoteLists.size()) {
- _staffNoteLists[staff].push_back(StartStop(startTick, endTick));
- }
-}
-
-void NoteList::dump(const int& voice) const
-{
- // dump contents
- for (int i = 0; i < MAX_STAVES; ++i) {
- printf("voice %d staff %d:", voice, i);
- for (size_t j = 0; j < _staffNoteLists.at(i).size(); ++j) {
- printf(" %d-%d", _staffNoteLists.at(i).at(j).first, _staffNoteLists.at(i).at(j).second);
- }
- printf("\n");
- }
- // show overlap
- printf("overlap voice %d:", voice);
- for (int i = 0; i < MAX_STAVES - 1; ++i) {
- for (int j = i + 1; j < MAX_STAVES; ++j) {
- stavesOverlap(i, j);
- }
- }
- printf("\n");
-}
-
-/**
- Determine if notes n1 and n2 overlap.
- This is NOT the case if
- - n1 starts when or after n2 stops
- - or n2 starts when or after n1 stops
- */
-
-static bool notesOverlap(const StartStop& n1, const StartStop& n2)
-{
- return !(n1.first >= n2.second || n1.second <= n2.first);
-}
-
-/**
- Determine if any note in staff1 and staff2 overlaps.
- */
-
-bool NoteList::stavesOverlap(const int staff1, const int staff2) const
-{
- for (size_t i = 0; i < _staffNoteLists.at(staff1).size(); ++i) {
- for (size_t j = 0; j < _staffNoteLists.at(staff2).size(); ++j) {
- if (notesOverlap(_staffNoteLists.at(staff1).at(i), _staffNoteLists.at(staff2).at(j))) {
- //printf(" %d-%d", staff1, staff2);
- return true;
- }
- }
- }
- return false;
-}
-
-/**
- Determine if any note in any staff overlaps.
- */
-
-bool NoteList::anyStaffOverlaps() const
-{
- for (int i = 0; i < MAX_STAVES - 1; ++i) {
- for (int j = i + 1; j < MAX_STAVES; ++j) {
- if (stavesOverlap(i, j)) {
- return true;
- }
- }
- }
- return false;
-}
-
-VoiceOverlapDetector::VoiceOverlapDetector()
-{
- // LOGD("VoiceOverlapDetector::VoiceOverlapDetector(staves %d)", MAX_STAVES);
-}
-
-void VoiceOverlapDetector::addNote(const int startTick, const int endTick, const int& voice, const int staff)
-{
- // if necessary, create the note list for voice
- if (!muse::contains(_noteLists, voice)) {
- _noteLists.insert({ voice, NoteList() });
- }
- _noteLists[voice].addNote(startTick, endTick, staff);
-}
-
-void VoiceOverlapDetector::dump() const
-{
- // LOGD("VoiceOverlapDetector::dump()");
- for (auto p : _noteLists) {
- p.second.dump(p.first);
- }
-}
-
-void VoiceOverlapDetector::newMeasure()
-{
- // LOGD("VoiceOverlapDetector::newMeasure()");
- _noteLists.clear();
-}
-
-bool VoiceOverlapDetector::stavesOverlap(const int& voice) const
-{
- if (muse::contains(_noteLists, voice)) {
- return _noteLists.at(voice).anyStaffOverlaps();
- } else {
- return false;
- }
-}
-
-String MusicXMLInstrument::toString() const
-{
- return String(u"chan %1 prog %2 vol %3 pan %4 unpitched %5 name '%6' sound '%7' head %8 line %9 stemDir %10")
- .arg(midiChannel)
- .arg(midiProgram)
- .arg(midiVolume)
- .arg(midiPan)
- .arg(unpitched)
- .arg(name, sound)
- .arg(int(notehead))
- .arg(line)
- .arg(int(stemDirection));
-}
-
//---------------------------------------------------------
// errorStringWithLocation
//---------------------------------------------------------
@@ -623,6 +438,54 @@ String accidentalType2MxmlString(const AccidentalType type)
return s;
}
+static const std::map smuflAccidentalTypes {
+ { u"accidentalDoubleFlatOneArrowDown", AccidentalType::DOUBLE_FLAT_ONE_ARROW_DOWN },
+ { u"accidentalFlatOneArrowDown", AccidentalType::FLAT_ONE_ARROW_DOWN },
+ { u"accidentalNaturalOneArrowDown", AccidentalType::NATURAL_ONE_ARROW_DOWN },
+ { u"accidentalSharpOneArrowDown", AccidentalType::SHARP_ONE_ARROW_DOWN },
+ { u"accidentalDoubleSharpOneArrowDown", AccidentalType::DOUBLE_SHARP_ONE_ARROW_DOWN },
+ { u"accidentalDoubleFlatOneArrowUp", AccidentalType::DOUBLE_FLAT_ONE_ARROW_UP },
+ { u"accidentalFlatOneArrowUp", AccidentalType::FLAT_ONE_ARROW_UP },
+ { u"accidentalNaturalOneArrowUp", AccidentalType::NATURAL_ONE_ARROW_UP },
+ { u"accidentalSharpOneArrowUp", AccidentalType::SHARP_ONE_ARROW_UP },
+ { u"accidentalDoubleSharpOneArrowUp", AccidentalType::DOUBLE_SHARP_ONE_ARROW_UP },
+ { u"accidentalDoubleFlatTwoArrowsDown", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_DOWN },
+ { u"accidentalFlatTwoArrowsDown", AccidentalType::FLAT_TWO_ARROWS_DOWN },
+ { u"accidentalNaturalTwoArrowsDown", AccidentalType::NATURAL_TWO_ARROWS_DOWN },
+ { u"accidentalSharpTwoArrowsDown", AccidentalType::SHARP_TWO_ARROWS_DOWN },
+ { u"accidentalDoubleSharpTwoArrowsDown", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_DOWN },
+ { u"accidentalDoubleFlatTwoArrowsUp", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_UP },
+ { u"accidentalFlatTwoArrowsUp", AccidentalType::FLAT_TWO_ARROWS_UP },
+ { u"accidentalNaturalTwoArrowsUp", AccidentalType::NATURAL_TWO_ARROWS_UP },
+ { u"accidentalSharpTwoArrowsUp", AccidentalType::SHARP_TWO_ARROWS_UP },
+ { u"accidentalDoubleSharpTwoArrowsUp", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_UP },
+ { u"accidentalDoubleFlatThreeArrowsDown", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_DOWN },
+ { u"accidentalFlatThreeArrowsDown", AccidentalType::FLAT_THREE_ARROWS_DOWN },
+ { u"accidentalNaturalThreeArrowsDown", AccidentalType::NATURAL_THREE_ARROWS_DOWN },
+ { u"accidentalSharpThreeArrowsDown", AccidentalType::SHARP_THREE_ARROWS_DOWN },
+ { u"accidentalDoubleSharpThreeArrowsDown", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_DOWN },
+ { u"accidentalDoubleFlatThreeArrowsUp", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_UP },
+ { u"accidentalFlatThreeArrowsUp", AccidentalType::FLAT_THREE_ARROWS_UP },
+ { u"accidentalNaturalThreeArrowsUp", AccidentalType::NATURAL_THREE_ARROWS_UP },
+ { u"accidentalSharpThreeArrowsUp", AccidentalType::SHARP_THREE_ARROWS_UP },
+ { u"accidentalDoubleSharpThreeArrowsUp", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_UP },
+ { u"accidentalLowerOneSeptimalComma", AccidentalType::LOWER_ONE_SEPTIMAL_COMMA },
+ { u"accidentalRaiseOneSeptimalComma", AccidentalType::RAISE_ONE_SEPTIMAL_COMMA },
+ { u"accidentalLowerTwoSeptimalCommas", AccidentalType::LOWER_TWO_SEPTIMAL_COMMAS },
+ { u"accidentalRaiseTwoSeptimalCommas", AccidentalType::RAISE_TWO_SEPTIMAL_COMMAS },
+ { u"accidentalLowerOneUndecimalQuartertone", AccidentalType::LOWER_ONE_UNDECIMAL_QUARTERTONE },
+ { u"accidentalRaiseOneUndecimalQuartertone", AccidentalType::RAISE_ONE_UNDECIMAL_QUARTERTONE },
+ { u"accidentalLowerOneTridecimalQuartertone", AccidentalType::LOWER_ONE_TRIDECIMAL_QUARTERTONE },
+ { u"accidentalRaiseOneTridecimalQuartertone", AccidentalType::RAISE_ONE_TRIDECIMAL_QUARTERTONE },
+ { u"accidentalDoubleFlatEqualTempered", AccidentalType::DOUBLE_FLAT_EQUAL_TEMPERED },
+ { u"accidentalFlatEqualTempered", AccidentalType::FLAT_EQUAL_TEMPERED },
+ { u"accidentalNaturalEqualTempered", AccidentalType::NATURAL_EQUAL_TEMPERED },
+ { u"accidentalSharpEqualTempered", AccidentalType::SHARP_EQUAL_TEMPERED },
+ { u"accidentalDoubleSharpEqualTempered", AccidentalType::DOUBLE_SHARP_EQUAL_TEMPERED },
+ { u"accidentalQuarterFlatEqualTempered", AccidentalType::QUARTER_FLAT_EQUAL_TEMPERED },
+ { u"accidentalQuarterSharpEqualTempered", AccidentalType::QUARTER_SHARP_EQUAL_TEMPERED }
+};
+
//---------------------------------------------------------
// accidentalType2SmuflMxmlString
//---------------------------------------------------------
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h
index 0eac04013d6dd..b2a7916f6fce0 100644
--- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h
@@ -20,11 +20,9 @@
* along with this program. If not, see .
*/
-#ifndef __MUSICXMLSUPPORT_H__
-#define __MUSICXMLSUPPORT_H__
+#pragma once
#include "engraving/types/fraction.h"
-#include "engraving/dom/mscore.h"
#include "engraving/dom/note.h"
#include "engraving/dom/fret.h"
@@ -33,217 +31,6 @@ class XmlStreamReader;
}
namespace mu::engraving {
-class Chord;
-
-//---------------------------------------------------------
-// NoteList
-//---------------------------------------------------------
-
-/**
- List of note start/stop times in a voice in a single staff.
-*/
-
-typedef std::pair StartStop;
-typedef std::vector StartStopList;
-
-//---------------------------------------------------------
-// NoteList
-//---------------------------------------------------------
-
-/**
- List of note start/stop times in a voice in all staves.
-*/
-
-class NoteList
-{
-public:
- NoteList();
- void addNote(const int startTick, const int endTick, const size_t staff);
- void dump(const int& voice) const;
- bool stavesOverlap(const int staff1, const int staff2) const;
- bool anyStaffOverlaps() const;
-private:
- std::vector _staffNoteLists; // The note start/stop times in all staves
-};
-
-struct MusicXmlArpeggioDesc {
- Arpeggio* arp;
- int no;
-
- MusicXmlArpeggioDesc(Arpeggio* arp, int no)
- : arp(arp), no(no) {}
-};
-typedef std::multimap ArpeggioMap;
-
-/**
- The description of a chord symbol with or without a fret diagram
- */
-
-struct HarmonyDesc
-{
- track_idx_t m_track;
- bool fretDiagramVisible() const { return m_fretDiagram ? m_fretDiagram->visible() : false; }
- Harmony* m_harmony;
- FretDiagram* m_fretDiagram;
-
- HarmonyDesc(track_idx_t m_track, Harmony* m_harmony, FretDiagram* m_fretDiagram)
- : m_track(m_track), m_harmony(m_harmony),
- m_fretDiagram(m_fretDiagram) {}
-
- HarmonyDesc()
- : m_track(0), m_harmony(nullptr), m_fretDiagram(nullptr) {}
-};
-
-using HarmonyMap = std::multimap;
-
-//---------------------------------------------------------
-// VoiceDesc
-//---------------------------------------------------------
-
-/**
- The description of a single voice in a MusicXML part.
-*/
-
-class VoiceDesc
-{
-public:
- VoiceDesc();
- void incrChordRests(int s);
- int numberChordRests() const;
- int numberChordRests(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_chordRests[s] : 0; }
- int preferredStaff() const; // Determine preferred staff for this voice
- void setStaff(int s)
- {
- if (s >= 0) {
- m_staff = s;
- }
- }
-
- int staff() const { return m_staff; }
- void setVoice(int v)
- {
- if (v >= 0) {
- m_voice = v;
- }
- }
-
- int voice() const { return m_voice; }
- void setVoice(int s, int v)
- {
- if (s >= 0 && s < MAX_STAVES) {
- m_voices[s] = v;
- }
- }
-
- int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_voices[s] : -1; }
- void setOverlap(bool b) { m_overlaps = b; }
- bool overlaps() const { return m_overlaps; }
- void setStaffAlloc(int s, int i)
- {
- if (s >= 0 && s < MAX_STAVES) {
- m_staffAlloc[s] = i;
- }
- }
-
- int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_staffAlloc[s] : -1; }
- String toString() const;
-private:
- int m_chordRests[MAX_STAVES]; // The number of chordrests on each MusicXML staff
- int m_staff; // The MuseScore staff allocated
- int m_voice; // The MuseScore voice allocated
- bool m_overlaps; // This voice contains active notes in multiple staves at the same time
- int m_staffAlloc[MAX_STAVES]; // For overlapping voices: voice is allocated on these staves (note: -2=unalloc -1=undef 1=alloc)
- int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number
-};
-
-//---------------------------------------------------------
-// VoiceOverlapDetector
-//---------------------------------------------------------
-
-/**
- Detect overlap in a voice, which is when a voice has two or more notes
- active at the same time. In theory this should not happen, as voices
- only move forward in time, but Sibelius 7 reuses voice numbers in multi-
- staff parts, which leads to overlap.
-
- Current implementation does not detect voice overlap within a staff,
- but only between staves.
-*/
-
-class VoiceOverlapDetector
-{
-public:
- VoiceOverlapDetector();
- void addNote(const int startTick, const int endTick, const int& voice, const int staff);
- void dump() const;
- void newMeasure();
- bool stavesOverlap(const int& voice) const;
-private:
- std::map _noteLists; // The notelists for all the voices
-};
-
-//---------------------------------------------------------
-// MusicXMLInstrument
-//---------------------------------------------------------
-
-/**
- A single instrument in a MusicXML part.
- Used for both a drum part and a (non-drum) multi-instrument part
- */
-
-struct MusicXMLInstrument {
- int unpitched; // midi-unpitched read from MusicXML
- String name; // instrument-name read from MusicXML
- String sound; // instrument-sound read from MusicXML
- String virtLib; // virtual-library read from MusicXML
- String virtName; // virtual-name read from MusicXML
- int midiChannel; // midi-channel read from MusicXML
- int midiPort; // port read from MusicXML
- int midiProgram; // midi-program read from MusicXML
- int midiVolume; // volume read from MusicXML
- int midiPan; // pan value read from MusicXML
- NoteHeadGroup notehead; // notehead symbol set
- int line = 0; // place notehead onto this line
- DirectionV stemDirection;
-
- String toString() const;
-
- MusicXMLInstrument() // required by std::map
- : unpitched(-1), name(), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63),
- notehead(NoteHeadGroup::HEAD_INVALID), line(0), stemDirection(DirectionV::AUTO) {}
- MusicXMLInstrument(String s)
- : unpitched(-1), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63),
- notehead(NoteHeadGroup::HEAD_NORMAL), line(0), stemDirection(DirectionV::AUTO) {}
- /*
- MusicXMLInstrument(int p, String s, NoteHead::Group nh, int l, Direction d)
- : unpitched(p), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63),
- notehead(nh), line(l), stemDirection(d) {}
- */
-};
-
-struct InferredPercInstr {
- int pitch;
- track_idx_t track;
- String name;
- Fraction tick;
-
- InferredPercInstr(int pitch, track_idx_t track, String name, Fraction tick)
- : pitch(pitch), track(track), name(name), tick(tick) {}
-
- InferredPercInstr()
- : pitch(-1), track(muse::nidx), name(u""), tick(Fraction(0, -1)) {}
-};
-
-typedef std::vector InferredPercList;
-
-/**
- A MusicXML drumset or set of instruments in a multi-instrument part.
- */
-
-typedef std::map MusicXMLInstruments;
-
-typedef std::map > MetronomeTextMap;
-
//---------------------------------------------------------
// MxmlSupport -- MusicXML import support functions
//---------------------------------------------------------
@@ -269,4 +56,3 @@ extern const Articulation* findLaissezVibrer(const Chord* chord);
extern String errorStringWithLocation(int line, int col, const String& error);
extern String checkAtEndElement(const muse::XmlStreamReader& e, const String& expName);
} // namespace Ms
-#endif
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp
new file mode 100644
index 0000000000000..54557255949eb
--- /dev/null
+++ b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp
@@ -0,0 +1,364 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-only
+ * MuseScore-Studio-CLA-applies
+ *
+ * MuseScore Studio
+ * Music Composition & Notation
+ *
+ * Copyright (C) 2024 MuseScore Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see .
+ */
+#include "musicxmltupletstate.h"
+#include "internal/musicxml/musicxmlsupport.h"
+
+using namespace mu::engraving;
+
+//---------------------------------------------------------
+// isTupletFilled
+//---------------------------------------------------------
+
+/**
+ Determine if the tuplet is completely filled,
+ because either (1) it is at least the same duration
+ as the specified number of the specified normal type notes
+ or (2) the duration adds up to a normal note duration.
+
+ Example (1): a 3:2 tuplet with a 1/4 and a 1/8 note
+ is filled if normal type is 1/8,
+ it is not filled if normal type is 1/4.
+
+ Example (2): a 3:2 tuplet with a 1/4 and a 1/8 note is filled.
+ */
+
+bool MxmlTupletState::isTupletFilled(const TDuration normalType, const Fraction timeMod)
+{
+ UNUSED(timeMod);
+ bool res = false;
+ /*
+ const auto normalNotes = state.m_normalNotes;
+ LOGD("duration %s normalType %s timeMod %s normalNotes %d actualNotes %d",
+ muPrintable(state.m_duration.toString()),
+ muPrintable(normalType.fraction().toString()),
+ muPrintable(timeMod.toString()),
+ normalNotes,
+ actualNotes
+ );
+ */
+
+ if (normalType.isValid()) {
+ int matchedNormalType = int(normalType.type());
+ int matchedNormalCount = actualNotes;
+ // match the types
+ matchTypeAndCount(matchedNormalType, matchedNormalCount);
+ // ... result scenario (1)
+ res = smallestNoteCount >= matchedNormalCount;
+ /*
+ LOGD("normalType valid tupletType %d tupletCount %d matchedNormalType %d matchedNormalCount %d res %d",
+ tupletType,
+ tupletCount,
+ matchedNormalType,
+ matchedNormalCount,
+ res
+ );
+ */
+ } else {
+ // ... result scenario (2)
+ res = smallestNoteCount >= actualNotes;
+ /*
+ LOGD("normalType not valid tupletCount %d actualNotes %d res %d",
+ tupletCount,
+ actualNotes,
+ res
+ );
+ */
+ }
+ return res;
+}
+
+//---------------------------------------------------------
+// smallestTypeAndCount
+//---------------------------------------------------------
+
+/**
+ Determine the smallest note type and the number of those
+ present in a ChordRest.
+ For a note without dots the type equals the note type
+ and count is one.
+ For a single dotted note the type equals half the note type
+ and count is three.
+ A double dotted note is similar.
+ Note: code assumes when duration().type() is incremented,
+ the note length is divided by two, checked by tupletAssert().
+ */
+
+void MxmlTupletState::smallestTypeAndCount(const TDuration durType, int& type, int& count)
+{
+ type = int(durType.type());
+ count = 1;
+ switch (durType.dots()) {
+ case 0:
+ // nothing to do
+ break;
+ case 1:
+ type += 1; // next-smaller type
+ count = 3;
+ break;
+ case 2:
+ type += 2; // next-next-smaller type
+ count = 7;
+ break;
+ default:
+ LOGD("smallestTypeAndCount() does not support more than 2 dots");
+ }
+}
+
+//---------------------------------------------------------
+// matchTypeAndCount
+//---------------------------------------------------------
+
+/**
+ Given two note types and counts, if the types are not equal,
+ make them equal by successively doubling the count of the
+ largest type.
+ */
+
+void MxmlTupletState::matchTypeAndCount(int& noteType, int& noteCount)
+{
+ while (smallestNoteType < noteType) {
+ smallestNoteType++;
+ smallestNoteCount *= 2;
+ }
+ while (noteType < smallestNoteType) {
+ noteType++;
+ noteCount *= 2;
+ }
+}
+
+//---------------------------------------------------------
+// addDurationToTuplet
+//---------------------------------------------------------
+
+/**
+ Add duration to tuplet duration
+ Determine type and number of smallest notes in the tuplet
+ */
+
+void MxmlTupletState::addDurationToTuplet(const Fraction dur, const Fraction timeMod)
+{
+ /*
+ LOGD("1 duration %s timeMod %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d",
+ muPrintable(duration.print()),
+ muPrintable(timeMod.print()),
+ m_tupletType,
+ m_tupletCount,
+ m_actualNotes,
+ m_normalNotes
+ );
+ */
+ if (duration <= Fraction(0, 1)) {
+ // first note: init variables
+ actualNotes = timeMod.denominator();
+ normalNotes = timeMod.numerator();
+ smallestTypeAndCount(dur / timeMod, smallestNoteType, smallestNoteCount);
+ } else {
+ int noteType = 0;
+ int noteCount = 0;
+ smallestTypeAndCount(dur / timeMod, noteType, noteCount);
+ // match the types
+ matchTypeAndCount(noteType, noteCount);
+ smallestNoteCount += noteCount;
+ }
+ duration += dur;
+ /*
+ LOGD("2 duration %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d",
+ muPrintable(duration.print()),
+ m_tupletType,
+ m_tupletCount,
+ m_actualNotes,
+ m_normalNotes
+ );
+ */
+}
+
+//---------------------------------------------------------
+// determineTupletAction
+//---------------------------------------------------------
+
+/**
+ Update tuplet state using parse result tupletDesc.
+ Tuplets with and but without
+ are handled correctly.
+ TODO Nested tuplets are not (yet) supported.
+ */
+
+MxmlTupletFlags MxmlTupletState::determineTupletAction(const Fraction noteDuration,
+ const Fraction timeMod,
+ const MxmlStartStop tupletStartStop,
+ const TDuration normalType,
+ Fraction& missingPreviousDuration,
+ Fraction& missingCurrentDuration)
+{
+ const int actNotes = timeMod.denominator();
+ const int norNotes = timeMod.numerator();
+ MxmlTupletFlags res = MxmlTupletFlag::NONE;
+
+ // check for unexpected termination of previous tuplet
+ if (inTuplet && timeMod == Fraction(1, 1)) {
+ // recover by simply stopping the current tuplet first
+ if (!isTupletFilled(normalType, timeMod)) {
+ missingPreviousDuration = missingTupletDuration(duration);
+ //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print()));
+ }
+ *this = {};
+ res |= MxmlTupletFlag::STOP_PREVIOUS;
+ }
+
+ // check for obvious errors
+ if (inTuplet && tupletStartStop == MxmlStartStop::START) {
+ LOGD("tuplet already started");
+ // recover by simply stopping the current tuplet first
+ if (!isTupletFilled(normalType, timeMod)) {
+ missingPreviousDuration = missingTupletDuration(duration);
+ //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print()));
+ }
+ *this = {};
+ res |= MxmlTupletFlag::STOP_PREVIOUS;
+ }
+ if (tupletStartStop == MxmlStartStop::STOP && !inTuplet) {
+ LOGD("tuplet stop but no tuplet started"); // TODO
+ // recovery handled later (automatically, no special case needed)
+ }
+
+ // Tuplet are either started by the tuplet start
+ // or when the time modification is first found.
+ if (!inTuplet) {
+ if (tupletStartStop == MxmlStartStop::START
+ || (!inTuplet && (actNotes != 1 || norNotes != 1))) {
+ if (tupletStartStop != MxmlStartStop::START) {
+ implicit = true;
+ } else {
+ implicit = false;
+ }
+ // create a new tuplet
+ inTuplet = true;
+ res |= MxmlTupletFlag::START_NEW;
+ }
+ }
+
+ // Add chord to the current tuplet.
+ // Must also check for actual/normal notes to prevent
+ // adding one chord too much if tuplet stop is missing.
+ if (inTuplet && !(actNotes == 1 && norNotes == 1)) {
+ addDurationToTuplet(noteDuration, timeMod);
+ res |= MxmlTupletFlag::ADD_CHORD;
+ }
+
+ // Tuplets are stopped by the tuplet stop
+ // or when the tuplet is filled completely
+ // (either with knowledge of the normal type
+ // or as a last resort calculated based on
+ // actual and normal notes plus total duration)
+ // or when the time-modification is not found.
+
+ if (inTuplet) {
+ if (tupletStartStop == MxmlStartStop::STOP
+ || (implicit && isTupletFilled(normalType, timeMod))
+ || (actNotes == 1 && norNotes == 1)) { // incorrect ??? check scenario incomplete tuplet w/o start
+ if (actNotes > norNotes && !isTupletFilled(normalType, timeMod)) {
+ missingCurrentDuration = missingTupletDuration(duration);
+ LOGD("current tuplet incomplete, missing %s", muPrintable(missingCurrentDuration.toString()));
+ }
+
+ *this = {};
+ res |= MxmlTupletFlag::STOP_CURRENT;
+ }
+ }
+
+ return res;
+}
+
+//---------------------------------------------------------
+// determineTupletFractionAndFullDuration
+//---------------------------------------------------------
+
+/**
+ Split duration into two factors where fullDuration is note sized
+ (i.e. the denominator is a power of 2), 1/2 < fraction <= 1/1
+ and fraction * fullDuration equals duration.
+ */
+
+void mu::engraving::determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration)
+{
+ fraction = duration;
+ fullDuration = Fraction(1, 1);
+ // move denominator's powers of 2 from fraction to fullDuration
+ while (fraction.denominator() % 2 == 0) {
+ fraction *= 2;
+ fraction.reduce();
+ fullDuration *= Fraction(1, 2);
+ }
+ // move numerator's powers of 2 from fraction to fullDuration
+ while (fraction.numerator() % 2 == 0) {
+ fraction *= Fraction(1, 2);
+ fraction.reduce();
+ fullDuration *= 2;
+ fullDuration.reduce();
+ }
+ // make sure 1/2 < fraction <= 1/1
+ while (fraction <= Fraction(1, 2)) {
+ fullDuration *= Fraction(1, 2);
+ fraction *= 2;
+ }
+ fullDuration.reduce();
+ fraction.reduce();
+
+ /*
+ Examples (note result when denominator is not a power of two):
+ 3:2 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/2
+ 2:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4
+ 4:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4
+ 3:4 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/1
+
+ Bring back fraction in 1/2 .. 1/1 range.
+ */
+
+ if (fraction > Fraction(1, 1) && fraction.denominator() == 1) {
+ fullDuration *= fraction;
+ fullDuration.reduce();
+ fraction = Fraction(1, 1);
+ }
+
+ /*
+ LOGD("duration %s fraction %s fullDuration %s",
+ muPrintable(duration.toString()),
+ muPrintable(fraction.toString()),
+ muPrintable(fullDuration.toString())
+ );
+ */
+}
+
+//---------------------------------------------------------
+// missingTupletDuration
+//---------------------------------------------------------
+
+Fraction mu::engraving::missingTupletDuration(const Fraction duration)
+{
+ Fraction tupletFraction;
+ Fraction tupletFullDuration;
+
+ determineTupletFractionAndFullDuration(duration, tupletFraction, tupletFullDuration);
+ Fraction missing = (Fraction(1, 1) - tupletFraction) * tupletFullDuration;
+
+ return missing;
+}
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h
new file mode 100644
index 0000000000000..9df19463ced95
--- /dev/null
+++ b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h
@@ -0,0 +1,60 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-only
+ * MuseScore-Studio-CLA-applies
+ *
+ * MuseScore Studio
+ * Music Composition & Notation
+ *
+ * Copyright (C) 2024 MuseScore Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see .
+ */
+#pragma once
+
+#include "musicxmltypes.h"
+#include "dom/durationtype.h"
+
+namespace mu::engraving {
+enum class MxmlTupletFlag : char {
+ NONE = 0,
+ STOP_PREVIOUS = 1,
+ START_NEW = 2,
+ ADD_CHORD = 4,
+ STOP_CURRENT = 8
+};
+typedef muse::Flags MxmlTupletFlags;
+
+class MxmlTupletState
+{
+public:
+ MxmlTupletFlags determineTupletAction(const Fraction noteDuration, const Fraction timeMod, const MxmlStartStop tupletStartStop,
+ const TDuration normalType, Fraction& missingPreviousDuration, Fraction& missingCurrentDuration);
+private:
+ void addDurationToTuplet(const Fraction duration, const Fraction timeMod);
+ void smallestTypeAndCount(const TDuration durType, int& type, int& count);
+ void matchTypeAndCount(int& noteType, int& noteCount);
+ bool isTupletFilled(const TDuration normalType, const Fraction timeMod);
+
+ bool inTuplet = false;
+ bool implicit = false;
+ int actualNotes = 1;
+ int normalNotes = 1;
+ Fraction duration { 0, 1 };
+ int smallestNoteType = 0; // smallest note type in the tuplet
+ int smallestNoteCount = 0; // number of smallest notes in the tuplet
+};
+using MxmlTupletStates = std::map;
+
+void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration);
+Fraction missingTupletDuration(const Fraction duration);
+}
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp
new file mode 100644
index 0000000000000..94300d8bc6c20
--- /dev/null
+++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp
@@ -0,0 +1,155 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-only
+ * MuseScore-Studio-CLA-applies
+ *
+ * MuseScore Studio
+ * Music Composition & Notation
+ *
+ * Copyright (C) 2024 MuseScore Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see .
+ */
+
+#include "musicxmltypes.h"
+
+using namespace mu::engraving;
+
+String MusicXMLInstrument::toString() const
+{
+ return String(u"chan %1 prog %2 vol %3 pan %4 unpitched %5 name '%6' sound '%7' head %8 line %9 stemDir %10")
+ .arg(midiChannel)
+ .arg(midiProgram)
+ .arg(midiVolume)
+ .arg(midiPan)
+ .arg(unpitched)
+ .arg(name, sound)
+ .arg(int(notehead))
+ .arg(line)
+ .arg(int(stemDirection));
+}
+
+//---------------------------------------------------------
+// interval
+//---------------------------------------------------------
+
+Interval MusicXmlIntervalList::interval(const Fraction f) const
+{
+ if (empty()) {
+ return {};
+ }
+ auto i = upper_bound(f);
+ if (i == begin()) {
+ return {};
+ }
+ --i;
+ return i->second;
+}
+
+//---------------------------------------------------------
+// instrument
+//---------------------------------------------------------
+
+const String MusicXmlInstrList::instrument(const Fraction f) const
+{
+ if (empty()) {
+ return String();
+ }
+ auto i = upper_bound(f);
+ if (i == begin()) {
+ return String();
+ }
+ --i;
+ return i->second;
+}
+
+//---------------------------------------------------------
+// setInstrument
+//---------------------------------------------------------
+
+void MusicXmlInstrList::setInstrument(const String instr, const Fraction f)
+{
+ // TODO determine how to handle multiple instrument changes at the same time
+ // current implementation keeps the first one
+ if (!insert({ f, instr }).second) {
+ LOGD() << "element already exists, instr: " << instr
+ << ", tick: " << f.toString() << "(" << f.ticks() << ")";
+ }
+ //(*this)[f] = instr;
+}
+
+//---------------------------------------------------------
+// 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 String& number)
+{
+ if (m_numberToNo.find(number) == m_numberToNo.end()) {
+ m_numberToNo[number] = -1; // unassigned
+ }
+}
+
+//---------------------------------------------------------
+// toString
+//---------------------------------------------------------
+
+String LyricNumberHandler::toString() const
+{
+ String res;
+ for (const auto& p : m_numberToNo) {
+ if (!res.isEmpty()) {
+ res += u" ";
+ }
+ res += String(u"%1:%2").arg(p.first).arg(p.second);
+ }
+ return res;
+}
+
+//---------------------------------------------------------
+// getLyricNo
+//---------------------------------------------------------
+
+int LyricNumberHandler::getLyricNo(const String& number) const
+{
+ const auto it = m_numberToNo.find(number);
+ return it == m_numberToNo.end() ? 0 : it->second;
+}
+
+//---------------------------------------------------------
+// determineLyricNos
+//---------------------------------------------------------
+
+void LyricNumberHandler::determineLyricNos()
+{
+ int i = 0;
+ for (auto& p : m_numberToNo) {
+ p.second = i;
+ ++i;
+ }
+}
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h
new file mode 100644
index 0000000000000..a6d480e6f94ce
--- /dev/null
+++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h
@@ -0,0 +1,122 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-only
+ * MuseScore-Studio-CLA-applies
+ *
+ * MuseScore Studio
+ * Music Composition & Notation
+ *
+ * Copyright (C) 2024 MuseScore Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see .
+ */
+
+#pragma once
+#include "dom/arpeggio.h"
+#include "dom/interval.h"
+
+namespace mu::engraving {
+const int MAX_NUMBER_LEVEL = 16; // maximum number of overlapping MusicXML objects
+
+enum class MusicXMLExporterSoftware : char {
+ SIBELIUS,
+ DOLET6,
+ DOLET8,
+ FINALE,
+ NOTEFLIGHT,
+ OTHER
+};
+
+//---------------------------------------------------------
+// MxmlStartStop
+//---------------------------------------------------------
+
+enum class MxmlStartStop : char {
+ NONE, START, STOP
+};
+
+struct MusicXmlArpeggioDesc {
+ Arpeggio* arp;
+ int no;
+
+ MusicXmlArpeggioDesc(Arpeggio* arp, int no)
+ : arp(arp), no(no) {}
+};
+typedef std::multimap ArpeggioMap;
+
+//---------------------------------------------------------
+// MusicXMLInstrument
+//---------------------------------------------------------
+
+/**
+ A single instrument in a MusicXML part.
+ Used for both a drum part and a (non-drum) multi-instrument part
+ */
+
+struct MusicXMLInstrument {
+ int unpitched; // midi-unpitched read from MusicXML
+ String name; // instrument-name read from MusicXML
+ String sound; // instrument-sound read from MusicXML
+ String virtLib; // virtual-library read from MusicXML
+ String virtName; // virtual-name read from MusicXML
+ int midiChannel; // midi-channel read from MusicXML
+ int midiPort; // port read from MusicXML
+ int midiProgram; // midi-program read from MusicXML
+ int midiVolume; // volume read from MusicXML
+ int midiPan; // pan value read from MusicXML
+ NoteHeadGroup notehead; // notehead symbol set
+ int line = 0; // place notehead onto this line
+ DirectionV stemDirection;
+
+ String toString() const;
+
+ MusicXMLInstrument() // required by std::map
+ : unpitched(-1), name(), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63),
+ notehead(NoteHeadGroup::HEAD_INVALID), line(0), stemDirection(DirectionV::AUTO) {}
+ MusicXMLInstrument(String s)
+ : unpitched(-1), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63),
+ notehead(NoteHeadGroup::HEAD_NORMAL), line(0), stemDirection(DirectionV::AUTO) {}
+ /*
+ MusicXMLInstrument(int p, String s, NoteHead::Group nh, int l, Direction d)
+ : unpitched(p), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63),
+ notehead(nh), line(l), stemDirection(d) {}
+ */
+};
+typedef std::map MusicXMLInstruments;
+
+class MusicXmlIntervalList : public std::map
+{
+public:
+ MusicXmlIntervalList() {}
+ Interval interval(const Fraction f) const;
+};
+
+class MusicXmlInstrList : public std::map
+{
+public:
+ MusicXmlInstrList() {}
+ const String instrument(const Fraction f) const;
+ void setInstrument(const String instr, const Fraction f);
+};
+
+class LyricNumberHandler
+{
+public:
+ LyricNumberHandler() {}
+ void addNumber(const String& number);
+ String toString() const;
+ int getLyricNo(const String& number) const;
+ void determineLyricNos();
+private:
+ std::map m_numberToNo;
+};
+}
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h b/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h
index d59f95a86476e..e6e180c6a1795 100644
--- a/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h
@@ -20,8 +20,7 @@
* along with this program. If not, see .
*/
-#ifndef MU_MUSICXML_MUSICXMLVALIDATION_H
-#define MU_MUSICXML_MUSICXMLVALIDATION_H
+#pragma once
#include "global/types/bytearray.h"
#include "global/types/string.h"
@@ -35,5 +34,3 @@ class MusicxmlValidation
static engraving::Err validate(const muse::String& name, const muse::ByteArray& data);
};
}
-
-#endif // MU_MUSICXML_MUSICXMLVALIDATION_H
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.cpp
new file mode 100644
index 0000000000000..ec3e1f0980029
--- /dev/null
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.cpp
@@ -0,0 +1,90 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-only
+ * MuseScore-Studio-CLA-applies
+ *
+ * MuseScore Studio
+ * Music Composition & Notation
+ *
+ * Copyright (C) 2024 MuseScore Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see .
+ */
+
+#include "musicxmlvoicedesc.h"
+
+using namespace mu::engraving;
+//---------------------------------------------------------
+// VoiceDesc
+//---------------------------------------------------------
+
+VoiceDesc::VoiceDesc()
+ : m_staff(-1), m_voice(-1), m_overlaps(false)
+{
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ m_chordRests[i] = 0;
+ m_staffAlloc[i] = -1;
+ m_voices[i] = -1;
+ }
+}
+
+void VoiceDesc::incrChordRests(int s)
+{
+ if (0 <= s && s < MAX_STAVES) {
+ m_chordRests[s]++;
+ }
+}
+
+int VoiceDesc::numberChordRests() const
+{
+ int res = 0;
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ res += m_chordRests[i];
+ }
+ return res;
+}
+
+int VoiceDesc::preferredStaff() const
+{
+ int max = 0;
+ int res = 0;
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ if (m_chordRests[i] > max) {
+ max = m_chordRests[i];
+ res = i;
+ }
+ }
+ return res;
+}
+
+String VoiceDesc::toString() const
+{
+ String res = u"[";
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ res += String(u" %1").arg(m_chordRests[i]);
+ }
+ res += String(u" ] overlaps %1").arg(m_overlaps);
+ if (m_overlaps) {
+ res += u" staffAlloc [";
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ res += String(u" %1").arg(m_staffAlloc[i]);
+ }
+ res += u" ] voices [";
+ for (int i = 0; i < MAX_STAVES; ++i) {
+ res += String(u" %1").arg(m_voices[i]);
+ }
+ res += u" ]";
+ } else {
+ res += String(u" staff %1 voice %2").arg(m_staff + 1).arg(m_voice + 1);
+ }
+ return res;
+}
diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h
new file mode 100644
index 0000000000000..19ef75f3fb7b6
--- /dev/null
+++ b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h
@@ -0,0 +1,87 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-only
+ * MuseScore-Studio-CLA-applies
+ *
+ * MuseScore Studio
+ * Music Composition & Notation
+ *
+ * Copyright (C) 2024 MuseScore Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see .
+ */
+#pragma once
+
+#include "dom/mscore.h"
+
+namespace mu::engraving {
+//---------------------------------------------------------
+// VoiceDesc
+//---------------------------------------------------------
+
+/**
+ The description of a single voice in a MusicXML part.
+*/
+
+class VoiceDesc
+{
+public:
+ VoiceDesc();
+ void incrChordRests(int s);
+ int numberChordRests() const;
+ int numberChordRests(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_chordRests[s] : 0; }
+ int preferredStaff() const; // Determine preferred staff for this voice
+ void setStaff(int s)
+ {
+ if (s >= 0) {
+ m_staff = s;
+ }
+ }
+
+ int staff() const { return m_staff; }
+ void setVoice(int v)
+ {
+ if (v >= 0) {
+ m_voice = v;
+ }
+ }
+
+ int voice() const { return m_voice; }
+ void setVoice(int s, int v)
+ {
+ if (s >= 0 && s < MAX_STAVES) {
+ m_voices[s] = v;
+ }
+ }
+
+ int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_voices[s] : -1; }
+ void setOverlap(bool b) { m_overlaps = b; }
+ bool overlaps() const { return m_overlaps; }
+ void setStaffAlloc(int s, int i)
+ {
+ if (s >= 0 && s < MAX_STAVES) {
+ m_staffAlloc[s] = i;
+ }
+ }
+
+ int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_staffAlloc[s] : -1; }
+ String toString() const;
+private:
+ int m_chordRests[MAX_STAVES]; // The number of chordrests on each MusicXML staff
+ int m_staff; // The MuseScore staff allocated
+ int m_voice; // The MuseScore voice allocated
+ bool m_overlaps; // This voice contains active notes in multiple staves at the same time
+ int m_staffAlloc[MAX_STAVES]; // For overlapping voices: voice is allocated on these staves (note: -2=unalloc -1=undef 1=alloc)
+ int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number
+};
+typedef std::map VoiceList;
+}
diff --git a/src/importexport/musicxml/internal/musicxmlconfiguration.h b/src/importexport/musicxml/internal/musicxmlconfiguration.h
index fbf9c82482a51..c2075c9950056 100644
--- a/src/importexport/musicxml/internal/musicxmlconfiguration.h
+++ b/src/importexport/musicxml/internal/musicxmlconfiguration.h
@@ -19,8 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef MU_IMPORTEXPORT_MUSICXMLCONFIGURATION_H
-#define MU_IMPORTEXPORT_MUSICXMLCONFIGURATION_H
+#pragma once
#include "../imusicxmlconfiguration.h"
@@ -64,5 +63,3 @@ class MusicXmlConfiguration : public IMusicXmlConfiguration
std::optional m_inferTextTypeOverride;
};
}
-
-#endif // MU_IMPORTEXPORT_MUSICXMLCONFIGURATION_H
diff --git a/src/importexport/musicxml/internal/musicxmlreader.h b/src/importexport/musicxml/internal/musicxmlreader.h
index 25bcc04d53c4e..6c4f687532be1 100644
--- a/src/importexport/musicxml/internal/musicxmlreader.h
+++ b/src/importexport/musicxml/internal/musicxmlreader.h
@@ -19,8 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef MU_IMPORTEXPORT_MUSICXMLREADER_H
-#define MU_IMPORTEXPORT_MUSICXMLREADER_H
+#pragma once
#include "project/inotationreader.h"
@@ -31,4 +30,3 @@ class MusicXmlReader : public project::INotationReader
muse::Ret read(mu::engraving::MasterScore* score, const muse::io::path_t& path, const Options& options = Options()) override;
};
}
-#endif // MU_IMPORTEXPORT_MUSICXMLREADER_H
diff --git a/src/importexport/musicxml/internal/musicxmlwriter.h b/src/importexport/musicxml/internal/musicxmlwriter.h
index 23d22b5a7b05b..5a8bfba88722e 100644
--- a/src/importexport/musicxml/internal/musicxmlwriter.h
+++ b/src/importexport/musicxml/internal/musicxmlwriter.h
@@ -19,9 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
-#ifndef MU_IMPORTEXPORT_MUSICXMLWRITER_H
-#define MU_IMPORTEXPORT_MUSICXMLWRITER_H
+#pragma once
#include "project/inotationwriter.h"
@@ -38,5 +36,3 @@ class MusicXmlWriter : public project::INotationWriter
const Options& options = Options()) override;
};
}
-
-#endif // MU_IMPORTEXPORT_MUSICXMLWRITER_H
diff --git a/src/importexport/musicxml/internal/mxlwriter.h b/src/importexport/musicxml/internal/mxlwriter.h
index f456fbe251ced..fa772f38e4140 100644
--- a/src/importexport/musicxml/internal/mxlwriter.h
+++ b/src/importexport/musicxml/internal/mxlwriter.h
@@ -19,9 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
-#ifndef MU_IMPORTEXPORT_MXLWRITER_H
-#define MU_IMPORTEXPORT_MXLWRITER_H
+#pragma once
#include "musicxmlwriter.h"
@@ -32,5 +30,3 @@ class MxlWriter : public MusicXmlWriter
muse::Ret write(notation::INotationPtr notation, muse::io::IODevice& dstDevice, const Options& options = Options()) override;
};
}
-
-#endif // MU_IMPORTEXPORT_MXLWRITER_H
diff --git a/src/importexport/musicxml/musicxmlmodule.h b/src/importexport/musicxml/musicxmlmodule.h
index 40a6d19736d4e..7481c1fd6cbd6 100644
--- a/src/importexport/musicxml/musicxmlmodule.h
+++ b/src/importexport/musicxml/musicxmlmodule.h
@@ -19,8 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef MU_IMPORTEXPORT_MUSICXMLMODULE_H
-#define MU_IMPORTEXPORT_MUSICXMLMODULE_H
+#pragma once
#include
@@ -42,5 +41,3 @@ class MusicXmlModule : public muse::modularity::IModuleSetup
std::shared_ptr m_configuration;
};
}
-
-#endif // MU_IMPORTEXPORT_MUSICXMLMODULE_H