Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MU3 Backend] ENG-38: Handle hanging ties #8556

Merged
merged 2 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 70 additions & 32 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1471,7 +1471,6 @@ void MusicXMLParserPass2::initPartState(const QString& partId)
{
Q_UNUSED(partId);
_timeSigDura = Fraction(0, 0); // invalid
_tie = 0;
_lastVolta = 0;
_hasDrumset = false;
for (int i = 0; i < MAX_NUMBER_LEVEL; ++i)
Expand Down Expand Up @@ -1522,6 +1521,35 @@ SpannerSet MusicXMLParserPass2::findIncompleteSpannersAtPartEnd()
return res;
}

//---------------------------------------------------------
// addArticLaissezVibrer
//---------------------------------------------------------

static void addArticLaissezVibrer(const Note* const note)
{
Q_ASSERT(note);
auto chord = note->chord();
if (!hasLaissezVibrer(chord)) {
Articulation* na = new Articulation(SymId::articLaissezVibrerBelow, chord->score());
chord->add(na);
}

}

//---------------------------------------------------------
// cleanupUnterminatedTie
//---------------------------------------------------------
/**
Delete tie and add Laissez Vibrer where it was
*/

static void cleanupUnterminatedTie(Tie*& tie)
{
Note* unterminatedTieNote = tie->startNote();
unterminatedTieNote->remove(tie);
delete tie;
addArticLaissezVibrer(unterminatedTieNote); // Treat as let-ring
}

//---------------------------------------------------------
// isLikelyIncorrectPartName
Expand Down Expand Up @@ -1680,6 +1708,7 @@ void MusicXMLParserPass2::scorePartwise()

_score->connectArpeggios();
cleanFretDiagrams(_score->firstMeasure());
_score->fixupLaissezVibrer();
}

//---------------------------------------------------------
Expand Down Expand Up @@ -1833,6 +1862,15 @@ void MusicXMLParserPass2::part()
++i;
}
_spanners.clear();

// Clean up unterminated ties
for (auto tie : _ties) {
if (tie.second) {
cleanupUnterminatedTie(tie.second);
_ties[tie.first] = 0;
}
}
_ties.clear();

if (_hasDrumset) {
Drumset* drumset = new Drumset;
Expand Down Expand Up @@ -5118,7 +5156,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,

// handle notations
if (cr) {
notations.addToScore(cr, note, noteStartTime.ticks(), _slurs, _glissandi, _spanners, _trills, _tie);
notations.addToScore(cr, note, noteStartTime.ticks(), _slurs, _glissandi, _spanners, _trills, _ties);
}

// handle grace after state: remember current grace list size
Expand Down Expand Up @@ -6053,7 +6091,11 @@ void MusicXMLParserNotations::tied()
Q_ASSERT(_e.isStartElement() && _e.name() == "tied");

Notation notation = Notation::notationWithAttributes(_e.name().toString(), _e.attributes(), "notations");
_notations.push_back(notation);
// Make sure "stops" get processed before "starts"
if (notation.attribute("type") == "stop")
_notations.insert(_notations.begin(), notation);
else
_notations.push_back(notation);
QString tiedType = notation.attribute("type");
if (tiedType != "start" && tiedType != "stop" && tiedType != "let-ring") {
_logger->logError(QString("unknown tied type %1").arg(tiedType), &_e);
Expand Down Expand Up @@ -6453,27 +6495,12 @@ static void addArpeggio(ChordRest* cr, const QString& arpeggioType,
}
}

//---------------------------------------------------------
// addArticLaissezVibrer
//---------------------------------------------------------

static void addArticLaissezVibrer(const Note* const note)
{
Q_ASSERT(note);
auto chord = note->chord();
if (!hasLaissezVibrer(chord)) {
Articulation* na = new Articulation(SymId::articLaissezVibrerBelow, chord->score());
chord->add(na);
}

}

//---------------------------------------------------------
// addTie
//---------------------------------------------------------

static void addTie(const Notation& notation, Score* score, Note* note, const int track,
Tie*& tie, MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
std::map<int, Tie*>& ties, MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
{
Q_ASSERT(note);
const QString& type = notation.attribute("type");
Expand All @@ -6484,18 +6511,22 @@ static void addTie(const Notation& notation, Score* score, Note* note, const int
// ignore, nothing to do
}
else if (type == "start") {
if (tie) {
if (ties[note->pitch()]) {
// Unterminated tie on this pitch. Clean this up, then resume.
logger->logError(QString("Tie already active"), xmlreader);
cleanupUnterminatedTie(ties[note->pitch()]);
ties[note->pitch()] = 0;
}
tie = new Tie(score);
note->setTieFor(tie);
tie->setStartNote(note);
tie->setTrack(track);
ties[note->pitch()] = new Tie(score);
Tie* currTie = ties[note->pitch()];
note->setTieFor(currTie);
currTie->setStartNote(note);
currTie->setTrack(track);

if (orientation == "over")
tie->setSlurDirection(Direction::UP);
currTie->setSlurDirection(Direction::UP);
else if (orientation == "under")
tie->setSlurDirection(Direction::DOWN);
currTie->setSlurDirection(Direction::DOWN);
else if (orientation == "auto")
; // ignore
else if (orientation == "")
Expand All @@ -6504,13 +6535,20 @@ static void addTie(const Notation& notation, Score* score, Note* note, const int
logger->logError(QString("unknown tied orientation: %1").arg(orientation), xmlreader);

if (lineType == "dotted")
tie->setLineType(1);
currTie->setLineType(1);
else if (lineType == "dashed")
tie->setLineType(2);
tie = nullptr;
currTie->setLineType(2);

}
else if (type == "stop")
; // ignore
if (ties[note->pitch()]) {
Tie* currTie = ties[note->pitch()];
currTie->setEndNote(note);
note->setTieBack(currTie);
ties[note->pitch()] = 0;
}
else
logger->logError(QString("Non-started tie terminated. No-op."), xmlreader);
else if (type == "let-ring")
addArticLaissezVibrer(note);
else
Expand Down Expand Up @@ -6923,7 +6961,7 @@ void MusicXMLParserNotations::addNotation(const Notation& notation, ChordRest* c

void MusicXMLParserNotations::addToScore(ChordRest* const cr, Note* const note, const int tick, SlurStack& slurs,
Glissando* glissandi[MAX_NUMBER_LEVEL][2], MusicXmlSpannerMap& spanners,
TrillStack& trills, Tie*& tie)
TrillStack& trills, std::map<int, Tie*>& ties)
{
addArpeggio(cr, _arpeggioType, _logger, &_e);
addBreath(cr, cr->tick(), _breath);
Expand All @@ -6940,7 +6978,7 @@ void MusicXMLParserNotations::addToScore(ChordRest* const cr, Note* const note,
addGlissandoSlide(notation, note, glissandi, spanners, _logger, &_e);
}
else if (note && notation.name() == "tied") {
addTie(notation, _score, note, cr->track(), tie, _logger, &_e);
addTie(notation, _score, note, cr->track(), ties, _logger, &_e);
}
else if (note && notation.parent() == "technical") {
addTechnical(notation, note);
Expand Down
4 changes: 2 additions & 2 deletions importexport/musicxml/importmxmlpass2.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class MusicXMLParserNotations {
void parse();
void addToScore(ChordRest* const cr, Note* const note, const int tick, SlurStack& slurs,
Glissando* glissandi[MAX_NUMBER_LEVEL][2], MusicXmlSpannerMap& spanners, TrillStack& trills,
Tie*& tie);
std::map<int, Tie*>& ties);
MusicXmlTupletDesc tupletDesc() const { return _tupletDesc; }
QString tremoloType() const { return _tremoloType; }
int tremoloNr() const { return _tremoloNr; }
Expand Down Expand Up @@ -327,7 +327,7 @@ class MusicXMLParserPass2 {

Glissando* _glissandi[MAX_NUMBER_LEVEL][2]; ///< Current slides ([0]) / glissandi ([1])

Tie* _tie;
std::map<int, Tie*> _ties;
Volta* _lastVolta;
bool _hasDrumset; ///< drumset defined TODO: move to pass 1

Expand Down
45 changes: 45 additions & 0 deletions libmscore/layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,51 @@ void Score::connectArpeggios()
}
}

//---------------------------------------------------------
// fixupLaissezVibrer
// This is a temporary hack to improve the placement of
// l.v. articulations when importing MusciXML.
// TODO: vastly improve the automatic placement of the
// l.v. articulation.
//---------------------------------------------------------

void Score::fixupLaissezVibrer()
{
int tracks = nstaves() * VOICES;
Measure* m = firstMeasure();
if (!m)
return;
if (m->canvasPos() == QPointF(0, 0))
doLayout();

SegmentType st = SegmentType::ChordRest;
for (Segment* s = m->first(st); s; s = s->next1(st)) {
for (int i = 0; i < tracks; ++i) {
Element* e = s->element(i);
if (e == 0 || !e->isChord())
continue;
Chord* c = toChord(e);
for (auto a : c->articulations()) {
if (a->symId() != SymId::articLaissezVibrerAbove && a->symId() != SymId::articLaissezVibrerBelow)
continue;

// Manually override placement
a->setAutoplace(false);
a->setMinDistance(Spatium(0));
c->layoutArticulations();
c->layoutArticulations2();
bool below = a->symId() == SymId::articLaissezVibrerBelow;
Note* n = below ? c->notes().front() : c->notes().back();

QPointF target = below ? n->canvasBoundingRect().bottomLeft() + QPointF(0.5 * n->width(), 0.25 * spatium())
: n->canvasBoundingRect().topLeft() + QPointF(0.5 * n->width(), -0.25 * spatium());
QPointF current = below ? a->canvasBoundingRect().topLeft() : a->canvasBoundingRect().bottomLeft();
a->setOffset(a->offset() + target - current);
}
}
}
}

//---------------------------------------------------------
// checkDivider
//---------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions libmscore/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ class Score : public QObject, public ScoreElement {

void connectTies(bool silent=false);
void connectArpeggios();
void fixupLaissezVibrer();

qreal point(const Spatium sp) const { return sp.val() * spatium(); }

Expand Down