@@ -4544,27 +4544,6 @@ static void measureStyle(XmlWriter& xml, Attributes& attr, const Measure* const
4544
4544
}
4545
4545
}
4546
4546
4547
- // ---------------------------------------------------------
4548
- // findFretDiagram
4549
- // ---------------------------------------------------------
4550
-
4551
- static const FretDiagram* findFretDiagram (int strack, int etrack, int track, Segment* seg)
4552
- {
4553
- if (seg->segmentType () == SegmentType::ChordRest) {
4554
- for (const Element* e : seg->annotations ()) {
4555
-
4556
- int wtrack = -1 ; // track to write annotation
4557
-
4558
- if (strack <= e->track () && e->track () < etrack)
4559
- wtrack = findTrackForAnnotations (e->track (), seg);
4560
-
4561
- if (track == wtrack && e->type () == ElementType::FRET_DIAGRAM)
4562
- return static_cast <const FretDiagram*>(e);
4563
- }
4564
- }
4565
- return 0 ;
4566
- }
4567
-
4568
4547
// ---------------------------------------------------------
4569
4548
// commonAnnotations
4570
4549
// ---------------------------------------------------------
@@ -4601,47 +4580,103 @@ static bool commonAnnotations(ExportMusicXml* exp, const Element* e, int sstaff)
4601
4580
// annotations
4602
4581
// ---------------------------------------------------------
4603
4582
4604
- /*
4605
- * Write annotations that are attached to chords or rests
4606
- */
4607
-
4608
- // In MuseScore, Element::FRET_DIAGRAM and Element::HARMONY are separate annotations,
4609
- // in MusicXML they are combined in the harmony element. This means they have to be matched.
4610
- // TODO: replace/repair current algorithm (which can only handle one FRET_DIAGRAM and one HARMONY)
4583
+ // Only handle common annotations, others are handled elsewhere
4611
4584
4612
4585
static void annotations (ExportMusicXml* exp, int strack, int etrack, int track, int sstaff, Segment* seg)
4613
4586
{
4614
- if ( seg->segmentType () == SegmentType::ChordRest ) {
4587
+ for ( const Element* e : seg->annotations () ) {
4615
4588
4616
- const FretDiagram* fd = findFretDiagram (strack, etrack, track, seg);
4617
- // if (fd) qDebug("annotations seg %p found fretboard diagram %p", seg, fd);
4589
+ int wtrack = -1 ; // track to write annotation
4618
4590
4619
- for (const Element* e : seg->annotations ()) {
4591
+ if (strack <= e->track () && e->track () < etrack)
4592
+ wtrack = findTrackForAnnotations (e->track (), seg);
4620
4593
4621
- int wtrack = -1 ; // track to write annotation
4594
+ if (track == wtrack) {
4595
+ if (commonAnnotations (exp , e, sstaff))
4596
+ ; // already handled
4597
+ }
4598
+ }
4599
+ }
4622
4600
4623
- if (strack <= e->track () && e->track () < etrack)
4624
- wtrack = findTrackForAnnotations (e->track (), seg);
4601
+ // ---------------------------------------------------------
4602
+ // harmonies
4603
+ // ---------------------------------------------------------
4625
4604
4626
- if (track == wtrack) {
4627
- if (commonAnnotations (exp , e, sstaff))
4628
- ; // already handled
4629
- else if (e->isHarmony ()) {
4630
- // qDebug("annotations seg %p found harmony %p", seg, e);
4631
- exp ->harmony (toHarmony (e), fd);
4632
- fd = nullptr ; // make sure to write only once ...
4633
- }
4634
- else if (e->isFermata () || e->isFiguredBass () || e->isFretDiagram () || e->isJump ())
4635
- ; // handled separately by chordAttributes(), figuredBass(), findFretDiagram() or ignored
4636
- else
4637
- qDebug (" direction type %s at tick %d not implemented" ,
4638
- Element::name (e->type ()), seg->tick ().ticks ());
4639
- }
4605
+ /*
4606
+ * Helper method to export harmonies and chord diagrams for a single segment.
4607
+ */
4608
+
4609
+ static void segmentHarmonies (ExportMusicXml* exp, int track, Segment* seg, int offset)
4610
+ {
4611
+ const std::vector<Element*> diagrams = seg->findAnnotations (ElementType::FRET_DIAGRAM, track, track);
4612
+ std::vector<Element*> harmonies = seg->findAnnotations (ElementType::HARMONY, track, track);
4613
+
4614
+ for (const Element* e : diagrams) {
4615
+ const FretDiagram* diagram = toFretDiagram (e);
4616
+ const Harmony* harmony = diagram->harmony ();
4617
+ if (harmony) {
4618
+ exp ->harmony (harmony, diagram, offset);
4619
+ }
4620
+ else if (! harmonies.empty ()) {
4621
+ const Element* defaultHarmony = harmonies.back ();
4622
+ exp ->harmony (toHarmony (defaultHarmony), diagram, offset);
4623
+ harmonies.pop_back ();
4624
+ }
4625
+ else {
4626
+ // Found a fret diagram with no harmony, ignore
4627
+ qDebug (" segmentHarmonies() seg %p found fretboard diagram %p w/o harmony: cannot write" , seg, diagram);
4640
4628
}
4641
- if (fd)
4642
- // found fd but no harmony, cannot write (MusicXML would be invalid)
4643
- qDebug (" seg %p found fretboard diagram %p w/o harmony: cannot write" ,
4644
- seg, fd);
4629
+ }
4630
+
4631
+ for (const Element* e: harmonies)
4632
+ exp ->harmony (toHarmony (e), 0 , offset);
4633
+ }
4634
+
4635
+ /*
4636
+ * Write harmonies and fret diagrams that are attached to chords or rests.
4637
+ *
4638
+ * There are fondamental differences between the ways Musescore and MusicXML handle harmonies (Chord symbols)
4639
+ * and fretboard diagrams.
4640
+ *
4641
+ * In MuseScore, the Harmony element is now a child of FretboardDiagram BUT in previous versions,
4642
+ * both elements were independant siblings so we have to handle both cases.
4643
+ * In MusicXML, fretboard diagram is always contained in a harmony element.
4644
+ *
4645
+ * In MuseScore, Harmony elements are not always linked to notes, and each Harmony will be contained
4646
+ * in a `ChordRest` Segment.
4647
+ * In MusicXML, those successive Harmony elements must be exported before the note with different offsets.
4648
+ *
4649
+ * Edge cases that we simply cannot handle:
4650
+ * - as of MusicXML 3.1, there is no way to represent a diagram without an associated chord symbol,
4651
+ * so when we encounter such an object in MuseScore, we simply cannot export it.
4652
+ * - If a ChordRest segment contans a FretboardDiagram with no harmonies and several different Harmony siblings,
4653
+ * we simply have to pick a random one to export.
4654
+ */
4655
+
4656
+ static void harmonies (ExportMusicXml* exp, int track, Segment* seg, int divisions)
4657
+ {
4658
+ int offset = 0 ;
4659
+ segmentHarmonies (exp , track, seg, offset);
4660
+
4661
+ // Edge case: find remaining `harmony` elements.
4662
+ // Suppose you have one single whole note in the measure but several chord symbols.
4663
+ // In MuseScore, each `Harmony` object will be stored in a `ChordRest` Segment that contains
4664
+ // no other Chords.
4665
+ // But in MusicXML, you are supposed to output all `harmony` elements before the first `note`,
4666
+ // with different `offset` parameters.
4667
+ //
4668
+ // That's why we need to explore the remaining segments to find
4669
+ // `Harmony` and `FretDiagram` elements in Segments without Chords and output them now.
4670
+ for (auto seg1 = seg->next (); seg1; seg1 = seg1->next ()) {
4671
+ if (!seg1->isChordRestType ())
4672
+ continue ;
4673
+
4674
+ const auto el1 = seg1->element (track);
4675
+ if (el1) // found a ChordRest, next harmony will be attached to this one
4676
+ break ;
4677
+
4678
+ offset = (seg1->tick () - seg->tick ()).ticks () / divisions;
4679
+ segmentHarmonies (exp , track, seg1, offset);
4645
4680
}
4646
4681
}
4647
4682
@@ -5886,19 +5921,8 @@ void ExportMusicXml::writeMeasureTracks(const Measure* const m,
5886
5921
}
5887
5922
tboxesBelowWritten = true ;
5888
5923
}
5924
+ harmonies (this , st, seg, div );
5889
5925
annotations (this , strack, etrack, st, sstaff, seg);
5890
- // look for more harmony
5891
- for (auto seg1 = seg->next (); seg1; seg1 = seg1->next ()) {
5892
- if (seg1->isChordRestType ()) {
5893
- const auto el1 = seg1->element (st);
5894
- if (el1) // found a ChordRest, next harmony will be attach to this one
5895
- break ;
5896
- for (auto annot : seg1->annotations ()) {
5897
- if (annot->isHarmony () && annot->track () == st)
5898
- harmony (toHarmony (annot), 0 , (seg1->tick () - seg->tick ()).ticks () / div );
5899
- }
5900
- }
5901
- }
5902
5926
figuredBass (_xml, strack, etrack, st, static_cast <const ChordRest*>(el), fbMap, div );
5903
5927
spannerStart (this , strack, etrack, st, sstaff, seg);
5904
5928
}
0 commit comments