Browse Source

merge osmd-public 1.5.2: add tuplet number limit, add soft-accent, fix rest note TypeLength undefined, fix CollectionUtil.binarySearch

sschmidTU 2 years ago
parent
commit
3d851b1752

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "osmd-extended",
-  "version": "1.5.1",
+  "version": "1.5.2",
   "description": "Private / sponsor exclusive OSMD mirror/audio player.",
   "main": "build/opensheetmusicdisplay.min.js",
   "types": "build/dist/src/index.d.ts",

+ 10 - 0
src/MusicalScore/Graphical/EngravingRules.ts

@@ -109,6 +109,8 @@ export class EngravingRules {
     public DistanceOffsetBetweenTwoHorizontallyCrossedWedges: number;
     public WedgeMinLength: number;
     public WedgeEndDistanceBetweenTimestampsFactor: number;
+    public SoftAccentWedgePadding: number;
+    public SoftAccentSizeFactor: number;
     public DistanceBetweenAdjacentDynamics: number;
     public TempoChangeMeasureValidity: number;
     public TempoContinousFactor: number;
@@ -151,6 +153,9 @@ export class EngravingRules {
     public TripletsBracketed: boolean;
     public TupletNumberLabelHeight: number;
     public TupletNumberYOffset: number;
+    public TupletNumberLimitConsecutiveRepetitions: boolean;
+    public TupletNumberMaxConsecutiveRepetitions: number;
+    public TupletNumberAlwaysDisableAfterFirstMax: boolean;
     public LabelMarginBorderFactor: number;
     public TupletVerticalLineLength: number;
     public TupletNumbersInTabs: boolean;
@@ -490,6 +495,8 @@ export class EngravingRules {
         this.DistanceOffsetBetweenTwoHorizontallyCrossedWedges = 0.3;
         this.WedgeMinLength = 2.0;
         this.WedgeEndDistanceBetweenTimestampsFactor = 1.75;
+        this.SoftAccentWedgePadding = 0.4;
+        this.SoftAccentSizeFactor = 0.6;
         this.DistanceBetweenAdjacentDynamics = 0.75;
 
         // Tempo Variables
@@ -532,6 +539,9 @@ export class EngravingRules {
         this.TripletsBracketed = false; // special setting for triplets, overrides tuplet setting (for triplets only)
         this.TupletNumberLabelHeight = 1.5 * EngravingRules.unit;
         this.TupletNumberYOffset = 0.5;
+        this.TupletNumberLimitConsecutiveRepetitions = true;
+        this.TupletNumberMaxConsecutiveRepetitions = 2;
+        this.TupletNumberAlwaysDisableAfterFirstMax = true;
         this.LabelMarginBorderFactor = 0.1;
         this.TupletVerticalLineLength = 0.5;
         this.TupletNumbersInTabs = false; // disabled by default, nonstandard in tabs, at least how we show them in non-tabs.

+ 12 - 4
src/MusicalScore/Graphical/GraphicalContinuousDynamicExpression.ts

@@ -23,6 +23,8 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
     private lines: GraphicalLine[] = [];
     private startMeasure: GraphicalMeasure;
     private endMeasure: GraphicalMeasure;
+    //public StartIsEnd: boolean;
+    public IsSoftAccent: boolean;
 
     /**
      * Create a new instance of the GraphicalContinuousDynamicExpression
@@ -67,7 +69,7 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
         const skyBottomLineCalculator: SkyBottomLineCalculator = this.parentStaffLine.SkyBottomLineCalculator;
         const left: number = this.IsVerbal ? this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginLeft : 0;
         const right: number = this.IsVerbal ? this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginRight : 0;
-        if (!this.IsVerbal && this.lines.length < 2) {
+        if (!this.IsSoftAccent && !this.IsVerbal && this.lines.length < 2) {
             log.warn("Not enough lines for SkyBottomLine calculation");
         }
         if (!this.IsVerbal) {
@@ -80,7 +82,11 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
         }
         switch (this.Placement) {
             case PlacementEnum.Above:
-                if (!this.IsVerbal) {
+                if (this.IsSoftAccent) {
+                    skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
+                    skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[2].End, this.lines[2].Start);
+                    skyBottomLineCalculator.updateSkyLineWithLine(this.lines[0].End, this.lines[2].End, this.lines[0].End.y);
+                } else if (!this.IsVerbal) {
                     if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
                         skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
                     } else if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.diminuendo) {
@@ -269,8 +275,10 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
         // TODO is the center position correct? it wasn't set before, important for AlignmentManager.alignDynamicExpressions()
         // console.log(`relative y, center y: ${this.PositionAndShape.RelativePosition.y},${this.PositionAndShape.Center.y})`);
 
-
-        if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+        if (this.IsSoftAccent) {
+            this.PositionAndShape.BorderMarginLeft = 0;
+            this.PositionAndShape.BorderMarginRight = this.lines[3].Start.x - this.lines[0].Start.x;
+        } else if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
             this.PositionAndShape.BorderMarginLeft = 0;
             this.PositionAndShape.BorderMarginRight = this.lines[0].End.x - this.lines[0].Start.x;
         } else {

+ 100 - 4
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -1084,6 +1084,75 @@ export abstract class MusicSheetCalculator {
     }
 
     protected calculateTupletNumbers(): void {
+        if (!this.rules.TupletNumberLimitConsecutiveRepetitions) {
+            return;
+        }
+        let currentTupletNumber: number = -1;
+        let consecutiveTupletCount: number = 0;
+        let currentTuplet: Tuplet = undefined;
+        let skipTuplet: Tuplet = undefined; // if set, ignore (further) handling of this tuplet
+        const disabledPerVoice: Object = {};
+        for (const instrument of this.graphicalMusicSheet.ParentMusicSheet.Instruments) {
+            for (const voice of instrument.Voices) {
+                consecutiveTupletCount = 0; // reset for next voice
+                disabledPerVoice[voice.VoiceId] = {};
+                for (const ve of voice.VoiceEntries) {
+                    if (ve.Notes.length > 0) {
+                        const firstNote: Note = ve.Notes[0];
+                        if (!firstNote.NoteTuplet) {
+                            currentTupletNumber = -1;
+                            consecutiveTupletCount = 0;
+                            currentTuplet = undefined;
+                            continue;
+                        }
+                        if (firstNote.NoteTuplet === skipTuplet) {
+                            continue;
+                        }
+                        let typeLength: Fraction = firstNote.TypeLength;
+                        if (!typeLength) {
+                            // shouldn't happen, now that rest notes have TypeLength set too, see VoiceGenerator.addRestNote(), addSingleNote()
+                            //   see test_tuplets_starting_with_rests_layout.mxl (first measure bass)
+                            log.warn("note missing TypeLength");
+                            typeLength = firstNote.NoteTuplet.Fractions[0];
+                        }
+                        if (firstNote.NoteTuplet !== currentTuplet) {
+                            if (disabledPerVoice[voice.VoiceId][firstNote.NoteTuplet.TupletLabelNumber]) {
+                                if (disabledPerVoice[voice.VoiceId][firstNote.NoteTuplet.TupletLabelNumber][typeLength.RealValue]) {
+                                    firstNote.NoteTuplet.RenderTupletNumber = false;
+                                    skipTuplet = firstNote.NoteTuplet;
+                                    continue;
+                                }
+                            }
+                        }
+                        if (firstNote.NoteTuplet.TupletLabelNumber !== currentTupletNumber) {
+                            currentTupletNumber = firstNote.NoteTuplet.TupletLabelNumber;
+                            consecutiveTupletCount = 0;
+                        }
+                        currentTuplet = firstNote.NoteTuplet;
+                        consecutiveTupletCount++;
+                        if (consecutiveTupletCount <= this.rules.TupletNumberMaxConsecutiveRepetitions) {
+                            firstNote.NoteTuplet.RenderTupletNumber = true; // need to re-activate after re-render when it was set to false
+                        }
+                        if (consecutiveTupletCount === this.rules.TupletNumberMaxConsecutiveRepetitions && this.rules.TupletNumberAlwaysDisableAfterFirstMax) {
+                            if (!disabledPerVoice[voice.VoiceId][currentTupletNumber]) {
+                                disabledPerVoice[voice.VoiceId][currentTupletNumber] = {};
+                            }
+                            disabledPerVoice[voice.VoiceId][currentTupletNumber][typeLength.RealValue] = true;
+                        }
+                        if (consecutiveTupletCount > this.rules.TupletNumberMaxConsecutiveRepetitions) {
+                            firstNote.NoteTuplet.RenderTupletNumber = false;
+                            if (this.rules.TupletNumberAlwaysDisableAfterFirstMax) {
+                                if (!disabledPerVoice[voice.VoiceId][currentTupletNumber]) {
+                                    disabledPerVoice[voice.VoiceId][currentTupletNumber] = {};
+                                }
+                                disabledPerVoice[voice.VoiceId][currentTupletNumber][typeLength.RealValue] = true;
+                            }
+                        }
+                        skipTuplet = currentTuplet;
+                    }
+                }
+            }
+        }
         return;
     }
 
@@ -1128,6 +1197,7 @@ export abstract class MusicSheetCalculator {
     * @param startPosInStaffline Starting point in staff line
     */
     public calculateGraphicalContinuousDynamic(graphicalContinuousDynamic: GraphicalContinuousDynamicExpression, startPosInStaffline: PointF2D): void {
+        const isSoftAccent: boolean = graphicalContinuousDynamic.IsSoftAccent;
         const staffIndex: number = graphicalContinuousDynamic.ParentStaffLine.ParentStaff.idInMusicSheet;
         // TODO: Previously the staffIndex was passed down. BUT you can (and this function actually does this) get it from
         // the musicSystem OR from the ParentStaffLine. Is this the same index?
@@ -1172,9 +1242,18 @@ export abstract class MusicSheetCalculator {
         const beginOfNextNote: Fraction = Fraction.plus(endAbsoluteTimestamp, maxNoteLength);
         const nextNotePosInStaffLine: PointF2D = this.getRelativePositionInStaffLineFromTimestamp(
             beginOfNextNote, staffIndex, endStaffLine, isPartOfMultiStaffInstrument, 0);
+        const wedgePadding: number = this.rules.SoftAccentWedgePadding;
+        const staffEntryWidth: number = container.getFirstNonNullStaffEntry().PositionAndShape.Size.width; // staff entry widths for whole notes is too long
+        const sizeFactor: number = this.rules.SoftAccentSizeFactor;
+        //const standardWidth: number = 2;
+
         //If the next note position is not on the next staffline
         //extend close to the next note
-        if (nextNotePosInStaffLine.x > endPosInStaffLine.x && nextNotePosInStaffLine.x < endOfMeasure) {
+        if (isSoftAccent) {
+            //startPosInStaffline.x -= 1;
+            startPosInStaffline.x -= staffEntryWidth / 2 * sizeFactor + wedgePadding;
+            endPosInStaffLine.x = startPosInStaffline.x + staffEntryWidth / 2 * sizeFactor;
+        } else if (nextNotePosInStaffLine.x > endPosInStaffLine.x && nextNotePosInStaffLine.x < endOfMeasure) {
             endPosInStaffLine.x += (nextNotePosInStaffLine.x - endPosInStaffLine.x) / this.rules.WedgeEndDistanceBetweenTimestampsFactor;
         } else { //Otherwise extend to the end of the measure
             endPosInStaffLine.x = endOfMeasure - this.rules.WedgeHorizontalMargin;
@@ -1198,13 +1277,13 @@ export abstract class MusicSheetCalculator {
         let secondGraphicalContinuousDynamic: GraphicalContinuousDynamicExpression = undefined;
 
         // last length check
-        if (sameStaffLine && endPosInStaffLine.x - startPosInStaffline.x < this.rules.WedgeMinLength) {
+        if (sameStaffLine && endPosInStaffLine.x - startPosInStaffline.x < this.rules.WedgeMinLength && !isSoftAccent) {
             endPosInStaffLine.x = startPosInStaffline.x + this.rules.WedgeMinLength;
         }
 
         // Upper staff wedge always starts at the given position and the lower staff wedge always starts at the begin of measure
         const upperStartX: number = startPosInStaffline.x;
-        const lowerStartX: number = endStaffLine.Measures[0].beginInstructionsWidth - this.rules.WedgeHorizontalMargin - 2;
+        let lowerStartX: number = endStaffLine.Measures[0].beginInstructionsWidth - this.rules.WedgeHorizontalMargin - 2;
         //TODO fix this when a range of measures to draw is given that doesn't include all the dynamic's measures (e.g. for crescendo)
         let upperEndX: number = 0;
         let lowerEndX: number = 0;
@@ -1221,6 +1300,17 @@ export abstract class MusicSheetCalculator {
         } else {
             upperEndX = endPosInStaffLine.x;
         }
+        if (isSoftAccent) {
+            // secondGraphicalContinuousDynamic = new GraphicalContinuousDynamicExpression(
+            //     graphicalContinuousDynamic.ContinuousDynamic,
+            //     graphicalContinuousDynamic.ParentStaffLine,
+            //     graphicalContinuousDynamic.StartMeasure.parentSourceMeasure
+            // );
+            // secondGraphicalContinuousDynamic.StartIsEnd = true;
+            // doesn't work well with secondGraphicalDynamic, positions/rendering messed up
+            lowerStartX = endPosInStaffLine.x + wedgePadding;
+            lowerEndX = lowerStartX + staffEntryWidth / 2 * sizeFactor;
+        }
 
         // the Height of the Expression's placement
         let idealY: number = 0;
@@ -1409,7 +1499,13 @@ export abstract class MusicSheetCalculator {
         // Crescendo (point to the left, opening to the right)
         graphicalContinuousDynamic.Lines.clear();
         if (graphicalContinuousDynamic.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
-            if (sameStaffLine) {
+            if (isSoftAccent) {
+                graphicalContinuousDynamic.createFirstHalfCrescendoLines(upperStartX, upperEndX, idealY);
+                graphicalContinuousDynamic.createSecondHalfDiminuendoLines(lowerStartX, lowerEndX, idealY);
+                graphicalContinuousDynamic.calcPsi();
+                // secondGraphicalContinuousDynamic.createSecondHalfDiminuendoLines(lowerStartX, lowerEndX, idealY);
+                // secondGraphicalContinuousDynamic.calcPsi();
+            } else if (sameStaffLine && !isSoftAccent) {
                 graphicalContinuousDynamic.createCrescendoLines(upperStartX, upperEndX, idealY);
                 graphicalContinuousDynamic.calcPsi();
             } else {

+ 9 - 0
src/MusicalScore/Graphical/SkyBottomLineCalculator.ts

@@ -202,6 +202,15 @@ export class SkyBottomLineCalculator {
 
         this.updateLines(results);
     }
+
+    public updateSkyLineWithLine(start: PointF2D, end: PointF2D, value: number): void {
+        const startIndex: number = Math.floor(start.x * this.SamplingUnit);
+        const endIndex: number = Math.ceil(end.x * this.SamplingUnit);
+        for (let i: number = startIndex + 1; i < Math.min(endIndex, this.SkyLine.length); i++) {
+            this.SkyLine[i] = value;
+        }
+    }
+
     /**
      * This method updates the SkyLine for a given Wedge.
      * @param start Start point of the wedge (the point where both lines meet)

+ 23 - 10
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -626,8 +626,20 @@ export class VexFlowMeasure extends GraphicalMeasure {
             // Draw tuplets
             for (const voiceID in this.vftuplets) {
                 if (this.vftuplets.hasOwnProperty(voiceID)) {
-                    for (const tuplet of this.vftuplets[voiceID]) {
-                        tuplet.setContext(ctx).draw();
+                    for (let i: number = 0; i < this.tuplets[voiceID].length; i++) {
+                        const tuplet: Tuplet = this.tuplets[voiceID][i][0];
+                        const vftuplet: VF.Tuplet = this.vftuplets[voiceID][i];
+                        if (!tuplet.RenderTupletNumber) {
+                            // (vftuplet as any).numerator_glyphs_stored = [...(vftuplet as any).numerator_glyphs];
+                            // (vftuplet as any).numerator_glyphs = [];
+                            (vftuplet as any).RenderTupletNumber = false;
+                        } else {
+                            // issue with restoring glyphs (version without vexflowpatch): need to deep copy array, otherwise the reference is overwritten
+                            // (vftuplet as any).numerator_glyphs = [...(vftuplet as any).numerator_glyphs_stored];
+                            // (vftuplet as any).numerator_glyphs_stored = undefined;
+                            (vftuplet as any).RenderTupletNumber = true;
+                        }
+                        vftuplet.setContext(ctx).draw();
                     }
                 }
             }
@@ -1138,14 +1150,15 @@ export class VexFlowMeasure extends GraphicalMeasure {
                       if (tuplet.tupletLabelNumberPlacement === PlacementEnum.Below) {
                           location = VF.Tuplet.LOCATION_BOTTOM;
                       }
-                      vftuplets.push(new VF.Tuplet( tupletStaveNotes,
-                                                          {
-                                                            bracketed: bracketed,
-                                                            location: location,
-                                                            notes_occupied: notesOccupied,
-                                                            num_notes: tuplet.TupletLabelNumber, //, location: -1, ratioed: true
-                                                            ratioed: this.rules.TupletsRatioed,
-                                                          }));
+                      const vftuplet: VF.Tuplet = new VF.Tuplet(tupletStaveNotes,
+                        {
+                          bracketed: bracketed,
+                          location: location,
+                          notes_occupied: notesOccupied,
+                          num_notes: tuplet.TupletLabelNumber, //, location: -1, ratioed: true
+                          ratioed: this.rules.TupletsRatioed,
+                        });
+                      vftuplets.push(vftuplet);
                     } else {
                         log.debug("Warning! Tuplet with no notes! Trying to ignore, but this is a serious problem.");
                     }

+ 2 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -742,6 +742,8 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         staffLine,
         startMeasure.parentSourceMeasure);
       graphicalContinuousDynamic.StartMeasure = startMeasure;
+      graphicalContinuousDynamic.IsSoftAccent = multiExpression.StartingContinuousDynamic.IsStartOfSoftAccent;
+      //graphicalContinuousDynamic.StartIsEnd = multiExpression.StartingContinuousDynamic.EndMultiExpression === multiExpression;
 
       if (!graphicalContinuousDynamic.IsVerbal && continuousDynamic.EndMultiExpression) {
         try {

+ 38 - 0
src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts

@@ -8,6 +8,9 @@ import {AccidentalEnum} from "../../../Common/DataObjects/Pitch";
 import { Articulation } from "../../VoiceData/Articulation";
 import { Note } from "../../VoiceData/Note";
 import { EngravingRules } from "../../Graphical/EngravingRules";
+import { MultiExpression } from "../../VoiceData/Expressions/MultiExpression";
+import { SourceMeasure } from "../../VoiceData/SourceMeasure";
+import { ContDynamicEnum, ContinuousDynamicExpression } from "../../VoiceData/Expressions/ContinuousExpressions";
 export class ArticulationReader {
   private rules: EngravingRules;
 
@@ -95,6 +98,41 @@ export class ArticulationReader {
                 newArticulation.articulationEnum = ArticulationEnum.marcatodown;
               }
             }
+            if (articulationEnum === ArticulationEnum.softaccent) {
+              const staffId: number = currentVoiceEntry.ParentSourceStaffEntry.ParentStaff.Id - 1;
+              if (placement === PlacementEnum.NotYetDefined) {
+                placement = PlacementEnum.Above;
+                if (staffId > 0) {
+                  placement = PlacementEnum.Below;
+                }
+                // TODO place according to whether the corresponding note is higher (-> above) or lower (-> below)
+                //   than the middle note line. Though this could be tricky at this stage of parsing.
+              }
+              const sourceMeasure: SourceMeasure = currentVoiceEntry.ParentSourceStaffEntry.VerticalContainerParent.ParentMeasure;
+              const multi: MultiExpression = new MultiExpression(sourceMeasure, currentVoiceEntry.Timestamp);
+              multi.StartingContinuousDynamic = new ContinuousDynamicExpression(
+                ContDynamicEnum.crescendo,
+                placement,
+                staffId,
+                sourceMeasure,
+                null,
+                -1
+              );
+              multi.StartingContinuousDynamic.IsStartOfSoftAccent = true;
+              multi.StartingContinuousDynamic.StartMultiExpression = multi;
+              multi.StartingContinuousDynamic.EndMultiExpression = multi;
+              multi.EndingContinuousDynamic = new ContinuousDynamicExpression(
+                ContDynamicEnum.diminuendo,
+                placement,
+                staffId,
+                sourceMeasure,
+                null,
+                -1
+              );
+              multi.EndingContinuousDynamic.StartMultiExpression = multi;
+              multi.EndingContinuousDynamic.EndMultiExpression = multi;
+              sourceMeasure.StaffLinkedExpressions[staffId].push(multi);
+            }
 
             // don't add the same articulation twice
             if (!currentVoiceEntry.hasArticulation(newArticulation)) {

+ 4 - 2
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -162,7 +162,7 @@ export class VoiceGenerator {
 
     try {
       this.currentNote = restNote
-        ? this.addRestNote(noteNode.element("rest"), noteDuration, noteTypeXml, normalNotes, printObject, isCueNote, noteheadColorXml)
+        ? this.addRestNote(noteNode.element("rest"), noteDuration, noteTypeXml, typeDuration, normalNotes, printObject, isCueNote, noteheadColorXml)
         : this.addSingleNote(noteNode, noteDuration, noteTypeXml, typeDuration, normalNotes, chord, octavePlusOne,
                              printObject, isCueNote, isGraceNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml);
       this.currentNote.DotsXml = dotsXml;
@@ -562,7 +562,7 @@ export class VoiceGenerator {
    * @param divisions
    * @returns {Note}
    */
-  private addRestNote(node: IXmlElement, noteDuration: Fraction, noteTypeXml: NoteType,
+  private addRestNote(node: IXmlElement, noteDuration: Fraction, noteTypeXml: NoteType, typeDuration: Fraction,
                       normalNotes: number, printObject: boolean, isCueNote: boolean, noteheadColorXml: string): Note {
     const restFraction: Fraction = Fraction.createFromFraction(noteDuration);
     const displayStepElement: IXmlElement = node.element("display-step");
@@ -577,6 +577,8 @@ export class VoiceGenerator {
     }
     const restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, pitch, this.currentMeasure, true);
     this.addNoteInfo(restNote, noteTypeXml, printObject, isCueNote, normalNotes, displayStep, displayOctave, noteheadColorXml, noteheadColorXml);
+    restNote.TypeLength = typeDuration; // needed for tuplet note type information
+    //  (e.g. quarter rest - but length different due to tuplet). see MusicSheetCalculator.calculateTupletNumbers()
     this.currentVoiceEntry.Notes.push(restNote);
     if (this.openBeams.length > 0) {
       this.openBeams.last().ExtendedNoteList.push(restNote);

+ 1 - 0
src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression.ts

@@ -35,6 +35,7 @@ export class ContinuousDynamicExpression extends AbstractExpression {
     private endVolume: number;
     private staffNumber: number;
     private label: string;
+    public IsStartOfSoftAccent: boolean;
     private activeInstantaneousDynamic: InstantaneousDynamicExpression;
 
     public setStartAndEndVolume(): void {

+ 1 - 0
src/MusicalScore/VoiceData/Tuplet.ts

@@ -15,6 +15,7 @@ export class Tuplet {
     private tupletLabelNumber: number;
     public PlacementFromXml: boolean = false;
     public tupletLabelNumberPlacement: PlacementEnum;
+    public RenderTupletNumber: boolean = true;
     /** Notes contained in the tuplet, per VoiceEntry (list of VoiceEntries, which has a list of notes). */
     private notes: Note[][] = []; // TODO should probably be VoiceEntry[], not Note[][].
     private fractions: Fraction[] = [];

+ 2 - 0
src/MusicalScore/VoiceData/VoiceEntry.ts

@@ -268,6 +268,7 @@ export class VoiceEntry {
         switch (articulation) {
             case ArticulationEnum.accent:
             case ArticulationEnum.strongaccent:
+            case ArticulationEnum.softaccent:
             case ArticulationEnum.invertedstrongaccent:
             case ArticulationEnum.staccato:
             case ArticulationEnum.staccatissimo:
@@ -332,6 +333,7 @@ export class VoiceEntry {
 export enum ArticulationEnum {
     accent,
     strongaccent,
+    softaccent,
     marcatoup,
     marcatodown,
     invertedstrongaccent,

+ 1 - 1
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -35,7 +35,7 @@ import { DynamicsCalculator } from "../MusicalScore/ScoreIO/MusicSymbolModules/D
  * After the constructor, use load() and render() to load and render a MusicXML file.
  */
 export class OpenSheetMusicDisplay {
-    private version: string = "1.5.1-audio-extended"; // getter: this.Version
+    private version: string = "1.5.2-audio-extended"; // getter: this.Version
     // at release, bump version and change to -release, afterwards to -dev again
 
     /**

+ 7 - 0
src/Util/CollectionUtil.ts

@@ -89,8 +89,14 @@ export class CollectionUtil {
                                   startIndex: number = 0,
                                   endIndex: number = array.length - 1): number {
         let mid: number = 1;
+        let lastMidChecked: number = -1;
         while (startIndex <= endIndex) {
             mid = Math.floor((startIndex + endIndex) / 2);
+            if (mid === lastMidChecked) {
+                break;
+                // this fixes a rare infinite loop when no matching element can be found,
+                //   e.g. with very small fraction difference in AbsoluteTimestamp like 511/1024 instead of 1/2 (#1201)
+            }
             const c: number = cmp(array[mid], element);
             if (c === 0) {
                 return mid;
@@ -101,6 +107,7 @@ export class CollectionUtil {
             if (0 < c) {
                 endIndex = mid;
             }
+            lastMidChecked = mid;
         }
 
         return -mid;

+ 5 - 2
src/VexFlowPatch/readme.txt

@@ -68,11 +68,14 @@ able to add extra attributes (like svg node id) to a stroke (e.g. stem)
 tabnote.js (merged Vexflow 3.x):
 Add a context group for each tabnote, so that it can be found in the SVG DOM ("vf-tabnote")
 
+timesignature.js (fixed vexflow 4):
+open group to get SVG group+class for key signature
+
 tremolo.js (fixed vexflow 4):
 Add extra_stroke_scale, y_spacing_scale
 
-timesignature.js (fixed vexflow 4):
-open group to get SVG group+class for key signature
+tuplet.js (vexflow 4: need to check if this option available):
+Add option tuplet.RenderTupletNumber
 
 Currently, we are using Vexflow 1.2.93, because of some formatter advantages
 compared to Vexflow 3.x versions, see this issue:

+ 368 - 0
src/VexFlowPatch/src/tuplet.js

@@ -0,0 +1,368 @@
+// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
+
+/**
+ * ## Description
+ *
+ * Create a new tuplet from the specified notes. The notes must
+ * be part of the same voice. If they are of different rhythmic
+ * values, then options.num_notes must be set.
+ *
+ * @constructor
+ * @param {Array.<Vex.Flow.StaveNote>} A set of notes: staveNotes,
+ *   notes, etc... any class that inherits stemmableNote at some
+ *   point in its prototype chain.
+ * @param options: object {
+ *
+ *   num_notes: fit this many notes into...
+ *   notes_occupied: ...the space of this many notes
+ *
+ *       Together, these two properties make up the tuplet ratio
+ *     in the form of num_notes : notes_occupied.
+ *       num_notes defaults to the number of notes passed in, so
+ *     it is important that if you omit this property, all of
+ *     the notes passed should be of the same note value.
+ *       notes_occupied defaults to 2 -- so you should almost
+ *     certainly pass this parameter for anything other than
+ *     a basic triplet.
+ *
+ *   location:
+ *     default 1, which is above the notes: ┌─── 3 ───┐
+ *      -1 is below the notes └─── 3 ───┘
+ *
+ *   bracketed: boolean, draw a bracket around the tuplet number
+ *     when true: ┌─── 3 ───┐   when false: 3
+ *     defaults to true if notes are not beamed, false otherwise
+ *
+ *   ratioed: boolean
+ *     when true: ┌─── 7:8 ───┐, when false: ┌─── 7 ───┐
+ *     defaults to true if the difference between num_notes and
+ *     notes_occupied is greater than 1.
+ *
+ *   y_offset: int, default 0
+ *     manually offset a tuplet, for instance to avoid collisions
+ *     with articulations, etc...
+ * }
+ */
+
+ import { Vex } from './vex';
+ import { Element } from './element';
+ import { Formatter } from './formatter';
+ import { Glyph } from './glyph';
+ import { Stem } from './stem';
+ 
+ export class Tuplet extends Element {
+   static get LOCATION_TOP() {
+     return 1;
+   }
+   static get LOCATION_BOTTOM() {
+     return -1;
+   }
+   static get NESTING_OFFSET() {
+     return 15;
+   }
+ 
+   constructor(notes, options) {
+     super();
+     this.setAttribute('type', 'Tuplet');
+     if (!notes || !notes.length) {
+       throw new Vex.RuntimeError('BadArguments', 'No notes provided for tuplet.');
+     }
+ 
+     this.options = Vex.Merge({}, options);
+     this.notes = notes;
+     this.num_notes = 'num_notes' in this.options ?
+       this.options.num_notes : notes.length;
+ 
+     // We accept beats_occupied, but warn that it's deprecated:
+     // the preferred property name is now notes_occupied.
+     if (this.options.beats_occupied) {
+       this.beatsOccupiedDeprecationWarning();
+     }
+     this.notes_occupied = this.options.notes_occupied ||
+       this.options.beats_occupied ||
+       2;
+     if ('bracketed' in this.options) {
+       this.bracketed = this.options.bracketed;
+     } else {
+       this.bracketed =
+         notes.some(note => note.beam === null);
+     }
+ 
+     this.ratioed = 'ratioed' in this.options ?
+       this.options.ratioed :
+       (Math.abs(this.notes_occupied - this.num_notes) > 1);
+     this.point = 28;
+     this.y_pos = 16;
+     this.x_pos = 100;
+     this.width = 200;
+     this.location = this.options.location || Tuplet.LOCATION_TOP;
+ 
+     Formatter.AlignRestsToNotes(notes, true, true);
+     this.resolveGlyphs();
+     this.attach();
+   }
+ 
+   attach() {
+     for (let i = 0; i < this.notes.length; i++) {
+       const note = this.notes[i];
+       note.setTuplet(this);
+     }
+   }
+ 
+   detach() {
+     for (let i = 0; i < this.notes.length; i++) {
+       const note = this.notes[i];
+       note.resetTuplet(this);
+     }
+   }
+ 
+   /**
+    * Set whether or not the bracket is drawn.
+    */
+   setBracketed(bracketed) {
+     this.bracketed = !!bracketed;
+     return this;
+   }
+ 
+   /**
+    * Set whether or not the ratio is shown.
+    */
+   setRatioed(ratioed) {
+     this.ratioed = !!ratioed;
+     return this;
+   }
+ 
+   /**
+    * Set the tuplet to be displayed either on the top or bottom of the stave
+    */
+   setTupletLocation(location) {
+     if (!location) {
+       location = Tuplet.LOCATION_TOP;
+     } else if (location !== Tuplet.LOCATION_TOP && location !== Tuplet.LOCATION_BOTTOM) {
+       throw new Vex.RERR('BadArgument', 'Invalid tuplet location: ' + location);
+     }
+ 
+     this.location = location;
+     return this;
+   }
+ 
+   getNotes() {
+     return this.notes;
+   }
+ 
+   getNoteCount() {
+     return this.num_notes;
+   }
+ 
+   beatsOccupiedDeprecationWarning() {
+     const msg = [
+       'beats_occupied has been deprecated as an ',
+       'option for tuplets. Please use notes_occupied ',
+       'instead. Calls to getBeatsOccupied and ',
+       'setBeatsOccupied should now be routed to ',
+       'getNotesOccupied and setNotesOccupied instead',
+     ].join('');
+ 
+     if (console && console.warn) { // eslint-disable-line no-console
+       console.warn(msg); // eslint-disable-line no-console
+     } else if (console) {
+       console.log(msg); // eslint-disable-line no-console
+     }
+   }
+ 
+   getBeatsOccupied() {
+     this.beatsOccupiedDeprecationWarning();
+     return this.getNotesOccupied();
+   }
+ 
+   setBeatsOccupied(beats) {
+     this.beatsOccupiedDeprecationWarning();
+     return this.setNotesOccupied(beats);
+   }
+ 
+   getNotesOccupied() {
+     return this.notes_occupied;
+   }
+ 
+   setNotesOccupied(notes) {
+     this.detach();
+     this.notes_occupied = notes;
+     this.resolveGlyphs();
+     this.attach();
+   }
+ 
+   resolveGlyphs() {
+     this.numerator_glyphs = [];
+     let n = this.num_notes;
+     while (n >= 1) {
+       this.numerator_glyphs.unshift(new Glyph('v' + (n % 10), this.point));
+       n = parseInt(n / 10, 10);
+     }
+ 
+     this.denom_glyphs = [];
+     n = this.notes_occupied;
+     while (n >= 1) {
+       this.denom_glyphs.unshift(new Glyph('v' + (n % 10), this.point));
+       n = parseInt(n / 10, 10);
+     }
+   }
+ 
+   // determine how many tuplets are nested within this tuplet
+   // on the same side (above/below), to calculate a y
+   // offset for this tuplet:
+   getNestedTupletCount() {
+     const location = this.location;
+     const first_note = this.notes[0];
+     let maxTupletCount = countTuplets(first_note, location);
+     let minTupletCount = countTuplets(first_note, location);
+ 
+     // Count the tuplets that are on the same side (above/below)
+     // as this tuplet:
+     function countTuplets(note, location) {
+       return note.tupletStack.filter(tuplet => tuplet.location === location).length;
+     }
+ 
+     this.notes.forEach(note => {
+       const tupletCount = countTuplets(note, location);
+       maxTupletCount = tupletCount > maxTupletCount ? tupletCount : maxTupletCount;
+       minTupletCount = tupletCount < minTupletCount ? tupletCount : minTupletCount;
+     });
+ 
+     return maxTupletCount - minTupletCount;
+   }
+ 
+   // determine the y position of the tuplet:
+   getYPosition() {
+     // offset the tuplet for any nested tuplets between
+     // it and the notes:
+     const nested_tuplet_y_offset =
+       this.getNestedTupletCount() *
+       Tuplet.NESTING_OFFSET *
+       -this.location;
+ 
+     // offset the tuplet for any manual y_offset:
+     const y_offset = this.options.y_offset || 0;
+ 
+     // now iterate through the notes and find our highest
+     // or lowest locations, to form a base y_pos
+     const first_note = this.notes[0];
+     let y_pos;
+     if (this.location === Tuplet.LOCATION_TOP) {
+       y_pos = first_note.getStave().getYForLine(0) - 15;
+       // y_pos = first_note.getStemExtents().topY - 10;
+ 
+       for (let i = 0; i < this.notes.length; ++i) {
+         const top_y = this.notes[i].getStemDirection() === Stem.UP
+           ? this.notes[i].getStemExtents().topY - 10
+           : this.notes[i].getStemExtents().baseY - 20;
+ 
+         if (top_y < y_pos) {
+           y_pos = top_y;
+         }
+       }
+     } else {
+       y_pos = first_note.getStave().getYForLine(4) + 20;
+ 
+       for (let i = 0; i < this.notes.length; ++i) {
+         const bottom_y = this.notes[i].getStemDirection() === Stem.UP
+           ? this.notes[i].getStemExtents().baseY + 20
+           : this.notes[i].getStemExtents().topY + 10;
+         if (bottom_y > y_pos) {
+           y_pos = bottom_y;
+         }
+       }
+     }
+ 
+     return y_pos + nested_tuplet_y_offset + y_offset;
+   }
+ 
+   draw() {
+     this.checkContext();
+     this.setRendered();
+ 
+     // determine x value of left bound of tuplet
+     const first_note = this.notes[0];
+     const last_note = this.notes[this.notes.length - 1];
+ 
+     if (!this.bracketed) {
+       this.x_pos = first_note.getStemX();
+       this.width = last_note.getStemX() - this.x_pos;
+     } else {
+       this.x_pos = first_note.getTieLeftX() - 5;
+       this.width = last_note.getTieRightX() - this.x_pos + 5;
+     }
+ 
+     // determine y value for tuplet
+     this.y_pos = this.getYPosition();
+ 
+     const addGlyphWidth = (width, glyph) => width + glyph.getMetrics().width;
+ 
+     // calculate total width of tuplet notation
+     let width = this.numerator_glyphs.reduce(addGlyphWidth, 0);
+     if (this.ratioed) {
+       width = this.denom_glyphs.reduce(addGlyphWidth, width);
+       width += this.point * 0.32;
+     }
+ 
+     const notation_center_x = this.x_pos + (this.width / 2);
+     const notation_start_x = notation_center_x - (width / 2);
+ 
+     // draw bracket if the tuplet is not beamed
+     if (this.bracketed) {
+       const line_width = this.width / 2 - width / 2 - 5;
+ 
+       // only draw the bracket if it has positive length
+       if (line_width > 0) {
+         this.context.fillRect(this.x_pos, this.y_pos, line_width, 1);
+         this.context.fillRect(
+           this.x_pos + this.width / 2 + width / 2 + 5,
+           this.y_pos,
+           line_width,
+           1
+         );
+         this.context.fillRect(
+           this.x_pos,
+           this.y_pos + (this.location === Tuplet.LOCATION_BOTTOM),
+           1,
+           this.location * 10
+         );
+         this.context.fillRect(
+           this.x_pos + this.width,
+           this.y_pos + (this.location === Tuplet.LOCATION_BOTTOM),
+           1,
+           this.location * 10
+         );
+       }
+     }
+ 
+     // VexFlowPatch: add option to not render tuplet numbers
+     if (this.RenderTupletNumber !== false) {
+         // draw numerator glyphs
+         let x_offset = 0;
+         this.numerator_glyphs.forEach(glyph => {
+           glyph.render(this.context, notation_start_x + x_offset, this.y_pos + (this.point / 3) - 2);
+           x_offset += glyph.getMetrics().width;
+         });
+     }
+ 
+     // display colon and denominator if the ratio is to be shown
+     if (this.ratioed) {
+       const colon_x = notation_start_x + x_offset + this.point * 0.16;
+       const colon_radius = this.point * 0.06;
+       this.context.beginPath();
+       this.context.arc(colon_x, this.y_pos - this.point * 0.08, colon_radius, 0, Math.PI * 2, true);
+       this.context.closePath();
+       this.context.fill();
+       this.context.beginPath();
+       this.context.arc(colon_x, this.y_pos + this.point * 0.12, colon_radius, 0, Math.PI * 2, true);
+       this.context.closePath();
+       this.context.fill();
+       x_offset += this.point * 0.32;
+       this.denom_glyphs.forEach(glyph => {
+         glyph.render(this.context, notation_start_x + x_offset, this.y_pos + (this.point / 3) - 2);
+         x_offset += glyph.getMetrics().width;
+       });
+     }
+   }
+ }
+ 

+ 111 - 0
test/data/test_soft-accent_cresc_decresc_single_note.musicxml

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.1">
+  <work>
+    <work-title>Title</work-title>
+    </work>
+  <identification>
+    <creator type="composer">Composer</creator>
+    <encoding>
+      <software>MuseScore 3.6.2</software>
+      <encoding-date>2022-08-23</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>6.99911</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1596.77</page-height>
+      <page-width>1233.87</page-width>
+      <page-margins type="even">
+        <left-margin>85.7252</left-margin>
+        <right-margin>85.7252</right-margin>
+        <top-margin>85.7252</top-margin>
+        <bottom-margin>85.7252</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>85.7252</left-margin>
+        <right-margin>85.7252</right-margin>
+        <top-margin>85.7252</top-margin>
+        <bottom-margin>85.7252</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="Edwin" font-size="10"/>
+    <lyric-font font-family="Edwin" font-size="10"/>
+    </defaults>
+  <credit page="1">
+    <credit-type>title</credit-type>
+    <credit-words default-x="616.935" default-y="1511.05" justify="center" valign="top" font-size="22">Title</credit-words>
+    </credit>
+  <credit page="1">
+    <credit-type>composer</credit-type>
+    <credit-words default-x="1148.15" default-y="1411.05" justify="right" valign="bottom">Composer</credit-words>
+    </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Piano</part-name>
+      <part-abbreviation>Pno.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Piano</instrument-name>
+        </score-instrument>
+      <midi-device id="P1-I1" port="1"></midi-device>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="304.16">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>50.00</left-margin>
+            <right-margin>674.54</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        </attributes>
+      <note default-x="80.72" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>whole</type>
+        <notations>
+          <articulations>
+            <soft-accent/>
+            </articulations>
+          </notations>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 536 - 0
test/data/test_tuplet_consecutive_3and5_tuplets.musicxml

@@ -0,0 +1,536 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.1">
+  <work>
+    <work-title>test_tuplet_consecutive_3and5_tuplets</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.6.2</software>
+      <encoding-date>2022-08-11</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1697.14</page-height>
+      <page-width>1200</page-width>
+      <page-margins type="even">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="Edwin" font-size="10"/>
+    <lyric-font font-family="Edwin" font-size="10"/>
+    </defaults>
+  <credit page="1">
+    <credit-type>title</credit-type>
+    <credit-words default-x="600" default-y="1611.43" justify="center" valign="top" font-size="22">tuplet_consecutive_test_simple</credit-words>
+    </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Piano</part-name>
+      <part-abbreviation>Pno.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Piano</instrument-name>
+        </score-instrument>
+      <midi-device id="P1-I1" port="1"></midi-device>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="426.54">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>65.90</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        <staff-layout number="2">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>15</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>3</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <staves>2</staves>
+        <clef number="1">
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        <clef number="2">
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        </attributes>
+      <note default-x="83.49" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>45</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>45</duration>
+        </backup>
+      <note default-x="83.49" default-y="-130.00">
+        <pitch>
+          <step>C</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>5</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="126.41" default-y="-135.00">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+          </pitch>
+        <duration>5</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="169.33" default-y="-130.00">
+        <pitch>
+          <step>C</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>5</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="212.25" default-y="-125.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>5</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="255.17" default-y="-120.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>5</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="298.09" default-y="-125.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>5</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="341.00" default-y="-120.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>15</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="2" width="536.13">
+      <note default-x="13.00" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>45</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>45</duration>
+        </backup>
+      <note default-x="13.00" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <beam number="2">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="47.17" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="81.33" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="115.50" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="149.66" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <beam number="2">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="183.83" default-y="-110.00">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <accidental>sharp</accidental>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <beam number="2">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="217.99" default-y="-110.00">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="252.16" default-y="-110.00">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="286.32" default-y="-110.00">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="320.49" default-y="-110.00">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <beam number="2">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="354.65" default-y="-105.00">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <accidental>sharp</accidental>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <beam number="2">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="388.82" default-y="-105.00">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="422.98" default-y="-105.00">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="457.15" default-y="-105.00">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <beam number="2">continue</beam>
+        </note>
+      <note default-x="491.31" default-y="-105.00">
+        <pitch>
+          <step>A</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <beam number="2">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 300 - 0
test/data/test_tuplet_consecutive_simple.musicxml

@@ -0,0 +1,300 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.1">
+  <work>
+    <work-title>test_tuplet_consecutive_simple</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.6.2</software>
+      <encoding-date>2022-08-11</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1697.14</page-height>
+      <page-width>1200</page-width>
+      <page-margins type="even">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="Edwin" font-size="10"/>
+    <lyric-font font-family="Edwin" font-size="10"/>
+    </defaults>
+  <credit page="1">
+    <credit-type>title</credit-type>
+    <credit-words default-x="600" default-y="1611.43" justify="center" valign="top" font-size="22">tuplet_consecutive_test_simple</credit-words>
+    </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Piano</part-name>
+      <part-abbreviation>Pno.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Piano</instrument-name>
+        </score-instrument>
+      <midi-device id="P1-I1" port="1"></midi-device>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="575.97">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>65.90</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        <staff-layout number="2">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>3</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>3</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <staves>2</staves>
+        <clef number="1">
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        <clef number="2">
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        </attributes>
+      <note default-x="83.49" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>9</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>9</duration>
+        </backup>
+      <note default-x="83.49" default-y="-130.00">
+        <pitch>
+          <step>C</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="138.01" default-y="-135.00">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="192.53" default-y="-130.00">
+        <pitch>
+          <step>C</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="247.05" default-y="-125.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="301.57" default-y="-120.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="356.09" default-y="-125.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="410.61" default-y="-120.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="465.13" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="519.65" default-y="-120.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      </measure>
+    <measure number="2" width="386.70">
+      <note default-x="13.00" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>9</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>9</duration>
+        </backup>
+      <note>
+        <rest measure="yes"/>
+        <duration>9</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 319 - 0
test/data/test_tuplet_consecutive_simple_alwaysdisable.musicxml

@@ -0,0 +1,319 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.1">
+  <work>
+    <work-title>test_tuplet_consecutive_simple_alwaysdisable</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.6.2</software>
+      <encoding-date>2022-08-11</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1697.14</page-height>
+      <page-width>1200</page-width>
+      <page-margins type="even">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="Edwin" font-size="10"/>
+    <lyric-font font-family="Edwin" font-size="10"/>
+    </defaults>
+  <credit page="1">
+    <credit-type>title</credit-type>
+    <credit-words default-x="600" default-y="1611.43" justify="center" valign="top" font-size="22">tuplet_consecutive_test_simple</credit-words>
+    </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Piano</part-name>
+      <part-abbreviation>Pno.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Piano</instrument-name>
+        </score-instrument>
+      <midi-device id="P1-I1" port="1"></midi-device>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="529.40">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>65.90</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        <staff-layout number="2">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>3</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>3</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <staves>2</staves>
+        <clef number="1">
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        <clef number="2">
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        </attributes>
+      <note default-x="83.49" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>9</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>9</duration>
+        </backup>
+      <note default-x="83.49" default-y="-130.00">
+        <pitch>
+          <step>C</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="139.34" default-y="-135.00">
+        <pitch>
+          <step>B</step>
+          <octave>2</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="195.20" default-y="-130.00">
+        <pitch>
+          <step>C</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="251.06" default-y="-125.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="306.91" default-y="-120.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="362.77" default-y="-125.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="418.63" default-y="-120.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="2" width="433.27">
+      <note default-x="13.00" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>9</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>9</duration>
+        </backup>
+      <note default-x="13.00" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="72.35" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="131.70" default-y="-115.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note>
+        <rest/>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <staff>2</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>3</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <staff>2</staff>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>