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-21: Connect cross-voice arpeggios #8506

Merged
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
24 changes: 22 additions & 2 deletions importexport/musicxml/exportxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2803,6 +2803,26 @@ void ExportMusicXml::chordAttributes(Chord* chord, Notations& notations, Technic
}
}

//---------------------------------------------------------
// findArpeggio
//---------------------------------------------------------

static Arpeggio* findArpeggio(Note* note)
{
if (note->chord()->arpeggio()) return note->chord()->arpeggio();

// Check if there is an arpeggio in any voice that intersects the note on the y-axis
for (int i = staff2track(note->staffIdx()); i < staff2track(note->staffIdx() + 1); ++i) {
Element* elem = note->chord()->segment()->elist()[i];
if (elem && elem->isChord()
&& toChord(elem)->arpeggio()
&& note->pageBoundingRect().top() + note->headHeight() >= toChord(elem)->arpeggio()->pageBoundingRect().top()
&& note->pageBoundingRect().top() + note->headHeight() <= toChord(elem)->arpeggio()->pageBoundingRect().bottom())
return toChord(elem)->arpeggio();
}
return 0;
}

//---------------------------------------------------------
// arpeggiate
//---------------------------------------------------------
Expand Down Expand Up @@ -3428,8 +3448,8 @@ void ExportMusicXml::chord(Chord* chord, int staff, const std::vector<Lyrics*>*
}

technical.etag(_xml);
if (chord->arpeggio()) {
arpeggiate(chord->arpeggio(), note == nl.front(), note == nl.back(), _xml, notations);
if (Arpeggio* arp = findArpeggio(note)) {
arpeggiate(arp, note == nl.front(), note == nl.back(), _xml, notations);
}
for (Spanner* spanner : note->spannerFor())
if (spanner->type() == ElementType::GLISSANDO) {
Expand Down
2 changes: 2 additions & 0 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,8 @@ void MusicXMLParserPass2::scorePartwise()
// TODO, handle other tracks?
if (_score->lastMeasure()->endBarLineType() == BarLineType::NORMAL)
_score->lastMeasure()->setEndBarLineType(BarLineType::NORMAL, 0);

_score->connectArpeggios();
}

//---------------------------------------------------------
Expand Down
51 changes: 51 additions & 0 deletions libmscore/layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//=============================================================================

#include "accidental.h"
#include "arpeggio.h"
#include "barline.h"
#include "beam.h"
#include "box.h"
Expand Down Expand Up @@ -1525,6 +1526,56 @@ void Score::connectTies(bool silent)
}
}

//---------------------------------------------------------
// connectArpeggios
// Fake cross-voice arpeggios by hiding all but the first
// and extending the first to cover the others.
// Retains the other properties of the first arpeggio.
//---------------------------------------------------------

void Score::connectArpeggios()
{
for (auto segment = firstSegment(SegmentType::ChordRest); segment; segment = segment->next1(SegmentType::ChordRest)) {
for (int staff = 0; staff < nstaves(); ++staff) {
qreal minTop = 10000;
qreal maxBottom = -10000;
int firstArpeggio = -1;
bool multipleArpeggios = false;
for (int i = staff2track(staff); i < staff2track(staff + 1); ++i) {
if (segment->elist()[i] && segment->elist()[i]->isChord()) {
Chord* chord = toChord(segment->elist()[i]);
if (chord->arpeggio() && chord->arpeggio()->visible()) {
if (chord->pagePos() == QPointF(0, 0)) doLayout();
qreal localTop = chord->arpeggio()->pageBoundingRect().top();
qreal localBottom = chord->arpeggio()->pageBoundingRect().bottom();
minTop = qMin(localTop, minTop);
maxBottom = qMax(localBottom, maxBottom);
if (firstArpeggio == -1)
// Leave arpeggio, adjust height after collecting
firstArpeggio = i;
else {
// Hide arpeggio; firstArpeggio will be extended to cover it.
chord->arpeggio()->setVisible(false);
multipleArpeggios = true;
}
}
}
}
if (firstArpeggio != -1 && multipleArpeggios) {
// Stretch first arpeggio to cover deleted
Chord* firstArpeggioChord = toChord(segment->elist()[firstArpeggio]);
Arpeggio* arpeggio = firstArpeggioChord->arpeggio();
qreal topDiff = minTop - arpeggio->pageBoundingRect().top();
qreal bottomDiff = maxBottom - arpeggio->pageBoundingRect().bottom();
arpeggio->setUserLen1(topDiff);
arpeggio->setUserLen2(bottomDiff);
arpeggio->setPropertyFlags(Pid::ARP_USER_LEN1, PropertyFlags::UNSTYLED);
arpeggio->setPropertyFlags(Pid::ARP_USER_LEN2, PropertyFlags::UNSTYLED);
}
}
}
}

//---------------------------------------------------------
// checkDivider
//---------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions libmscore/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,7 @@ class Score : public QObject, public ScoreElement {
Segment* lastSegmentMM() const;

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

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

Expand Down
Binary file added mtest/musicxml/io/testConnectedArpeggios.pdf
Binary file not shown.