Browse Source

merge osmd-public 1.4.5: fix wedges start/stop, octaveshifts, types location, etc.

see osmd-public changelog for 1.4.5 and 1.4.4
sschmidTU 3 years ago
parent
commit
0c541d702e

+ 2 - 2
package.json

@@ -1,9 +1,9 @@
 {
   "name": "osmd-extended",
-  "version": "1.4.3",
+  "version": "1.4.5",
   "description": "Private / sponsor exclusive OSMD mirror/audio player.",
   "main": "build/opensheetmusicdisplay.min.js",
-  "types": "build/dist/src/OpenSheetMusicDisplay/index.d.ts",
+  "types": "build/dist/src/index.d.ts",
   "scripts": {
     "docs": "typedoc --out ./build/docs --name OpenSheetMusicDisplay ./src",
     "eslint": "eslint -c .eslintrc.js --ext .ts .",

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

@@ -226,6 +226,11 @@ export class EngravingRules {
     public MinSkyBottomDistBetweenStaves: number;
     public MinimumCrossedBeamDifferenceMargin: number;
 
+    /** Maximum width of sheet / HTMLElement containing the score. Canvas is limited to 32767 in current browsers, though SVG isn't.
+     *  Setting this to > 32767 will break the canvas backend (no problem if you only use SVG).
+     */
+    public SheetMaximumWidth: number;
+
     public VoiceSpacingMultiplierVexflow: number;
     public VoiceSpacingAddendVexflow: number;
     public PickupMeasureWidthMultiplier: number;
@@ -293,6 +298,7 @@ export class EngravingRules {
     public RenderMultipleRestMeasures: boolean;
     public AutoGenerateMutipleRestMeasuresFromRestMeasures: boolean;
     public RenderRehearsalMarks: boolean;
+    public RenderClefsAtBeginningOfStaffline: boolean;
     public RenderKeySignatures: boolean;
     public RenderTimeSignatures: boolean;
     public DynamicExpressionMaxDistance: number;
@@ -580,6 +586,9 @@ export class EngravingRules {
         // Line Widths
         this.MinimumCrossedBeamDifferenceMargin = 0.0001;
 
+        // Canvas is limited to 32767 in most browsers, though SVG isn't.
+        this.SheetMaximumWidth = 32767;
+
         // xSpacing Variables
         this.VoiceSpacingMultiplierVexflow = 0.85;
         this.VoiceSpacingAddendVexflow = 3.0;
@@ -633,6 +642,7 @@ export class EngravingRules {
         this.RenderMultipleRestMeasures = true;
         this.AutoGenerateMutipleRestMeasuresFromRestMeasures = true;
         this.RenderRehearsalMarks = true;
+        this.RenderClefsAtBeginningOfStaffline = true;
         this.RenderKeySignatures = true;
         this.RenderTimeSignatures = true;
         this.ArticulationPlacementFromXML = true;

+ 3 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -166,6 +166,9 @@ export class VexFlowMeasure extends GraphicalMeasure {
      * @param clef
      */
     public addClefAtBegin(clef: ClefInstruction): void {
+        if (!this.rules.RenderClefsAtBeginningOfStaffline) {
+            return;
+        }
         this.octaveOffset = clef.OctaveOffset;
         if (clef.ClefType === ClefEnum.TAB) {
             this.stave.addClef("tab", undefined, undefined, undefined);

+ 6 - 6
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -708,14 +708,13 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
       return;
     }
 
-    const startPosInStaffline: PointF2D = this.getRelativePositionInStaffLineFromTimestamp(
+    // start position in staffline:
+    const dynamicStartPosition: PointF2D = this.getRelativePositionInStaffLineFromTimestamp(
       absoluteTimestamp,
       staffIndex,
       staffLine,
       staffLine?.isPartOfMultiStaffInstrument());
-
-    const dynamicStartPosition: PointF2D = startPosInStaffline;
-    if (startPosInStaffline.x <= 0) {
+    if (dynamicStartPosition.x <= 0) {
       dynamicStartPosition.x = startMeasure.beginInstructionsWidth + this.rules.RhythmRightMargin;
     }
 
@@ -724,6 +723,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         multiExpression.InstantaneousDynamic,
         staffLine,
         startMeasure);
+      // compare with multiExpression.InstantaneousDynamic.InMeasureTimestamp or add a relative timestamp? if we ever need a separate timestamp
       this.calculateGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic, dynamicStartPosition, absoluteTimestamp);
       this.dynamicExpressionMap.set(absoluteTimestamp.RealValue, graphicalInstantaneousDynamic.PositionAndShape);
     }
@@ -747,7 +747,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
       } else if (graphicalContinuousDynamic.IsVerbal) {
         this.calculateGraphicalVerbalContinuousDynamic(graphicalContinuousDynamic, dynamicStartPosition);
       } else {
-        log.warn("This continuous dynamic is not covered");
+        log.warn("This continuous dynamic is not covered. measure" + multiExpression.SourceMeasureParent.MeasureNumber);
       }
     }
   }
@@ -1072,7 +1072,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           const firstNote: GraphicalStaffEntry = nextPedalFirstMeasure.staffEntries[0];
           if(!nextPedal.setStartNote(firstNote)){
             return;
-        }
+          }
           nextPedal.setEndNote(endStaffEntry);
           graphicalPedal.setEndMeasure(endMeasure);
           endStaffLine.Pedals.push(nextPedal);

+ 1 - 0
src/MusicalScore/Instrument.ts

@@ -16,6 +16,7 @@ export class Instrument extends InstrumentalGroup {
     }
 
     /** Transposition halftones for this instrument only.
+     *  This is additive to osmd.Sheet.Transpose (MusicSheet).
      * You need to call osmd.updateGraphic() before the next render() (assuming this is set after load()).
      */
     public Transpose: number = 0;

+ 1 - 1
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -618,7 +618,7 @@ export class InstrumentReader {
         for (let i: number = 0; i < this.expressionReaders.length; i++) {
          const reader: ExpressionReader = this.expressionReaders[i];
          if (reader) {
-           reader.checkForOpenExpressions(this.currentMeasure, currentFraction);
+           reader.closeOpenExpressions(this.currentMeasure, currentFraction);
           }
         }
       }

+ 135 - 73
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -31,7 +31,7 @@ export class ExpressionReader {
     private globalStaffIndex: number;
     private directionTimestamp: Fraction;
     private currentMultiTempoExpression: MultiTempoExpression;
-    private openContinuousDynamicExpression: ContinuousDynamicExpression;
+    private openContinuousDynamicExpressions: ContinuousDynamicExpression[] = [];
     private openContinuousTempoExpression: ContinuousTempoExpression;
     private activeInstantaneousDynamic: InstantaneousDynamicExpression;
     private openOctaveShift: OctaveShift;
@@ -249,10 +249,13 @@ export class ExpressionReader {
             return;
         }
     }
-    public checkForOpenExpressions(sourceMeasure: SourceMeasure, timestamp: Fraction): void {
-        if (this.openContinuousDynamicExpression) {
-            this.createNewMultiExpressionIfNeeded(sourceMeasure, timestamp);
-            this.closeOpenContinuousDynamic();
+    /** Usually called at end of last measure. */
+    public closeOpenExpressions(sourceMeasure: SourceMeasure, timestamp: Fraction): void {
+        for (const openCont of this.openContinuousDynamicExpressions) {
+            // add to current stafflinked expression // refactor into closeOpenContinuousDynamic?
+            this.createNewMultiExpressionIfNeeded(sourceMeasure, openCont.NumberXml, timestamp);
+
+            this.closeOpenContinuousDynamic(openCont, sourceMeasure, timestamp);
         }
         if (this.openContinuousTempoExpression) {
             this.closeOpenContinuousTempo(Fraction.plus(sourceMeasure.AbsoluteTimestamp, timestamp));
@@ -275,8 +278,13 @@ export class ExpressionReader {
         const directionTypeNode: IXmlElement = directionNode.element("direction-type");
         if (directionTypeNode) {
             const octaveShiftNode: IXmlElement = directionTypeNode.element("octave-shift");
+            const placement: PlacementEnum = this.readPlacement(directionNode);
+            // if (placement === PlacementEnum.NotYetDefined && this.staffNumber === 1) {
+            //     placement = PlacementEnum.Above;
+            // }
             if (octaveShiftNode !== undefined && octaveShiftNode.hasAttributes) {
                 try {
+                    const numberXml: number = this.readNumber(octaveShiftNode);
                     if (octaveShiftNode.attribute("size")) {
                         const size: number = parseInt(octaveShiftNode.attribute("size").value, 10);
                         let octave: number = 0;
@@ -285,23 +293,34 @@ export class ExpressionReader {
                         } else if (size === 15) {
                             octave = 2;
                              }
-                        if (octaveShiftNode.attribute("type")) {
-                            const type: string = octaveShiftNode.attribute("type").value;
-                            if (type === "up" || type === "down") {
-                                const octaveShift: OctaveShift = new OctaveShift(type, octave);
-                                octaveShift.StaffNumber = octaveStaffNumber;
-                                this.createNewMultiExpressionIfNeeded(currentMeasure);
-                                this.getMultiExpression.OctaveShiftStart = octaveShift;
-                                octaveShift.ParentStartMultiExpression = this.getMultiExpression;
-                                this.openOctaveShift = octaveShift;
-                            } else if (type === "stop") {
-                                if (this.openOctaveShift) {
-                                    this.createNewMultiExpressionIfNeeded(currentMeasure, endTimestamp);
-                                    this.getMultiExpression.OctaveShiftEnd = this.openOctaveShift;
-                                    this.openOctaveShift.ParentEndMultiExpression = this.getMultiExpression;
-                                    this.openOctaveShift = undefined;
-                                }
+                        let type: string = octaveShiftNode.attribute("type")?.value;
+                        if (!type) {
+                            if (placement === PlacementEnum.Above) {
+                                type = "down";
+                            } else if (placement === PlacementEnum.Below) {
+                                type = "up";
+                            }
+                        }
+                        if (type === "up" || type === "down") { // unfortunately not always given in MusicXML (e.g. Musescore 3.6.2) even though required
+                            const octaveShift: OctaveShift = new OctaveShift(type, octave);
+                            octaveShift.StaffNumber = octaveStaffNumber;
+                            this.getMultiExpression = this.createNewMultiExpressionIfNeeded(
+                                currentMeasure, numberXml);
+                            this.getMultiExpression.OctaveShiftStart = octaveShift;
+                            octaveShift.ParentStartMultiExpression = this.getMultiExpression;
+                            this.openOctaveShift = octaveShift;
+                        } else if (type === "stop") {
+                            if (this.openOctaveShift) {
+                                this.getMultiExpression = this.createNewMultiExpressionIfNeeded(
+                                    currentMeasure, this.openOctaveShift.numberXml, endTimestamp);
+                                const octaveShiftStartExpression: MultiExpression = this.getMultiExpression;
+                                octaveShiftStartExpression.OctaveShiftEnd = this.openOctaveShift;
+                                this.openOctaveShift.ParentEndMultiExpression = this.getMultiExpression;
+                                this.openOctaveShift = undefined;
                             }
+                        } // TODO handle type === "continue"?
+                        else if (!type) {
+                            log.debug("octave-shift missing type in xml");
                         }
                     }
                 } catch (ex) {
@@ -337,7 +356,7 @@ export class ExpressionReader {
                         case "start":
                             //ignore duplicate tags
                             if (!this.openPedal || !this.openPedal.ParentStartMultiExpression.AbsoluteTimestamp.Equals(endTimestamp)) {
-                                this.createNewMultiExpressionIfNeeded(currentMeasure);
+                                this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
                             }
                             this.openPedal = new Pedal(line, sign);
                             this.getMultiExpression.PedalStart = this.openPedal;
@@ -345,7 +364,7 @@ export class ExpressionReader {
                         break;
                         case "stop":
                             if (this.openPedal) {
-                                this.createNewMultiExpressionIfNeeded(currentMeasure);
+                                this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
                                 this.getMultiExpression.PedalEnd = this.openPedal;
                                 this.openPedal.ParentEndMultiExpression = this.getMultiExpression;
                                 this.openPedal = undefined;
@@ -355,11 +374,11 @@ export class ExpressionReader {
                             //Ignore non-line pedals
                             if (this.openPedal && this.openPedal.IsLine) {
                                 this.openPedal.ChangeEnd = true;
-                                this.createNewMultiExpressionIfNeeded(currentMeasure);
+                                this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
                                 this.getMultiExpression.PedalEnd = this.openPedal;
                                 this.openPedal.ParentEndMultiExpression = this.getMultiExpression;
 
-                                this.createNewMultiExpressionIfNeeded(currentMeasure);
+                                this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
                                 this.openPedal = new Pedal(line, sign);
                                 this.openPedal.ChangeBegin = true;
                                 this.getMultiExpression.PedalStart = this.openPedal;
@@ -384,14 +403,14 @@ export class ExpressionReader {
             try {
                 switch (wavyLineNode.attribute("type").value) {
                     case "start":
-                        this.createNewMultiExpressionIfNeeded(currentMeasure);
+                        this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
                         this.openWavyLine = new WavyLine(this.placement);
                         this.getMultiExpression.WavyLineStart = this.openWavyLine;
                         this.openWavyLine.ParentStartMultiExpression = this.getMultiExpression;
                     break;
                     case "stop":
                         if (this.openWavyLine) {
-                            this.createNewMultiExpressionIfNeeded(currentMeasure, currentTimestamp);
+                            this.createNewMultiExpressionIfNeeded(currentMeasure, -1, currentTimestamp);
                             this.getMultiExpression.WavyLineEnd = this.openWavyLine;
                             this.openWavyLine.ParentEndMultiExpression = this.getMultiExpression;
                             this.openWavyLine = undefined;
@@ -416,6 +435,16 @@ export class ExpressionReader {
         this.soundDynamic = 0;
         this.offsetDivisions = 0;
     }
+    private readPlacement(node: IXmlElement): PlacementEnum {
+        const value: string = node.attribute("placement")?.value;
+        if (value === "above") {
+            return PlacementEnum.Above;
+        } else if (value === "below") {
+            return PlacementEnum.Below;
+        } else {
+            return PlacementEnum.NotYetDefined;
+        }
+    }
     private readExpressionPlacement(defAttr: IXmlAttribute, catchLogMessage: string): void {
         try {
             const y: number = parseInt(defAttr.value, 10);
@@ -437,6 +466,7 @@ export class ExpressionReader {
             if (dynamicsNode.hasAttributes && dynamicsNode.attribute("default-x")) {
                 this.directionTimestamp = Fraction.createFromFraction(inSourceMeasureCurrentFraction);
             }
+            const numberXml: number = this.readNumber(dynamicsNode); // probably never given, just to comply with createExpressionIfNeeded()
             let expressionText: string = dynamicsNode.elements()[0].name;
             if (expressionText === "other-dynamics") {
                 expressionText = dynamicsNode.elements()[0].value;
@@ -457,11 +487,10 @@ export class ExpressionReader {
                 // if (!this.activeInstantaneousDynamic ||
                 //     (this.activeInstantaneousDynamic && this.activeInstantaneousDynamic.DynEnum !== dynamicEnum)) {
                 if (!fromNotation) {
-                    this.createNewMultiExpressionIfNeeded(currentMeasure);
-                } else { this.createNewMultiExpressionIfNeeded(currentMeasure, Fraction.createFromFraction(inSourceMeasureCurrentFraction)); }
-                if (this.openContinuousDynamicExpression !== undefined &&
-                    this.openContinuousDynamicExpression.StartMultiExpression !== this.getMultiExpression) {
-                    this.closeOpenContinuousDynamic();
+                    this.createNewMultiExpressionIfNeeded(currentMeasure, numberXml);
+                } else {
+                    this.createNewMultiExpressionIfNeeded(currentMeasure, numberXml,
+                        Fraction.createFromFraction(inSourceMeasureCurrentFraction));
                 }
                 const instantaneousDynamicExpression: InstantaneousDynamicExpression =
                     new InstantaneousDynamicExpression(
@@ -470,7 +499,11 @@ export class ExpressionReader {
                         this.placement,
                         this.staffNumber,
                         currentMeasure);
+                instantaneousDynamicExpression.InMeasureTimestamp = inSourceMeasureCurrentFraction.clone();
                 this.getMultiExpression.addExpression(instantaneousDynamicExpression, "");
+                // addExpression unnecessary now?:
+                //const multiExpression = this.getMultiExpression(ExpressionType.InstantaneousDynamic, numberXml);
+                //multiExpression.addExpression(instantaneousDynamicExpression, "");
                 this.initialize();
                 if (this.activeInstantaneousDynamic) {
                     this.activeInstantaneousDynamic.DynEnum = instantaneousDynamicExpression.DynEnum;
@@ -498,22 +531,31 @@ export class ExpressionReader {
             if (this.checkIfWordsNodeIsRepetitionInstruction(text)) {
                 return;
             }
-            this.fillMultiOrTempoExpression(text, currentMeasure, fontStyle);
+            this.fillMultiOrTempoExpression(text, currentMeasure, inSourceMeasureCurrentFraction, fontStyle);
             this.initialize();
         }
     }
+    private readNumber(node: IXmlElement): number {
+        let numberXml: number = 1; // default value
+        const numberStringXml: string = node.attribute("number")?.value;
+        if (numberStringXml) {
+            numberXml = Number.parseInt(numberStringXml, 10);
+        }
+        return numberXml;
+    }
     private interpretWedge(wedgeNode: IXmlElement, currentMeasure: SourceMeasure, inSourceMeasureCurrentFraction: Fraction, currentMeasureIndex: number): void {
         if (wedgeNode !== undefined && wedgeNode.hasAttributes && wedgeNode.attribute("default-x")) {
             this.directionTimestamp = Fraction.createFromFraction(inSourceMeasureCurrentFraction);
         }
+        const wedgeNumberXml: number = this.readNumber(wedgeNode);
         //Ending needs to use previous fraction, not current.
         //If current is used, when there is a system break it will mess up
-        if (wedgeNode?.attribute("type")?.value?.toLowerCase() === "stop") {
-            this.createNewMultiExpressionIfNeeded(currentMeasure, inSourceMeasureCurrentFraction);
+        if (wedgeNode.attribute("type")?.value?.toLowerCase() === "stop") {
+            this.createNewMultiExpressionIfNeeded(currentMeasure, wedgeNumberXml, inSourceMeasureCurrentFraction);
         } else {
-            this.createNewMultiExpressionIfNeeded(currentMeasure);
+            this.createNewMultiExpressionIfNeeded(currentMeasure, wedgeNumberXml);
         }
-        this.addWedge(wedgeNode, currentMeasure);
+        this.addWedge(wedgeNode, currentMeasure, inSourceMeasureCurrentFraction);
         this.initialize();
     }
     private interpretRehearsalMark(
@@ -522,17 +564,21 @@ export class ExpressionReader {
         // TODO create multi expression? for now we just need to have a static rehearsal mark though.
         currentMeasure.rehearsalExpression = new RehearsalExpression(rehearsalNode.value, this.placement);
     }
-    private createNewMultiExpressionIfNeeded(currentMeasure: SourceMeasure, timestamp: Fraction = undefined): void {
+    private createNewMultiExpressionIfNeeded(currentMeasure: SourceMeasure, numberXml: number,
+        timestamp: Fraction = undefined): MultiExpression {
         if (!timestamp) {
             timestamp = this.directionTimestamp;
         }
-        if (!this.getMultiExpression ||
-            this.getMultiExpression &&
-            (this.getMultiExpression.SourceMeasureParent !== currentMeasure ||
-                (this.getMultiExpression.SourceMeasureParent === currentMeasure && this.getMultiExpression.Timestamp !== timestamp))) {
-            this.getMultiExpression = new MultiExpression(currentMeasure, Fraction.createFromFraction(timestamp));
-            currentMeasure.StaffLinkedExpressions[this.globalStaffIndex].push(this.getMultiExpression);
+        let existingMultiExpression: MultiExpression = this.getMultiExpression;
+        if (!existingMultiExpression ||
+            existingMultiExpression &&
+            (existingMultiExpression.SourceMeasureParent !== currentMeasure ||
+                existingMultiExpression.numberXml !== numberXml ||
+                (existingMultiExpression.SourceMeasureParent === currentMeasure && existingMultiExpression.Timestamp !== timestamp))) {
+                    this.getMultiExpression = existingMultiExpression = new MultiExpression(currentMeasure, Fraction.createFromFraction(timestamp));
+            currentMeasure.StaffLinkedExpressions[this.globalStaffIndex].push(existingMultiExpression);
         }
+        return existingMultiExpression;
     }
 
     private createNewTempoExpressionIfNeeded(currentMeasure: SourceMeasure): void {
@@ -543,8 +589,9 @@ export class ExpressionReader {
             currentMeasure.TempoExpressions.push(this.currentMultiTempoExpression);
         }
     }
-    private addWedge(wedgeNode: IXmlElement, currentMeasure: SourceMeasure): void {
+    private addWedge(wedgeNode: IXmlElement, currentMeasure: SourceMeasure, inSourceMeasureCurrentFraction: Fraction): void {
         if (wedgeNode !== undefined && wedgeNode.hasAttributes) {
+            const numberXml: number = this.readNumber(wedgeNode);
             const type: string = wedgeNode.attribute("type").value.toLowerCase();
             try {
                 if (type === "crescendo" || type === "diminuendo") {
@@ -554,20 +601,26 @@ export class ExpressionReader {
                             this.placement,
                             this.staffNumber,
                             currentMeasure,
-                            this.activeInstantaneousDynamic);
-                    if (this.openContinuousDynamicExpression) {
-                        this.closeOpenContinuousDynamic();
+                            this.activeInstantaneousDynamic,
+                            numberXml);
+                    this.openContinuousDynamicExpressions.push(continuousDynamicExpression);
+                    let multiExpression: MultiExpression = this.getMultiExpression;
+                    if (!multiExpression) {
+                        multiExpression = this.createNewMultiExpressionIfNeeded(currentMeasure, numberXml);
                     }
-                    this.openContinuousDynamicExpression = continuousDynamicExpression;
-                    this.getMultiExpression.StartingContinuousDynamic = continuousDynamicExpression;
-                    continuousDynamicExpression.StartMultiExpression = this.getMultiExpression;
+                    multiExpression.StartingContinuousDynamic = continuousDynamicExpression;
+                    continuousDynamicExpression.StartMultiExpression = multiExpression;
                     if (this.activeInstantaneousDynamic !== undefined &&
                         this.activeInstantaneousDynamic.StaffNumber === continuousDynamicExpression.StaffNumber) {
                         this.activeInstantaneousDynamic = undefined;
                     }
                 } else if (type === "stop") {
-                    if (this.openContinuousDynamicExpression) {
-                        this.closeOpenContinuousDynamic();
+                    for (const openCont of this.openContinuousDynamicExpressions) {
+                        if (openCont.NumberXml === numberXml) {
+                            if (openCont.NumberXml === numberXml) {
+                                this.closeOpenContinuousDynamic(openCont, currentMeasure, inSourceMeasureCurrentFraction);
+                            }
+                        }
                     }
                 }
             } catch (ex) {
@@ -577,7 +630,8 @@ export class ExpressionReader {
             }
         }
     }
-    private fillMultiOrTempoExpression(inputString: string, currentMeasure: SourceMeasure, fontStyle: FontStyles): void {
+    private fillMultiOrTempoExpression(inputString: string, currentMeasure: SourceMeasure, inSourceMeasureCurrentFraction: Fraction,
+        fontStyle: FontStyles): void {
         if (!inputString) {
             return;
         }
@@ -586,7 +640,7 @@ export class ExpressionReader {
         //const splitStrings: string[] = tmpInputString.split(/([\s,\r\n]and[\s,\r\n]|[\s,\r\n]und[\s,\r\n]|[\s,\r\n]e[\s,\r\n]|[\s,\r\n])+/g);
 
         //for (const splitStr of splitStrings) {
-        this.createExpressionFromString("", tmpInputString, currentMeasure, inputString, fontStyle);
+        this.createExpressionFromString("", tmpInputString, currentMeasure, inSourceMeasureCurrentFraction, inputString, fontStyle);
         //}
     }
     /*
@@ -618,7 +672,7 @@ export class ExpressionReader {
     }
     */
     private createExpressionFromString(prefix: string, stringTrimmed: string,
-                                       currentMeasure: SourceMeasure, inputString: string,
+                                       currentMeasure: SourceMeasure, inSourceMeasureCurrentFraction, inputString: string,
                                        fontStyle: FontStyles): boolean {
         if (InstantaneousTempoExpression.isInputStringInstantaneousTempo(stringTrimmed) ||
             ContinuousTempoExpression.isInputStringContinuousTempo(stringTrimmed)) {
@@ -645,10 +699,11 @@ export class ExpressionReader {
                 return true;
             }
             if (ContinuousTempoExpression.isInputStringContinuousTempo(stringTrimmed)) {
-                const continuousTempoExpression: ContinuousTempoExpression = new ContinuousTempoExpression(   stringTrimmed,
-                                                                                                              this.placement,
-                                                                                                              this.staffNumber,
-                                                                                                              this.currentMultiTempoExpression);
+                const continuousTempoExpression: ContinuousTempoExpression = new ContinuousTempoExpression(
+                    stringTrimmed,
+                    this.placement,
+                    this.staffNumber,
+                    this.currentMultiTempoExpression);
                 this.currentMultiTempoExpression.addExpression(continuousTempoExpression, prefix);
                 return true;
             }
@@ -656,7 +711,6 @@ export class ExpressionReader {
         if (ContinuousDynamicExpression.isInputStringContinuousDynamic(stringTrimmed)) {
             // || InstantaneousDynamicExpression.isInputStringInstantaneousDynamic(stringTrimmed)
             //   looks like <words> never has instantaneous dynamics like p or sf, those are in <dynamics>.
-            this.createNewMultiExpressionIfNeeded(currentMeasure);
             // if (InstantaneousDynamicExpression.isInputStringInstantaneousDynamic(stringTrimmed)) {
             //     if (this.openContinuousDynamicExpression !== undefined && !this.openContinuousDynamicExpression.EndMultiExpression) {
             //         this.closeOpenContinuousDynamic();
@@ -679,29 +733,32 @@ export class ExpressionReader {
                     this.staffNumber,
                     currentMeasure,
                     this.activeInstantaneousDynamic,
+                    -1,
                     stringTrimmed);
-            if (this.openContinuousDynamicExpression !== undefined && !this.openContinuousDynamicExpression.EndMultiExpression) {
-                this.closeOpenContinuousDynamic();
+            const openWordContinuousDynamic: MultiExpression = this.getMultiExpression;
+            if (openWordContinuousDynamic) {
+                this.closeOpenContinuousDynamic(openWordContinuousDynamic.StartingContinuousDynamic, currentMeasure, inSourceMeasureCurrentFraction);
             }
+            this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
             if (this.activeInstantaneousDynamic !== undefined && this.activeInstantaneousDynamic.StaffNumber === continuousDynamicExpression.StaffNumber) {
                 this.activeInstantaneousDynamic = undefined;
             }
-            this.openContinuousDynamicExpression = continuousDynamicExpression;
+            this.openContinuousDynamicExpressions.push(continuousDynamicExpression);
             continuousDynamicExpression.StartMultiExpression = this.getMultiExpression;
             this.getMultiExpression.addExpression(continuousDynamicExpression, prefix);
             return true;
         }
         if (MoodExpression.isInputStringMood(stringTrimmed)) {
-            this.createNewMultiExpressionIfNeeded(currentMeasure);
+            const multiExpression: MultiExpression = this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
             currentMeasure.hasMoodExpressions = true;
             const moodExpression: MoodExpression = new MoodExpression(stringTrimmed, this.placement, this.staffNumber);
             moodExpression.fontStyle = fontStyle;
-            this.getMultiExpression.addExpression(moodExpression, prefix);
+            multiExpression.addExpression(moodExpression, prefix);
             return true;
         }
 
         // create unknown:
-        this.createNewMultiExpressionIfNeeded(currentMeasure);
+        const unknownMultiExpression: MultiExpression = this.createNewMultiExpressionIfNeeded(currentMeasure, -1);
         // check here first if there might be a tempo expression doublette:
         if (currentMeasure.TempoExpressions.length > 0) {
             for (let idx: number = 0, len: number = currentMeasure.TempoExpressions.length; idx < len; ++idx) {
@@ -728,15 +785,20 @@ export class ExpressionReader {
         }
         const unknownExpression: UnknownExpression = new UnknownExpression(
             stringTrimmed, this.placement, textAlignment, this.staffNumber);
-        unknownExpression.fontStyle = fontStyle;
-        this.getMultiExpression.addExpression(unknownExpression, prefix);
+            unknownExpression.fontStyle = fontStyle;
+        unknownMultiExpression.addExpression(unknownExpression, prefix);
 
         return false;
     }
-    private closeOpenContinuousDynamic(): void {
-        this.openContinuousDynamicExpression.EndMultiExpression = this.getMultiExpression;
-        this.getMultiExpression.EndingContinuousDynamic = this.openContinuousDynamicExpression;
-        this.openContinuousDynamicExpression = undefined;
+    private closeOpenContinuousDynamic(openContinuousDynamicExpression: ContinuousDynamicExpression, endMeasure: SourceMeasure, timestamp: Fraction): void {
+        if (!openContinuousDynamicExpression) {
+            return;
+        }
+        const numberXml: number = openContinuousDynamicExpression.NumberXml;
+        openContinuousDynamicExpression.EndMultiExpression = this.createNewMultiExpressionIfNeeded(
+            endMeasure, numberXml, timestamp);
+        openContinuousDynamicExpression.StartMultiExpression.EndingContinuousDynamic = openContinuousDynamicExpression;
+        this.openContinuousDynamicExpressions = this.openContinuousDynamicExpressions.filter(dyn => dyn !== openContinuousDynamicExpression);
     }
     private closeOpenContinuousTempo(endTimestamp: Fraction): void {
         this.openContinuousTempoExpression.AbsoluteEndTimestamp = endTimestamp;

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

@@ -7,9 +7,11 @@ import {InstantaneousDynamicExpression} from "../InstantaneousDynamicExpression"
 export class ContinuousDynamicExpression extends AbstractExpression {
     constructor(dynamicType: ContDynamicEnum, placement: PlacementEnum, staffNumber: number,
                 measure: SourceMeasure, activeInstantaneousDynamic: InstantaneousDynamicExpression,
+                numberXml: number,
                 label: string = "") {
         super(placement);
         super.parentMeasure = measure;
+        this.NumberXml = numberXml;
         this.dynamicType = dynamicType;
         this.label = label;
         this.staffNumber = staffNumber;
@@ -26,6 +28,7 @@ export class ContinuousDynamicExpression extends AbstractExpression {
     private static listContinuousDynamicDecreasing: string[] = ["decrescendo", "decresc", "decr.", "diminuendo", "dim.", "dim"];
     // private static listContinuousDynamicGeneral: string[] = ["subito","al niente","piu","meno"];
     private dynamicType: ContDynamicEnum;
+    public NumberXml: number;
     private startMultiExpression: MultiExpression;
     private endMultiExpression: MultiExpression;
     private startVolume: number;

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

@@ -8,6 +8,7 @@ export class OctaveShift {
 
     private octaveValue: OctaveEnum;
     private staffNumber: number;
+    public numberXml: number = 1;
     private startMultiExpression: MultiExpression;
     private endMultiExpression: MultiExpression;
 

+ 2 - 0
src/MusicalScore/VoiceData/Expressions/InstantaneousDynamicExpression.ts

@@ -6,6 +6,7 @@ import {InvalidEnumArgumentException} from "../../Exceptions";
 import log from "loglevel";
 import { SourceMeasure } from "../SourceMeasure";
 import { Dictionary } from "typescript-collections";
+import { Fraction } from "../../../Common/DataObjects/Fraction";
 
 export class InstantaneousDynamicExpression extends AbstractExpression {
     public static staticConstructor(): void {
@@ -51,6 +52,7 @@ export class InstantaneousDynamicExpression extends AbstractExpression {
     private soundDynamic: number;
     private staffNumber: number;
     private length: number;
+    public InMeasureTimestamp: Fraction;
 
     public get ParentMultiExpression(): MultiExpression {
         return this.multiExpression;

+ 2 - 0
src/MusicalScore/VoiceData/Expressions/MultiExpression.ts

@@ -21,6 +21,8 @@ export class MultiExpression {
     private sourceMeasure: SourceMeasure;
     private staffNumber: number;
     private timestamp: Fraction;
+    /** The 'number="x"' given in XML, e.g. of a wedge, used to identify similar expressions. */
+    public numberXml: number;
     private instantaneousDynamic: InstantaneousDynamicExpression;
     private endingContinuousDynamic: ContinuousDynamicExpression;
     private startingContinuousDynamic: ContinuousDynamicExpression;

+ 2 - 2
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.4.3-audio-extended"; // getter: this.Version
+    private version: string = "1.4.5-audio-extended"; // getter: this.Version
     // at release, bump version and change to -release, afterwards to -dev again
 
     /**
@@ -281,7 +281,7 @@ export class OpenSheetMusicDisplay {
         // Set page width
         let width: number = this.container.offsetWidth;
         if (this.rules.RenderSingleHorizontalStaffline) {
-            width = 32767; // set safe maximum (browser limit), will be reduced later
+            width = this.rules.SheetMaximumWidth; // set safe maximum (browser limit), will be reduced later
             // reduced later in MusicSheetCalculator.calculatePageLabels (sets sheet.pageWidth to page.PositionAndShape.Size.width before labels)
             // rough calculation:
             // width = 600 * this.sheet.SourceMeasures.length;

+ 1 - 1
src/Util/CollectionUtil.ts

@@ -89,7 +89,7 @@ export class CollectionUtil {
                                   startIndex: number = 0,
                                   endIndex: number = array.length - 1): number {
         let mid: number = 1;
-        while (startIndex < endIndex) {
+        while (startIndex <= endIndex) {
             mid = Math.floor((startIndex + endIndex) / 2);
             const c: number = cmp(array[mid], element);
             if (c === 0) {

+ 391 - 0
test/data/test_cresc_dim_simultaneous_quartet.musicxml

@@ -0,0 +1,391 @@
+<?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_cresc_dim_simultaneous_quartet</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.6.2</software>
+      <encoding-date>2022-01-26</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">test_cresc_dim_simultaneous_quartet</credit-words>
+    </credit>
+  <part-list>
+    <part-group type="start" number="1">
+      <group-symbol>bracket</group-symbol>
+      </part-group>
+    <score-part id="P1">
+      <part-name>Violin I</part-name>
+      <score-instrument id="P1-I1">
+        <instrument-name>Violin</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>41</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <score-part id="P2">
+      <part-name>Violin II</part-name>
+      <score-instrument id="P2-I1">
+        <instrument-name>Violin</instrument-name>
+        </score-instrument>
+      <midi-device id="P2-I1" port="1"></midi-device>
+      <midi-instrument id="P2-I1">
+        <midi-channel>4</midi-channel>
+        <midi-program>41</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <score-part id="P3">
+      <part-name>Viola</part-name>
+      <score-instrument id="P3-I1">
+        <instrument-name>Viola</instrument-name>
+        </score-instrument>
+      <midi-device id="P3-I1" port="1"></midi-device>
+      <midi-instrument id="P3-I1">
+        <midi-channel>7</midi-channel>
+        <midi-program>42</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <score-part id="P4">
+      <part-name>Violoncello</part-name>
+      <score-instrument id="P4-I1">
+        <instrument-name>Violoncello</instrument-name>
+        </score-instrument>
+      <midi-device id="P4-I1" port="1"></midi-device>
+      <midi-instrument id="P4-I1">
+        <midi-channel>11</midi-channel>
+        <midi-program>43</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <part-group type="stop" number="1"/>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="483.61">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>118.24</left-margin>
+            <right-margin>426.72</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>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="crescendo" number="1" default-y="-60.00"/>
+          </direction-type>
+        </direction>
+      <note default-x="84.45" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="181.58" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="278.70" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="375.83" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="stop" number="1"/>
+          </direction-type>
+        </direction>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  <part id="P2">
+    <measure number="1" width="483.61">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>97.44</staff-distance>
+          </staff-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>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="diminuendo" number="1" default-y="-65.04"/>
+          </direction-type>
+        </direction>
+      <note default-x="84.45" default-y="-157.44">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="181.58" default-y="-157.44">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="278.70" default-y="-157.44">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="375.83" default-y="-157.44">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="stop" number="1"/>
+          </direction-type>
+        </direction>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  <part id="P3">
+    <measure number="1" width="483.61">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>97.44</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>C</sign>
+          <line>3</line>
+          </clef>
+        </attributes>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="crescendo" number="1" default-y="-61.29"/>
+          </direction-type>
+        </direction>
+      <note default-x="84.45" default-y="-289.88">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="181.58" default-y="-289.88">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="stop" number="1" relative-x="-2.06"/>
+          </direction-type>
+        </direction>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="diminuendo" number="1" default-y="-61.29"/>
+          </direction-type>
+        </direction>
+      <note default-x="278.70" default-y="-289.88">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="375.83" default-y="-289.88">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="stop" number="1"/>
+          </direction-type>
+        </direction>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  <part id="P4">
+    <measure number="1" width="483.61">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>97.44</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        </attributes>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="diminuendo" number="1" default-y="-60.00"/>
+          </direction-type>
+        </direction>
+      <note default-x="83.49" default-y="-417.32">
+        <pitch>
+          <step>G</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>whole</type>
+        </note>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="stop" number="1" relative-x="-4.09"/>
+          </direction-type>
+        </direction>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 720 - 0
test/data/test_wedges_layered.musicxml

@@ -0,0 +1,720 @@
+<?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_wedges_layered</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.6.2</software>
+      <encoding-date>2022-01-26</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">test_wedges_layered</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="358.35">
+      <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>98.67</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>2</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>4</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>
+      <direction placement="below">
+        <direction-type>
+          <words default-y="-40.00" relative-y="-25.00" font-style="italic">cresc.</words>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="diminuendo" number="1" default-y="36.69"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="crescendo" number="2" default-y="36.69"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <note default-x="83.49" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <notations>
+          <slur type="start" placement="above" number="1"/>
+          <technical>
+            <fingering>3</fingering>
+            </technical>
+          </notations>
+        </note>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="stop" number="1"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="stop" number="2"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <note default-x="151.72" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        </note>
+      <note default-x="219.95" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <notations>
+          <slur type="stop" number="1"/>
+          </notations>
+        </note>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="crescendo" number="1" default-y="51.20"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note default-x="83.49" default-y="-123.67">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <technical>
+            <fingering placement="below">3</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="117.60" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="151.72" default-y="-128.67">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="185.83" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        </note>
+      <note default-x="219.95" default-y="-133.67">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        </note>
+      <note default-x="254.06" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="288.32" default-y="-113.67">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="322.44" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        </note>
+      </measure>
+    <measure number="2" width="290.09">
+      <direction placement="below">
+        <direction-type>
+          <dynamics default-x="3.25" default-y="-40.00" relative-y="-25.00">
+            <mf/>
+            </dynamics>
+          </direction-type>
+        <staff>1</staff>
+        <sound dynamics="88.89"/>
+        </direction>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="diminuendo" number="2" default-y="57.44"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="diminuendo" number="3" default-y="-60.00"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <note default-x="21.54" default-y="10.00">
+        <pitch>
+          <step>A</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <notations>
+          <slur type="start" placement="above" number="1"/>
+          <technical>
+            <fingering>5</fingering>
+            </technical>
+          </notations>
+        </note>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="stop" number="1"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <note default-x="88.23" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        </note>
+      <direction placement="above">
+        <direction-type>
+          <wedge type="stop" number="2"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <note default-x="154.91" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        </note>
+      <note default-x="221.60" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note default-x="21.54" default-y="-113.67">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        </note>
+      <note default-x="54.88" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="88.23" default-y="-118.67">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="121.57" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        </note>
+      <note default-x="154.91" default-y="-123.67">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        </note>
+      <note default-x="188.26" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="221.60" default-y="-128.67">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="254.95" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        </note>
+      </measure>
+    <measure number="3" width="314.23">
+      <note print-object="no">
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <staff>1</staff>
+        </note>
+      <note default-x="156.44" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>3</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <dot/>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <note default-x="264.02" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        <notations>
+          <slur type="stop" number="1"/>
+          <technical>
+            <fingering>3</fingering>
+            </technical>
+          </notations>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note default-x="13.00" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        </note>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="stop" number="3"/>
+          </direction-type>
+        <staff>1</staff>
+        </direction>
+      <note default-x="48.86" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="84.72" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="120.58" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+        <notations>
+          <technical>
+            <fingering placement="above">5</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="156.44" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <technical>
+            <fingering placement="below">1</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="192.30" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+        <notations>
+          <technical>
+            <fingering placement="below">2</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="228.16" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="264.02" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>2</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note default-x="13.00" default-y="-133.67">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">begin</beam>
+        <notations>
+          <technical>
+            <fingering placement="below">5</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="48.86" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="84.72" default-y="-128.67">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">continue</beam>
+        <notations>
+          <technical>
+            <fingering placement="below">4</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="120.58" default-y="-128.67">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>5</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem>up</stem>
+        <staff>2</staff>
+        <beam number="1">end</beam>
+        </note>
+      <note default-x="156.44" default-y="-123.67">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        </note>
+      <note default-x="228.16" default-y="-108.67">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>