Skip to content

Commit

Permalink
Infer sticking on XML import
Browse files Browse the repository at this point in the history
Backport of musescore#22536
  • Loading branch information
miiizen authored and Jojo-Schmitz committed Aug 18, 2024
1 parent 972dbe2 commit 81ea8a1
Show file tree
Hide file tree
Showing 9 changed files with 1,462 additions and 10 deletions.
2 changes: 1 addition & 1 deletion importexport/musicxml/exportxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5826,7 +5826,7 @@ static bool commonAnnotations(ExportMusicXml* exp, const Element* e, int sstaff)
exp->symbol(toSymbol(e), sstaff);
else if (e->isTempoText())
exp->tempoText(toTempoText(e), sstaff);
else if (e->isStaffText()|| e->isText() || (e->isInstrumentChange() && e->visible()))
else if (e->isStaffText()|| e->isText() || (e->isInstrumentChange() && e->visible()) || e->isSticking())
exp->words(toTextBase(e), sstaff);
else if (e->isDynamic())
exp->dynamic(toDynamic(e), sstaff);
Expand Down
1 change: 1 addition & 0 deletions importexport/musicxml/importmxmlpass1.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class MusicXMLParserPass1 {
const CreditWordsList& credits() const { return _credits; }
bool hasBeamingInfo() const { return _hasBeamingInfo; }
bool isVocalStaff(const QString& id) const { return _parts[id].isVocalStaff(); }
bool isPercussionStaff(const QString& id) const { return _parts[id].isPercussionStaff(); }
static VBox* createAndAddVBoxForCreditWords(Score* const score, const int miny = 0, const int maxy = 75);
int maxDiff() const { return _maxDiff; }
void insertAdjustedDuration(Fraction key, Fraction value) { _adjustedDurations.insert(key, value); }
Expand Down
54 changes: 51 additions & 3 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include "libmscore/staff.h"
#include "libmscore/stafftext.h"
#include "libmscore/stem.h"
#include "libmscore/sticking.h"
#include "libmscore/sym.h"
#include "libmscore/system.h"
#include "libmscore/tempo.h"
Expand Down Expand Up @@ -3239,6 +3240,7 @@ void MusicXMLParserDirection::direction(const QString& partId,
_placement = _e.attributes().value("placement").toString();
int track = _pass1.trackForPart(partId);
bool isVocalStaff = _pass1.isVocalStaff(partId);
bool isPercussionStaff = _pass1.isPercussionStaff(partId);
bool isExpressionText = false;
bool delayOttava = _pass1.exporterString().contains("sibelius");
_systemDirection = _e.attributes().value("system").toString() == "only-top";
Expand Down Expand Up @@ -3326,6 +3328,27 @@ void MusicXMLParserDirection::direction(const QString& partId,

addElemOffset(tt, track, placement(), measure, tick + _offset);
}
else if (isLikelySticking() && isPercussionStaff) {
Sticking* sticking = new Sticking(_score);
sticking->setXmlText(_wordsText);
if (!qFuzzyIsNull(_relativeX) || !qFuzzyIsNull(_relativeY)) {
QPointF offset = sticking->offset();
offset.setX(!qFuzzyIsNull(_relativeX) ? _relativeX : sticking->offset().x());
offset.setY(!qFuzzyIsNull(_relativeY) ? _relativeY : sticking->offset().y());
sticking->setOffset(offset);
sticking->setPropertyFlags(Pid::OFFSET, PropertyFlags::UNSTYLED);
}

if (hasTotalY()) {
// Add element to score later, after collecting all the others and sorting by default-y
// This allows default-y to be at least respected by the order of elements
MusicXMLDelayedDirectionElement* delayedDirection = new MusicXMLDelayedDirectionElement(
totalY(), sticking, track, placement(), measure, tick + _offset, _isBold);
delayedDirections.push_back(delayedDirection);
}
else
addElemOffset(sticking, track, placement(), measure, tick + _offset);
}
else if (!_wordsText.isEmpty() || !_rehearsalText.isEmpty() || !_metroText.isEmpty()) {
TextBase* t = 0;
if (_tpoSound > 0.1 || attemptTempoTextCoercion(tick)) {
Expand Down Expand Up @@ -3584,10 +3607,20 @@ void MusicXMLParserDirection::directionType(QList<MusicXmlSpannerDesc>& starts,
{
while (_e.readNextStartElement()) {
// Prevent multi-word directions from overwriting y-values.
bool hasDefaultXCandidate = false;
bool hasRelativeXCandidate = false;
bool hasDefaultYCandidate = false;
bool hasRelativeYCandidate = false;
qreal defaultXCandidate = _e.attributes().value("default-x").toDouble(&hasDefaultXCandidate) * -0.1;
qreal defaultYCandidate = _e.attributes().value("default-y").toDouble(&hasDefaultYCandidate) * -0.1;
qreal relativeXCandidate =_e.attributes().value("relative-x").toDouble(&hasRelativeXCandidate) * -0.1;
qreal relativeYCandidate =_e.attributes().value("relative-y").toDouble(&hasRelativeYCandidate) * -0.1;
if (hasDefaultXCandidate && !_hasDefaultX)
_defaultY = defaultXCandidate;
if (hasRelativeXCandidate && !_hasRelativeX)
_relativeX = relativeXCandidate;
_hasDefaultX |= hasDefaultXCandidate;
_hasRelativeX |= hasRelativeXCandidate;
if (hasDefaultYCandidate && !_hasDefaultY)
_defaultY = defaultYCandidate;
if (hasRelativeYCandidate && !_hasRelativeY)
Expand Down Expand Up @@ -3866,9 +3899,11 @@ void MusicXMLParserDirection::textToDynamic(QString& text) const
{
if (!preferences.getBool(PREF_IMPORT_MUSICXML_IMPORTINFERTEXTTYPE))
return;
QString simplifiedText = text.simplified();
QString simplifiedText = MScoreTextToMXML::toPlainText(text).simplified();
// We don't want to count a single 'm', 'r', 's' or 'z' as a whole dynamic
static const QRegularExpression singleCharDynamic("^[mrsz]$");
for (auto dyn : dynList) {
if (dyn.tag == simplifiedText) {
if (!simplifiedText.contains(singleCharDynamic) && dyn.tag == simplifiedText) {
text = text.replace(simplifiedText, dyn.text);
}
}
Expand Down Expand Up @@ -4355,7 +4390,7 @@ void MusicXMLParserDirection::handleNmiCmi(Measure* measure, const int track, co

void MusicXMLParserDirection::handleChordSym(const int track, const Fraction tick, HarmonyMap& harmonyMap)
{
if (!preferences.getBool(PREF_IMPORT_MUSICXML_IMPORTINFERTEXTTYPE))
if (!preferences.getBool(PREF_IMPORT_MUSICXML_IMPORTINFERTEXTTYPE) || placement() == "below")
return;

static const QRegularExpression re("^([abcdefg])(([#b♯♭])\3?)?(maj|min|m)?[769]?((add[#b♯♭]?(9|11|))|(sus[24]?))?(\\(.*\\))?$");
Expand Down Expand Up @@ -4388,6 +4423,19 @@ void MusicXMLParserDirection::handleChordSym(const int track, const Fraction tic
_wordsText.clear();
}

bool MusicXMLParserDirection::isLikelySticking()
{
if (!preferences.getBool(PREF_IMPORT_MUSICXML_IMPORTINFERTEXTTYPE))
return false;

QString plainWords = MScoreTextToMXML::toPlainText(_wordsText.simplified());
static const QRegularExpression sticking("^[lrbLRB]$");
return plainWords.contains(sticking)
&& _rehearsalText.isEmpty()
&& _metroText.isEmpty()
&& _tpoSound < 0.1;
}

//---------------------------------------------------------
// bracket
//---------------------------------------------------------
Expand Down
18 changes: 12 additions & 6 deletions importexport/musicxml/importmxmlpass2.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,14 +418,19 @@ class MusicXMLParserDirection {
QString _sndSegno;
QString _sndToCoda;
QString _placement;
bool _hasDefaultY;
qreal _defaultY;
bool _hasRelativeY;
qreal _relativeY;
bool _hasDefaultX = false;
bool _hasDefaultY = false;
qreal _defaultX = 0.0;
qreal _defaultY = 0.0;
bool _hasRelativeX = false;
bool _hasRelativeY = false;
qreal _relativeX = 0.0;
qreal _relativeY = 0.0;
bool hasTotalX() const { return _hasRelativeX || _hasDefaultX; }
bool hasTotalY() const { return _hasRelativeY || _hasDefaultY; }
bool _isBold;
double _tpoMetro; // tempo according to metronome
double _tpoSound; // tempo according to sound
double _tpoMetro = 0.0; // tempo according to metronome
double _tpoSound = 0.0; // tempo according to sound
bool _systemDirection = false;
bool _visible = true;
QList<Element*> _elems;
Expand All @@ -446,6 +451,7 @@ class MusicXMLParserDirection {
void handleNmiCmi(Measure* measure, const int track, const Fraction tick, DelayedDirectionsList& delayedDirections);
void handleChordSym(const int track, const Fraction tick, HarmonyMap& harmonyMap);
bool isLikelyFingering(const QString& fingeringStr) const;
bool isLikelySticking();
bool isLikelyCredit(const Fraction& tick) const;
void textToCrescLine(QString& text);
void addInferredCrescLine(const int track, const Fraction& tick, const bool isVocalStaff);
Expand Down
27 changes: 27 additions & 0 deletions importexport/musicxml/importxmlfirstpass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ static const std::vector<QString> vocalInstrumentNames({"Voice",
"Women",
"Men"});

static const std::vector<QString> percussionInstrumentNames({"Percussion",
"Timpani",
"Glockenspiel",
"Xylophone",
"Vibraphone",
"Marimba",
"Bell",
"Drum"
"Cymbal",
"Triangle",
"Claves",
"Wood Blocks",
"Tambourine",
"Finger Snap",
"Hand Clap",
"Slap",
"Stamp"});

MusicXmlPart::MusicXmlPart(QString id, QString name)
: id(id), name(name)
{
Expand Down Expand Up @@ -72,6 +90,15 @@ QString MusicXmlPart::toString() const
return res;
}

bool MusicXmlPart::isPercussionStaff() const
{
for (const QString& pname : percussionInstrumentNames) {
if (name.contains(pname))
return true;
}
return false;
}

Interval MusicXmlPart::interval(const Fraction f) const
{
return _intervals.interval(f);
Expand Down
1 change: 1 addition & 0 deletions importexport/musicxml/importxmlfirstpass.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class MusicXmlPart {
void setMaxStaff(const int staff);
int maxStaff() const { return _maxStaff; }
bool isVocalStaff() const;
bool isPercussionStaff() const;
void hasLyrics(bool b) { _hasLyrics = b; }
private:
QString id;
Expand Down
Loading

0 comments on commit 81ea8a1

Please sign in to comment.