Skip to content

Commit 038a148

Browse files
committed
Add further header text inference cases
This commit adds credit inferring to the title text (similar to what already exists for subtitles). It also adds and refines the inference cases for both credits and subtitles.
1 parent 3b21717 commit 038a148

File tree

6 files changed

+69
-43
lines changed

6 files changed

+69
-43
lines changed

importexport/musicxml/importmxmlpass1.cpp

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ static void copyOverlapData(VoiceOverlapDetector& vod, VoiceList& vcLst)
183183
//---------------------------------------------------------
184184

185185
MusicXMLParserPass1::MusicXMLParserPass1(Score* score, MxmlLogger* logger)
186-
: _divs(0), _score(score), _logger(logger), _hasBeamingInfo(false)
186+
: _divs(0), _score(score), _logger(logger), _hasBeamingInfo(false), _hasInferredHeaderText(false)
187187
{
188188
// nothing
189189
}
@@ -675,27 +675,52 @@ bool isLikelySubtitleText(const QString& text, const bool caseInsensitive = true
675675
QRegularExpression::PatternOption caseOption = caseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption;
676676
return (text.trimmed().contains(QRegularExpression("^[Ff]rom\\s+(?!$)", caseOption))
677677
|| text.trimmed().contains(QRegularExpression("^Theme from\\s+(?!$)", caseOption))
678-
|| text.trimmed().contains(QRegularExpression("(Op\\.?\\s?\\d+)\\s?(No\\.?\\s?\\d+)?", caseOption))
679-
|| text.trimmed().contains(QRegularExpression("^\\(.*[Ff]rom\\s.*\\)$", caseOption)));
678+
|| text.trimmed().contains(QRegularExpression("(((Op\\.?\\s?\\d+)|(No\\.?\\s?\\d+))\\s?)+", caseOption))
679+
|| text.trimmed().contains(QRegularExpression("\\(.*[Ff]rom\\s.*\\)", caseOption)));
680+
}
681+
682+
//---------------------------------------------------------
683+
// isLikelyCreditText
684+
//---------------------------------------------------------
685+
686+
bool isLikelyCreditText(const QString& text, const bool caseInsensitive = true)
687+
{
688+
QRegularExpression::PatternOption caseOption = caseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption;
689+
return (text.trimmed().contains(QRegularExpression("^((Words|Music|Lyrics|Composed),?(\\sand|\\s&|\\s&)?\\s)*[Bb]y\\s+(?!$)", caseOption))
690+
|| text.trimmed().contains(QRegularExpression("^(Traditional|Trad\\.)", caseOption)));
680691
}
681692

682693
//---------------------------------------------------------
683694
// inferSubTitleFromTitle
684695
//---------------------------------------------------------
685696

686-
// Extracts a likely subtitle from the title string
687-
// Returns the inferred subtitle
697+
// Extracts likely subtitle and credits from the title string
688698

689-
static QString inferSubTitleFromTitle(const QString& title)
699+
static void inferFromTitle(QString& title, QString& inferredSubtitle, QString& inferredCredits)
690700
{
691-
QString inferredSubTitle = "";
692-
for (auto line : title.split(QRegularExpression("\\n"))) {
701+
QStringList subtitleLines;
702+
QStringList creditLines;
703+
QStringList titleLines = title.split(QRegularExpression("\\n"));
704+
for (int i = titleLines.length() - 1; i > 0; --i) {
705+
QString line = titleLines[i];
706+
if (isLikelyCreditText(line, true)) {
707+
for (int j = titleLines.length() - 1; j >= i; --j) {
708+
creditLines.push_front(titleLines[j]);
709+
titleLines.removeAt(j);
710+
}
711+
continue;
712+
}
693713
if (isLikelySubtitleText(line, true)) {
694-
inferredSubTitle = line;
695-
break;
714+
for (int j = titleLines.length() - 1; j >= i; --j) {
715+
subtitleLines.push_front(titleLines[j]);
716+
titleLines.removeAt(j);
717+
}
718+
continue;
696719
}
697720
}
698-
return inferredSubTitle;
721+
title = titleLines.join("\n");
722+
inferredSubtitle = subtitleLines.join("\n");
723+
inferredCredits = creditLines.join("\n");
699724
}
700725
//---------------------------------------------------------
701726
// addCreditWords
@@ -759,11 +784,12 @@ static VBox* addCreditWords(Score* const score, const CreditWordsList& crWords,
759784
// createMeasuresAndVboxes
760785
//---------------------------------------------------------
761786

762-
static void createDefaultHeader(Score* const score)
787+
void MusicXMLParserPass1::createDefaultHeader(Score* const score)
763788
{
764789
QString strTitle;
765790
QString strSubTitle;
766791
QString inferredStrSubTitle;
792+
QString inferredStrComposer;
767793
QString strComposer;
768794
QString strPoet;
769795
QString strTranslator;
@@ -772,21 +798,25 @@ static void createDefaultHeader(Score* const score)
772798
strTitle = score->metaTag("movementTitle");
773799
if (strTitle.isEmpty())
774800
strTitle = score->metaTag("workTitle");
775-
inferredStrSubTitle = inferSubTitleFromTitle(strTitle);
801+
inferFromTitle(strTitle, inferredStrSubTitle, inferredStrComposer);
776802
}
777803
if (!(score->metaTag("movementNumber").isEmpty() && score->metaTag("workNumber").isEmpty())) {
778804
strSubTitle = score->metaTag("movementNumber");
779805
if (strSubTitle.isEmpty())
780806
strSubTitle = score->metaTag("workNumber");
781807
}
782-
else if (!inferredStrSubTitle.isEmpty()) {
808+
if (!inferredStrSubTitle.isEmpty()) {
783809
strSubTitle = inferredStrSubTitle;
784-
strTitle.replace(inferredStrSubTitle, "");
810+
_hasInferredHeaderText = true;
785811
}
786812
QString metaComposer = score->metaTag("composer");
787813
QString metaPoet = score->metaTag("poet");
788814
QString metaTranslator = score->metaTag("translator");
789815
if (!metaComposer.isEmpty()) strComposer = metaComposer;
816+
if (!inferredStrComposer.isEmpty()) {
817+
strComposer = inferredStrComposer;
818+
_hasInferredHeaderText = true;
819+
}
790820
if (metaPoet.isEmpty()) metaPoet = score->metaTag("lyricist");
791821
if (!metaPoet.isEmpty()) strPoet = metaPoet;
792822
if (!metaTranslator.isEmpty()) strTranslator = metaTranslator;
@@ -807,7 +837,7 @@ static void createDefaultHeader(Score* const score)
807837
Create required measures with correct number, start tick and length for Score \a score.
808838
*/
809839

810-
static void createMeasuresAndVboxes(Score* const score,
840+
void MusicXMLParserPass1::createMeasuresAndVboxes(Score* const score,
811841
const QVector<Fraction>& ml, const QVector<Fraction>& ms,
812842
const std::set<int>& systemStartMeasureNrs,
813843
const std::set<int>& pageStartMeasureNrs,

importexport/musicxml/importmxmlpass1.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ using MxmlTupletStates = std::map<QString, MxmlTupletState>;
104104

105105
void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration);
106106
Fraction missingTupletDuration(const Fraction duration);
107+
bool isLikelyCreditText(const QString& text, const bool caseInsensitive);
107108
bool isLikelySubtitleText(const QString& text, const bool caseInsensitive);
108109

109110

@@ -169,8 +170,17 @@ class MusicXMLParserPass1 {
169170
bool hasBeamingInfo() const { return _hasBeamingInfo; }
170171
bool isVocalStaff(const QString& id) const { return _parts[id].isVocalStaff(); }
171172
static VBox* createAndAddVBoxForCreditWords(Score* const score, const int miny = 0, const int maxy = 75);
173+
void createDefaultHeader(Score* const score);
174+
void createMeasuresAndVboxes(Score* const score,
175+
const QVector<Fraction>& ml, const QVector<Fraction>& ms,
176+
const std::set<int>& systemStartMeasureNrs,
177+
const std::set<int>& pageStartMeasureNrs,
178+
const CreditWordsList& crWords,
179+
const QSize pageSize);
172180
QString supportsTranspose() const { return _supportsTranspose; }
173181
void addInferredTranspose(const QString& partId);
182+
void setHasInferredHeaderText(bool b) { _hasInferredHeaderText = b; }
183+
bool hasInferredHeaderText() const { return _hasInferredHeaderText; }
174184

175185
private:
176186
// functions
@@ -191,6 +201,7 @@ class MusicXMLParserPass1 {
191201
MxmlLogger* _logger; ///< Error logger
192202
bool _hasBeamingInfo; ///< Whether the score supports or contains beaming info
193203
QString _supportsTranspose; ///< Whether the score supports transposition info
204+
bool _hasInferredHeaderText;
194205

195206
// part specific data (TODO: move to part-specific class)
196207
Fraction _timeSigDura; ///< Measure duration according to last timesig read

importexport/musicxml/importmxmlpass2.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,7 @@ static void handleSpannerStop(SLine* cur_sp, int track2, const Fraction& tick, M
13131313
//---------------------------------------------------------
13141314

13151315
MusicXMLParserPass2::MusicXMLParserPass2(Score* score, MusicXMLParserPass1& pass1, MxmlLogger* logger)
1316-
: _divs(0), _score(score), _pass1(pass1), _logger(logger), _hasInferredHeaderText(false)
1316+
: _divs(0), _score(score), _pass1(pass1), _logger(logger)
13171317
{
13181318
// nothing
13191319
}
@@ -1750,7 +1750,7 @@ void MusicXMLParserPass2::scorePartwise()
17501750

17511751
_score->connectArpeggios();
17521752
cleanFretDiagrams(_score->firstMeasure());
1753-
if (_hasInferredHeaderText)
1753+
if (_pass1.hasInferredHeaderText())
17541754
reformatHeaderVBox(_score->measures()->first());
17551755
_score->fixupLaissezVibrer();
17561756
}
@@ -2774,14 +2774,14 @@ void MusicXMLParserDirection::direction(const QString& partId,
27742774
else if (isLikelyCredit(tick)) {
27752775
Text* inferredText = addTextToHeader(Tid::COMPOSER);
27762776
if (inferredText) {
2777-
_pass2.setHasInferredHeaderText(true);
2777+
_pass1.setHasInferredHeaderText(true);
27782778
hideRedundantHeaderText(inferredText, {"lyricist", "composer", "poet"});
27792779
}
27802780
}
27812781
else if (isLikelySubtitle(tick)) {
27822782
Text* inferredText = addTextToHeader(Tid::SUBTITLE);
27832783
if (inferredText) {
2784-
_pass2.setHasInferredHeaderText(true);
2784+
_pass1.setHasInferredHeaderText(true);
27852785
if (_score->metaTag("source").isEmpty())
27862786
_score->setMetaTag("source", inferredText->plainText());
27872787
hideRedundantHeaderText(inferredText, {"source"});
@@ -3285,7 +3285,7 @@ bool MusicXMLParserDirection::isLikelyCredit(const Fraction& tick) const
32853285
&& _rehearsalText == ""
32863286
&& _metroText == ""
32873287
&& _tpoSound < 0.1
3288-
&& _wordsText.contains(QRegularExpression("^\\s*((Words|Music|Lyrics),?(\\sand|\\s&amp;)?\\s)*[Bb]y\\s+(?!$)"));
3288+
&& isLikelyCreditText(_wordsText, false);
32893289
}
32903290

32913291
//---------------------------------------------------------
@@ -7449,7 +7449,7 @@ void MusicXMLParserNotations::tuplet()
74497449

74507450
MusicXMLParserDirection::MusicXMLParserDirection(QXmlStreamReader& e,
74517451
Score* score,
7452-
const MusicXMLParserPass1& pass1,
7452+
MusicXMLParserPass1& pass1,
74537453
MusicXMLParserPass2& pass2,
74547454
MxmlLogger* logger)
74557455
: _e(e), _score(score), _pass1(pass1), _pass2(pass2), _logger(logger),

importexport/musicxml/importmxmlpass2.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ class MusicXMLParserPass2 {
267267
MusicXmlExtendedSpannerDesc& getSpanner(const MusicXmlSpannerDesc& desc);
268268
void clearSpanner(const MusicXmlSpannerDesc& desc);
269269
void deleteHandledSpanner(SLine* const& spanner);
270-
void setHasInferredHeaderText(bool b) { _hasInferredHeaderText = b; }
271270

272271
private:
273272
void initPartState(const QString& partId);
@@ -339,7 +338,6 @@ class MusicXMLParserPass2 {
339338
std::map<int, Tie*> _ties;
340339
Volta* _lastVolta;
341340
bool _hasDrumset; ///< drumset defined TODO: move to pass 1
342-
bool _hasInferredHeaderText;
343341

344342
MusicXmlSpannerMap _spanners;
345343

@@ -360,7 +358,7 @@ class MusicXMLParserPass2 {
360358

361359
class MusicXMLParserDirection {
362360
public:
363-
MusicXMLParserDirection(QXmlStreamReader& e, Score* score, const MusicXMLParserPass1& pass1, MusicXMLParserPass2& pass2, MxmlLogger* logger);
361+
MusicXMLParserDirection(QXmlStreamReader& e, Score* score, MusicXMLParserPass1& pass1, MusicXMLParserPass2& pass2, MxmlLogger* logger);
364362
void direction(const QString& partId, Measure* measure, const Fraction& tick, const int divisions,
365363
MusicXmlSpannerMap& spanners, DelayedDirectionsList& delayedDirections, InferredFingeringsList& inferredFingerings);
366364
qreal totalY() const { return _defaultY + _relativeY; }
@@ -369,7 +367,7 @@ class MusicXMLParserDirection {
369367
private:
370368
QXmlStreamReader& _e;
371369
Score* const _score; // the score
372-
const MusicXMLParserPass1& _pass1; // the pass1 results
370+
MusicXMLParserPass1& _pass1; // the pass1 results
373371
MusicXMLParserPass2& _pass2; // the pass2 results
374372
MxmlLogger* _logger; ///< Error logger
375373

mtest/musicxml/io/testInferredCredits2.xml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<work>
55
<work-title>Inferred Credits 2
66
from MUSESCORE: the musical: a tone poem
7+
Words &amp; Music by also Henry Ives (and ampersands)
78
</work-title>
89
</work>
910
<identification>
@@ -84,15 +85,6 @@ from MUSESCORE: the musical: a tone poem
8485
<line>2</line>
8586
</clef>
8687
</attributes>
87-
<direction placement="above">
88-
<direction-type>
89-
<words default-y="34.66" relative-y="10.00">Words &amp; Music by Henry Ives (and ampersands)
90-
</words>
91-
<words>
92-
</words>
93-
<words></words>
94-
</direction-type>
95-
</direction>
9688
<direction placement="above">
9789
<direction-type>
9890
<words default-y="34.66" relative-y="10.00">Lyrics to be sung by candlelight

mtest/musicxml/io/testInferredCredits2_ref.mscx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<metaTag name="workNumber"></metaTag>
5353
<metaTag name="workTitle">Inferred Credits 2
5454
from MUSESCORE: the musical: a tone poem
55+
Words &amp; Music by also Henry Ives (and ampersands)
5556
</metaTag>
5657
<Part>
5758
<Staff id="1">
@@ -119,7 +120,7 @@ from MUSESCORE: the musical: a tone poem
119120
</Part>
120121
<Staff id="1">
121122
<VBox>
122-
<height>19.8846</height>
123+
<height>18.03</height>
123124
<Text>
124125
<style>Title</style>
125126
<text>Inferred Credits 2</text>
@@ -130,19 +131,13 @@ from MUSESCORE: the musical: a tone poem
130131
<text>from MUSESCORE: the musical: a tone poem</text>
131132
</Text>
132133
<Text>
133-
<visible>0</visible>
134134
<style>Composer</style>
135-
<text>Henry Ives</text>
135+
<text>Words &amp; Music by also Henry Ives (and ampersands)</text>
136136
</Text>
137137
<Text>
138-
<visible>0</visible>
139138
<style>Lyricist</style>
140139
<text>Henry Ives</text>
141140
</Text>
142-
<Text>
143-
<style>Composer</style>
144-
<text>Words &amp; Music by Henry Ives (and ampersands)</text>
145-
</Text>
146141
<Text>
147142
<style>Subtitle</style>
148143
<offset x="0" y="17.1591"/>

0 commit comments

Comments
 (0)