Skip to content

Commit

Permalink
ENG-25: Bolster placement defaults
Browse files Browse the repository at this point in the history
This commit adds some context-aware placement defaults for text and
dynamics (in the case that neither "placement" nor "default-y" nor
"relative-y" are specified). Namely, both expression text and dynamics
are placed below the staff unless it is a vocal staff.

To this end, there are some simple checks added to discern whether a
part is a vocal part, which is currently a simple check against the
name of the staff and a check for the presence of lyrics. Additionally,
the "expression text" is currently defined by the presence of italics in
the _wordsText, and an absence of _tempoText.
  • Loading branch information
iveshenry18 committed Jul 6, 2021
1 parent b776c1f commit 7a02054
Show file tree
Hide file tree
Showing 9 changed files with 1,172 additions and 5 deletions.
1 change: 1 addition & 0 deletions importexport/musicxml/importmxmlpass1.cpp
Expand Up @@ -3192,6 +3192,7 @@ void MusicXMLParserPass1::note(const QString& partId,
else if (_e.name() == "lyric") {
const auto number = _e.attributes().value("number").toString();
_parts[partId].lyricNumberHandler().addNumber(number);
_parts[partId].hasLyrics(true);
_e.skipCurrentElement();
}
else if (_e.name() == "notations")
Expand Down
1 change: 1 addition & 0 deletions importexport/musicxml/importmxmlpass1.h
Expand Up @@ -166,6 +166,7 @@ class MusicXMLParserPass1 {
int octaveShift(const QString& id, const int staff, const Fraction f) const;
const CreditWordsList& credits() const { return _credits; }
bool hasBeamingInfo() const { return _hasBeamingInfo; }
bool isVocalStaff(const QString& id) const { return _parts[id].isVocalStaff(); }
static VBox* createAndAddVBoxForCreditWords(Score* const score, const int miny = 0, const int maxy = 75);

private:
Expand Down
27 changes: 22 additions & 5 deletions importexport/musicxml/importmxmlpass2.cpp
Expand Up @@ -852,8 +852,11 @@ static void addElemOffset(Element* el, int track, const QString& placement, Meas
el->setPlacement(placement == "above" ? Placement::ABOVE : Placement::BELOW);
}
#endif
el->setPlacement(placement == "above" ? Placement::ABOVE : Placement::BELOW);
el->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::UNSTYLED);
if (placement != "") {
el->setPlacement(placement == "above" ? Placement::ABOVE : Placement::BELOW);
el->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::UNSTYLED);
}


el->setTrack(el->isTempoText() ? 0 : track); // TempoText must be in track 0
Segment* s = measure->getSegment(SegmentType::ChordRest, tick);
Expand Down Expand Up @@ -2476,6 +2479,8 @@ void MusicXMLParserDirection::direction(const QString& partId,

_placement = _e.attributes().value("placement").toString();
int track = _pass1.trackForPart(partId);
bool isVocalStaff = _pass1.isVocalStaff(partId);
bool isExpressionText = false;
//qDebug("direction track %d", track);
QList<MusicXmlSpannerDesc> starts;
QList<MusicXmlSpannerDesc> stops;
Expand Down Expand Up @@ -2556,6 +2561,7 @@ void MusicXMLParserDirection::direction(const QString& partId,
if (_wordsText != "" || _metroText != "") {
t = new StaffText(_score);
t->setXmlText(_wordsText + _metroText);
isExpressionText = _wordsText.contains("<i>") && _metroText.isEmpty();
}
else {
t = new RehearsalMark(_score);
Expand All @@ -2580,16 +2586,22 @@ void MusicXMLParserDirection::direction(const QString& partId,
t->setFrameType(FrameType::SQUARE);
t->setFrameRound(0);
}

QString wordsPlacement = placement();
// Case-based defaults
if (wordsPlacement.isEmpty())
if (isVocalStaff) wordsPlacement = "above";
else if (isExpressionText) wordsPlacement = "below";

if (isLikelyFingering()) {
_logger->logDebugInfo(QString("Inferring fingering: %1").arg(_wordsText));
MusicXMLInferredFingering* inferredFingering = new MusicXMLInferredFingering(totalY(), t, _wordsText, track, placement(), measure, tick + _offset);
MusicXMLInferredFingering* inferredFingering = new MusicXMLInferredFingering(totalY(), t, _wordsText, track, wordsPlacement, measure, tick + _offset);
inferredFingerings.push_back(inferredFingering);
}
else {
// 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(hasTotalY() ? totalY() : 100, t, track, placement(), measure, tick + _offset);
MusicXMLDelayedDirectionElement* delayedDirection = new MusicXMLDelayedDirectionElement(hasTotalY() ? totalY() : 100, t, track, wordsPlacement, measure, tick + _offset);
delayedDirections.push_back(delayedDirection);
}
}
Expand Down Expand Up @@ -2629,9 +2641,14 @@ void MusicXMLParserDirection::direction(const QString& partId,
dyn->setVelocity( dynaValue );
}

QString dynamicsPlacement = placement();
// Case-based defaults
if (dynamicsPlacement.isEmpty())
dynamicsPlacement = isVocalStaff ? "above" : "below";

// 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(hasTotalY() ? totalY() : 100, dyn, track, placement(), measure, tick + _offset);
MusicXMLDelayedDirectionElement* delayedDirection = new MusicXMLDelayedDirectionElement(hasTotalY() ? totalY() : 100, dyn, track, dynamicsPlacement, measure, tick + _offset);
delayedDirections.push_back(delayedDirection);
}

Expand Down
16 changes: 16 additions & 0 deletions importexport/musicxml/importxmlfirstpass.cpp
Expand Up @@ -16,6 +16,16 @@ namespace Ms {

// TODO: move somewhere else

static const std::vector<QString> vocalInstrumentNames({"Voice",
"Soprano",
"Mezzo-Soprano",
"Alto",
"Tenor",
"Baritone",
"Bass",
"Women",
"Men"});

MusicXmlPart::MusicXmlPart(QString id, QString name)
: id(id), name(name)
{
Expand Down Expand Up @@ -92,6 +102,12 @@ void MusicXmlPart::calcOctaveShifts()
}
}

bool MusicXmlPart::isVocalStaff() const
{
return (std::find(vocalInstrumentNames.begin(), vocalInstrumentNames.end(), name) != vocalInstrumentNames.end()
|| _hasLyrics);
}

//---------------------------------------------------------
// interval
//---------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions importexport/musicxml/importxmlfirstpass.h
Expand Up @@ -81,6 +81,8 @@ class MusicXmlPart {
const LyricNumberHandler& lyricNumberHandler() const { return _lyricNumberHandler; }
void setMaxStaff(const int staff);
int maxStaff() const { return _maxStaff; }
bool isVocalStaff() const;
void hasLyrics(bool b) { _hasLyrics = b; }
private:
QString id;
QString name;
Expand All @@ -92,6 +94,7 @@ class MusicXmlPart {
QVector<MusicXmlOctaveShiftList> octaveShifts; // octave shift list for every staff
LyricNumberHandler _lyricNumberHandler;
int _maxStaff = 0; // maximum staff value found (1 based), 0 = none
bool _hasLyrics = false;
};

} // namespace Ms
Expand Down
Binary file added mtest/musicxml/io/testPlacementDefaults.pdf
Binary file not shown.

0 comments on commit 7a02054

Please sign in to comment.