From 355b917e59fa8c8642c5b223c14113654128b2a9 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Tue, 20 Aug 2024 12:57:55 +0200 Subject: [PATCH] Fix badly exported metronome marks from Sibelius on XML import Backport of #21587 --- importexport/musicxml/importmxmlpass2.cpp | 55 ++- importexport/musicxml/importmxmlpass2.h | 1 + libmscore/tempotext.cpp | 19 +- mtest/musicxml/io/testSibMetronomeMarks.xml | Bin 0 -> 15004 bytes .../io/testSibMetronomeMarks_ref.mscx | 317 ++++++++++++++++++ mtest/musicxml/io/tst_mxml_io.cpp | 1 + 6 files changed, 382 insertions(+), 11 deletions(-) create mode 100644 mtest/musicxml/io/testSibMetronomeMarks.xml create mode 100644 mtest/musicxml/io/testSibMetronomeMarks_ref.mscx diff --git a/importexport/musicxml/importmxmlpass2.cpp b/importexport/musicxml/importmxmlpass2.cpp index d9742f9d26ac8..f36bff921aa1c 100644 --- a/importexport/musicxml/importmxmlpass2.cpp +++ b/importexport/musicxml/importmxmlpass2.cpp @@ -31,6 +31,7 @@ #include "libmscore/chordlist.h" #include "libmscore/chordrest.h" #include "libmscore/drumset.h" +#include "libmscore/durationtype.h" #include "libmscore/dynamic.h" #include "libmscore/fermata.h" #include "libmscore/figuredbass.h" @@ -3275,7 +3276,7 @@ void MusicXMLParserDirection::direction(const QString& partId, else skipLogCurrElem(); } - + handleTempo(); handleRepeats(measure, track, tick + _offset); handleNmiCmi(measure, track, tick + _offset, delayedDirections); handleChordSym(track, tick + _offset, harmonyMap); @@ -4388,6 +4389,58 @@ void MusicXMLParserDirection::handleChordSym(const int track, const Fraction tic _wordsText.clear(); } +void MusicXMLParserDirection::handleTempo() + { + // Pick up any tempo markings which may have been exported from Sibelius as + // eg. andante (q = c. 90) + // Sibelius uses a symbol font with the characters 'yxeqhVwW' each drawn as a different duration + // which we need to map to SMuFL syms + QString plainWords = MScoreTextToMXML::toPlainText(_wordsText.simplified()); + + static const QRegExp tempo(".*([yxeqhVwW])(\\.?)\\s*=[^0-9]*([0-9]+).*"); +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + QStringList tempoMatches = plainWords.split(tempo, Qt::SkipEmptyParts); +#else + QStringList tempoMatches = plainWords.split(tempo, QString::SkipEmptyParts); +#endif + + // Not a tempo + if (tempoMatches.size() < 2) + return; + + const QString dur = tempoMatches.at(0); + const bool dot = tempoMatches.size() == 3; + const QString val = tempoMatches.at(dot ? 2 : 1); + + const QString dotStr = dot ? "spacemetAugmentationDot" : ""; + // Map Sibelius' representation of note types to their SMuFL counterparts and duration types + static const QMap > syms = { + { "y", { "metNote32ndUp", TDuration::DurationType::V_32ND } }, + { "x", { "metNote16thUp", TDuration::DurationType::V_16TH } }, + { "e", { "metNote8thUp", TDuration::DurationType::V_EIGHTH } }, + { "q", { "metNoteQuarterUp", TDuration::DurationType::V_QUARTER } }, + { "h", { "metNoteHalfUp", TDuration::DurationType::V_HALF } }, + { "w", { "metNoteWhole", TDuration::DurationType::V_WHOLE } }, + { "V", { "metNoteDoubleWholeSquare", TDuration::DurationType::V_BREVE } }, + { "W", { "metNoteDoubleWhole", TDuration::DurationType::V_BREVE } } + }; + + static const QRegExp replace("(.*)[yxeqhVwW]\\.?(\\s*=[^0-9]*[0-9]+.*)"); + const QString newStr = "$1" + syms[dur].first + dotStr + "$2"; + plainWords.replace(replace, newStr); + _wordsText = plainWords; + + if (!val.isEmpty() && !dur.isEmpty()) { + bool ok; + double d = val.toDouble(&ok); + TDuration duration = TDuration(syms[dur].second); + duration.setDots(dot); + + if (ok && duration.isValid()) // convert fraction to beats per minute + _tpoMetro = 4 * duration.fraction().numerator() * d / duration.fraction().denominator(); + } + } + //--------------------------------------------------------- // bracket //--------------------------------------------------------- diff --git a/importexport/musicxml/importmxmlpass2.h b/importexport/musicxml/importmxmlpass2.h index d3550a91175bf..b5031d28ae3ea 100644 --- a/importexport/musicxml/importmxmlpass2.h +++ b/importexport/musicxml/importmxmlpass2.h @@ -444,6 +444,7 @@ class MusicXMLParserDirection { void handleRepeats(Measure* measure, const int track, const Fraction tick); QString matchRepeat() const; void handleNmiCmi(Measure* measure, const int track, const Fraction tick, DelayedDirectionsList& delayedDirections); + void handleTempo(); void handleChordSym(const int track, const Fraction tick, HarmonyMap& harmonyMap); bool isLikelyFingering(const QString& fingeringStr) const; bool isLikelyCredit(const Fraction& tick) const; diff --git a/libmscore/tempotext.cpp b/libmscore/tempotext.cpp index efe0e23c36037..74bf20951fa8a 100644 --- a/libmscore/tempotext.cpp +++ b/libmscore/tempotext.cpp @@ -155,20 +155,20 @@ int TempoText::findTempoDuration(const QString& s, int& len, TDuration& dur) } static const TempoPattern tpSym[] = { - TempoPattern("metNoteQuarterUp\\s*metAugmentationDot\\s*metAugmentationDot", 1.75/60.0, TDuration::DurationType::V_QUARTER, 2), // double dotted 1/4 - TempoPattern("metNoteQuarterUp\\s*metAugmentationDot", 1.5/60.0, TDuration::DurationType::V_QUARTER, 1), // dotted 1/4 + TempoPattern("metNoteQuarterUpspacemetAugmentationDotspacemetAugmentationDot", 1.75/60.0, TDuration::DurationType::V_QUARTER, 2), // double dotted 1/4 + TempoPattern("metNoteQuarterUpspacemetAugmentationDot", 1.5/60.0, TDuration::DurationType::V_QUARTER, 1), // dotted 1/4 TempoPattern("metNoteQuarterUp", 1.0/60.0, TDuration::DurationType::V_QUARTER), // 1/4 - TempoPattern("metNoteHalfUp\\s*metAugmentationDot\\s*metAugmentationDot", 1.75/30.0, TDuration::DurationType::V_HALF, 2), // double dotted 1/2 - TempoPattern("metNoteHalfUp\\s*metAugmentationDot", 1.5/30.0, TDuration::DurationType::V_HALF, 1), // dotted 1/2 + TempoPattern("metNoteHalfUpspacemetAugmentationDotspacemetAugmentationDot", 1.75/30.0, TDuration::DurationType::V_HALF, 2), // double dotted 1/2 + TempoPattern("metNoteHalfUpspacemetAugmentationDot", 1.5/30.0, TDuration::DurationType::V_HALF, 1), // dotted 1/2 TempoPattern("metNoteHalfUp", 1.0/30.0, TDuration::DurationType::V_HALF), // 1/2 - TempoPattern("metNote8thUp\\s*metAugmentationDot\\s*metAugmentationDot", 1.75/120.0, TDuration::DurationType::V_EIGHTH, 2), // double dotted 1/8 - TempoPattern("metNote8thUp\\s*metAugmentationDot", 1.5/120.0, TDuration::DurationType::V_EIGHTH, 1), // dotted 1/8 + TempoPattern("metNote8thUpspacemetAugmentationDotspacemetAugmentationDot", 1.75/120.0, TDuration::DurationType::V_EIGHTH, 2), // double dotted 1/8 + TempoPattern("metNote8thUpspacemetAugmentationDot", 1.5/120.0, TDuration::DurationType::V_EIGHTH, 1), // dotted 1/8 TempoPattern("metNote8thUp", 1.0/120.0, TDuration::DurationType::V_EIGHTH), // 1/8 - TempoPattern("metNoteWhole\\s*metAugmentationDot", 1.5/15.0, TDuration::DurationType::V_WHOLE, 1), // dotted whole + TempoPattern("metNoteWholespacemetAugmentationDot", 1.5/15.0, TDuration::DurationType::V_WHOLE, 1), // dotted whole TempoPattern("metNoteWhole", 1.0/15.0, TDuration::DurationType::V_WHOLE), // whole - TempoPattern("metNote16thUp\\s*metAugmentationDot", 1.5/240.0, TDuration::DurationType::V_16TH, 1), // dotted 1/16 + TempoPattern("metNote16thUpspacemetAugmentationDot", 1.5/240.0, TDuration::DurationType::V_16TH, 1), // dotted 1/16 TempoPattern("metNote16thUp", 1.0/240.0, TDuration::DurationType::V_16TH), // 1/16 - TempoPattern("metNote32ndUp\\s*metAugmentationDot", 1.5/480.0, TDuration::DurationType::V_32ND, 1), // dotted 1/32 + TempoPattern("metNote32ndUpspacemetAugmentationDot", 1.5/480.0, TDuration::DurationType::V_32ND, 1), // dotted 1/32 TempoPattern("metNote32ndUp", 1.0/480.0, TDuration::DurationType::V_32ND), // 1/32 TempoPattern("metNoteDoubleWholeSquare", 1.0/7.5, TDuration::DurationType::V_BREVE), // longa TempoPattern("metNoteDoubleWhole", 1.0/7.5, TDuration::DurationType::V_BREVE), // double whole @@ -206,7 +206,6 @@ QString TempoText::duration2tempoTextString(const TDuration dur) for (const TempoPattern& pa : tpSym) { if (pa.d == dur) { QString res = pa.pattern; - res.replace("\\s*", " "); return res; } } diff --git a/mtest/musicxml/io/testSibMetronomeMarks.xml b/mtest/musicxml/io/testSibMetronomeMarks.xml new file mode 100644 index 0000000000000000000000000000000000000000..101cfc7fbfc2b7bd93d661862118cba875abc1ad GIT binary patch literal 15004 zcmeI3X-}I+5XbjzD)l?KuSldC3gHNqAxcYHwW3Lsqlc=#0L;~bAwELrhj07;?ReSU z=P>5T;l@J5W_M$;vhbSJLk*4&JnceCy*eKxey zaXVUXYq#U}{q8IG(tYQef?D=4J=b?z+HLFKBTr-3!*%q1M(3=%&)sLP<-Twm?y37$ z*uS}F?y;!%JngPdX}VKkcSY$;C-t<_5X>w0joWlT2&W-RO*f~X=h}m#u0Ic4RyRa- zLr_1tftO^*{i^SqTDk9Kfm%) z-sSX*5;dg>WQTj>by0o+&-ml2=XexO&ig>tJN3Nvz4T~eS5`M&6A4?=^^UX?^pA%V z7kzgwO!M<|efG3Em)OE|b2_maI@pUAu8**m_kum? zVIWQ5F@b_vo2Vp`$dhr0y<@E&`8|9KY007B8Eq|@4vsk+5 zF3k_N%<-aOP-o=SMS#2PR8orLURY#B#y^+D^<;B5=gHBWH zUe!|ZG(7RtpYxT@xm_(9*6q5Io&p7sv=K3kA0u*LE)=a#W@Pb%-{5x*m!HHyKiWsFFXY&tr7(M5*4ZBu^3 z-H$s~_{JJ}i%#?0_vlaMy>_c3$F|*<35EQ&(o#0G8})dUU+NS(S-P+%`aIL;b5Wq$ zb)?03D?l#pPE}pA^@rT~=>x3#r5dTaH#Y>=_R>9*S7Way^hxfJ{s>2O>wyG z{uDPmI@4}POeogf@0v7p^n?C(MZe+h3WC|fw$C+8DngaI=l<~ZvV2y~OUk`gRA82HQeoyH!yiTTf#M zxsL9UA8S@?lcE>iTc}k%_sGX6^TRdu4q?t}nhBhabAHi!m+_k0nu%QT=CU-5oy#GM zx-DYVY1iVW%7a?+!{Q_?y``GI;rWf{+oq^CjainLK)Gr+=hSj!?z!lk=`7O*o6}eZ z$=6akU>qwyQ6bm$ZA;aOIt&kSJ;h+B>TK?PR+Gy-5ryak8Exv4uB6oAC+szaVKdGm zR=Sg7!&@~MEmd5*g_pUbC)@Y=_IodP{5z!$XQ8{N@8_)ecM8c))oH9><{3=*4t)lr zLom;vgTM=kIlPmMWs_klB+Z()fbc)=eH>2B%-QJl_4mUCQftw@o zL0;a}4pT_7uYGF>_qBf4^t+=f-jFAlcSh<(E-eno=TRHge4^W21NoB)qzK(_@f7Hj zC%H|eHKcyZ47IkeG}tpd1-UPInCTfW)S+}Y_YkT-Pf={j0!F$LVc9A3&1%l8$@Fp5 zYqXPdSFXkj-b+$BV>Jfao*ATdE_TbYw~v`L_mOcTrsxG_@2U1QJ2U$1xDoN%`+W5N zm0_ljl!_cxd2}^T=5fWmk08HtA$`!@RpR?S`2Mf9iR7zVrsxq&&CzWtxd^DH9V1`TN-oJyk!^lrKI_Cf~*X z4UlKOh|k-myg|Wx59|6jJgaZ&-H`AuI8P4kv>ZC`+H-#5#G&pvG(5z&$_)Nvcs31J Q>06QEy|VB>K8@4=0M7Ai6aWAK literal 0 HcmV?d00001 diff --git a/mtest/musicxml/io/testSibMetronomeMarks_ref.mscx b/mtest/musicxml/io/testSibMetronomeMarks_ref.mscx new file mode 100644 index 0000000000000..4b988bfb653b7 --- /dev/null +++ b/mtest/musicxml/io/testSibMetronomeMarks_ref.mscx @@ -0,0 +1,317 @@ + + + + + 0 + 480 + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + stdNormal + + 3 + + [Unnamed (treble staff)] + + + 21 + 108 + 21 + 108 + keyboard.piano.grand + F + 0 + + 100 + 95 + + + 100 + 33 + + + 100 + 50 + + + 100 + 67 + + + 100 + 100 + + + 120 + 67 + + + 150 + 100 + + + 150 + 50 + + + 120 + 50 + + + 120 + 100 + + + + + + + + + + 10 + + + + + G + G + + + 4 + 4 + + + 0.666667 + 1 + Lingering metNoteQuarterUp = c. 40 + + + measure + 1/1 + + + + + + line + + + + 1.95 + 1 + metNoteQuarterUpspacemetAugmentationDot = c. 78 + + + measure + 1/1 + + + + + + + 1.5 + 1 + Andante (metNoteQuarterUp = c. 90) + + + measure + 1/1 + + + + + + line + + + + 1.333333 + 1 + Leggero (metNoteQuarterUp = c. 80) + + + measure + 1/1 + + + + + + + 0.24375 + 1 + metNote32ndUpspacemetAugmentationDot = c. 78 + + + measure + 1/1 + + + + + + + 0.325 + 1 + metNote16thUp = c. 78 + + + measure + 1/1 + + + + + + + 0.975 + 1 + metNote8thUpspacemetAugmentationDot = c. 78 + + + measure + 1/1 + + + + + + + 2.6 + 1 + metNoteHalfUp = c. 78 + + + measure + 1/1 + + + + + + + 15.6 + 1 + metNoteDoubleWholeSquarespacemetAugmentationDot = c. 78 + + + measure + 1/1 + + + + + + + 5.2 + 1 + metNoteWhole = c. 78 + + + measure + 1/1 + + + + + + + 15.6 + 1 + metNoteDoubleWholespacemetAugmentationDot = c. 78 + + + measure + 1/1 + + + end + + + + + + diff --git a/mtest/musicxml/io/tst_mxml_io.cpp b/mtest/musicxml/io/tst_mxml_io.cpp index 4e38bbfc4f545..495d75acfab8e 100644 --- a/mtest/musicxml/io/tst_mxml_io.cpp +++ b/mtest/musicxml/io/tst_mxml_io.cpp @@ -263,6 +263,7 @@ private slots: void restsNoType() { mxmlIoTestRef("testRestsNoType"); } void restsTypeWhole() { mxmlIoTestRef("testRestsTypeWhole"); } void secondVoiceMelismata() { mxmlImportTestRef("testSecondVoiceMelismata"); } + void sibMetronomeMarks() { mxmlImportTestRef("testSibMetronomeMarks"); } void sibOttavas() { mxmlImportTestRef("testSibOttavas"); } void slurs() { mxmlIoTest("testSlurs"); } void slurs2() { mxmlIoTest("testSlurs2"); }