Skip to content

Commit 2d4be5f

Browse files
iveshenry18Jojo-Schmitz
authored andcommitted
ENG-21: Connect cross-voice arpeggios
Due to the one-to-one relationship between Arpeggios and Chords, there is no ideal way to import arpeggios that include notes across voices; currently, such a case results in the creation of one arpeggio for each chord with notes containing the <arpeggiated> tag. This commit adds a function to Score that iterates through these and connects them; this function is called after the score has been imported. Additionally, this commits corrects the exporting for this case, adding the <arpeggiated> tag to not only notes in a chord with an arpeggio, but notes that intersect an arpeggio in any voice in the given segment and staff. This effectively creates a correct round-trip for cross-voice arpeggios, although with the (theoretical) false positive of connecting multiple arpeggios in the same segment in the same staff even if they are intended to remain separate. Duplicate of musescore#8506, plus fixing a merge conflict due to musescore#8199
1 parent 0efebd2 commit 2d4be5f

9 files changed

+1855
-2
lines changed

importexport/musicxml/exportxml.cpp

+22-2
Original file line numberDiff line numberDiff line change
@@ -2824,6 +2824,26 @@ void ExportMusicXml::chordAttributes(Chord* chord, Notations& notations, Technic
28242824
}
28252825
}
28262826

2827+
//---------------------------------------------------------
2828+
// findArpeggio
2829+
//---------------------------------------------------------
2830+
2831+
static Arpeggio* findArpeggio(Note* note)
2832+
{
2833+
if (note->chord()->arpeggio()) return note->chord()->arpeggio();
2834+
2835+
// Check if there is an arpeggio in any voice that intersects the note on the y-axis
2836+
for (int i = staff2track(note->staffIdx()); i < staff2track(note->staffIdx() + 1); ++i) {
2837+
Element* elem = note->chord()->segment()->elist()[i];
2838+
if (elem && elem->isChord()
2839+
&& toChord(elem)->arpeggio()
2840+
&& note->pageBoundingRect().top() + note->headHeight() >= toChord(elem)->arpeggio()->pageBoundingRect().top()
2841+
&& note->pageBoundingRect().top() + note->headHeight() <= toChord(elem)->arpeggio()->pageBoundingRect().bottom())
2842+
return toChord(elem)->arpeggio();
2843+
}
2844+
return 0;
2845+
}
2846+
28272847
//---------------------------------------------------------
28282848
// arpeggiate
28292849
//---------------------------------------------------------
@@ -3464,8 +3484,8 @@ void ExportMusicXml::chord(Chord* chord, int staff, const std::vector<Lyrics*>*
34643484
}
34653485

34663486
technical.etag(_xml);
3467-
if (chord->arpeggio()) {
3468-
arpeggiate(chord->arpeggio(), note == nl.front(), note == nl.back(), _xml, notations);
3487+
if (Arpeggio* arp = findArpeggio(note)) {
3488+
arpeggiate(arp, note == nl.front(), note == nl.back(), _xml, notations);
34693489
}
34703490
for (Spanner* spanner : note->spannerFor())
34713491
if (spanner->type() == ElementType::GLISSANDO) {

importexport/musicxml/importmxmlpass2.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,8 @@ void MusicXMLParserPass2::scorePartwise()
16271627
auto lm = _score->lastMeasure();
16281628
if (lm && lm->endBarLineType() == BarLineType::NORMAL)
16291629
lm->setEndBarLineType(BarLineType::NORMAL, 0);
1630+
1631+
_score->connectArpeggios();
16301632
}
16311633

16321634
//---------------------------------------------------------

libmscore/layout.cpp

+51
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//=============================================================================
1212

1313
#include "accidental.h"
14+
#include "arpeggio.h"
1415
#include "barline.h"
1516
#include "beam.h"
1617
#include "box.h"
@@ -1525,6 +1526,56 @@ void Score::connectTies(bool silent)
15251526
}
15261527
}
15271528

1529+
//---------------------------------------------------------
1530+
// connectArpeggios
1531+
// Fake cross-voice arpeggios by hiding all but the first
1532+
// and extending the first to cover the others.
1533+
// Retains the other properties of the first arpeggio.
1534+
//---------------------------------------------------------
1535+
1536+
void Score::connectArpeggios()
1537+
{
1538+
for (auto segment = firstSegment(SegmentType::ChordRest); segment; segment = segment->next1(SegmentType::ChordRest)) {
1539+
for (int staff = 0; staff < nstaves(); ++staff) {
1540+
qreal minTop = 10000;
1541+
qreal maxBottom = -10000;
1542+
int firstArpeggio = -1;
1543+
bool multipleArpeggios = false;
1544+
for (int i = staff2track(staff); i < staff2track(staff + 1); ++i) {
1545+
if (segment->elist()[i] && segment->elist()[i]->isChord()) {
1546+
Chord* chord = toChord(segment->elist()[i]);
1547+
if (chord->arpeggio() && chord->arpeggio()->visible()) {
1548+
if (chord->pagePos() == QPointF(0, 0)) doLayout();
1549+
qreal localTop = chord->arpeggio()->pageBoundingRect().top();
1550+
qreal localBottom = chord->arpeggio()->pageBoundingRect().bottom();
1551+
minTop = qMin(localTop, minTop);
1552+
maxBottom = qMax(localBottom, maxBottom);
1553+
if (firstArpeggio == -1)
1554+
// Leave arpeggio, adjust height after collecting
1555+
firstArpeggio = i;
1556+
else {
1557+
// Hide arpeggio; firstArpeggio will be extended to cover it.
1558+
chord->arpeggio()->setVisible(false);
1559+
multipleArpeggios = true;
1560+
}
1561+
}
1562+
}
1563+
}
1564+
if (firstArpeggio != -1 && multipleArpeggios) {
1565+
// Stretch first arpeggio to cover deleted
1566+
Chord* firstArpeggioChord = toChord(segment->elist()[firstArpeggio]);
1567+
Arpeggio* arpeggio = firstArpeggioChord->arpeggio();
1568+
qreal topDiff = minTop - arpeggio->pageBoundingRect().top();
1569+
qreal bottomDiff = maxBottom - arpeggio->pageBoundingRect().bottom();
1570+
arpeggio->setUserLen1(topDiff);
1571+
arpeggio->setUserLen2(bottomDiff);
1572+
arpeggio->setPropertyFlags(Pid::ARP_USER_LEN1, PropertyFlags::UNSTYLED);
1573+
arpeggio->setPropertyFlags(Pid::ARP_USER_LEN2, PropertyFlags::UNSTYLED);
1574+
}
1575+
}
1576+
}
1577+
}
1578+
15281579
//---------------------------------------------------------
15291580
// checkDivider
15301581
//---------------------------------------------------------

libmscore/score.h

+1
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,7 @@ class Score : public QObject, public ScoreElement {
10351035
Segment* lastSegmentMM() const;
10361036

10371037
void connectTies(bool silent=false);
1038+
void connectArpeggios();
10381039

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

20.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)