Explorar el Código

Merge branch 'develop' into feat/percussion_stafflines

Simon hace 5 años
padre
commit
6b2642a487

+ 1 - 1
package.json

@@ -71,7 +71,7 @@
   "devDependencies": {
     "@types/chai": "^4.2.11",
     "@types/mocha": "^7.0.2",
-    "@types/node": "^14.0.5",
+    "@types/node": "^14.0.9",
     "canvas": "^2.6.1",
     "chai": "^4.1.0",
     "clean-webpack-plugin": "^3.0.0",

+ 1 - 1
src/MusicalScore/Graphical/AbstractGraphicalExpression.ts

@@ -13,7 +13,7 @@ export abstract class AbstractGraphicalExpression extends GraphicalObject {
     /** EngravingRules for positioning */
     protected rules: EngravingRules;
 
-    constructor(parentStaffline: StaffLine, expression: AbstractExpression) {
+    constructor(parentStaffline: StaffLine, expression: AbstractExpression = undefined) {
         super();
         this.expression = expression;
         this.boundingBox = new BoundingBox(this, parentStaffline.PositionAndShape);

+ 3 - 0
src/MusicalScore/Graphical/DrawingParameters.ts

@@ -223,6 +223,9 @@ export class DrawingParameters {
     public set DrawPartNames(value: boolean) {
         this.drawPartNames = value;
         this.rules.RenderPartNames = value;
+        if (!this.rules.RenderPartNames) {
+            this.rules.RenderPartAbbreviations = false;
+        }
     }
 
     public get FingeringPosition(): PlacementEnum {

+ 20 - 9
src/MusicalScore/Graphical/EngravingRules.ts

@@ -93,7 +93,6 @@ export class EngravingRules {
     private chordSymbolTextHeight: number;
     private chordSymbolXSpacing: number;
     private chordSymbolYOffset: number;
-    private fingeringLabelFontHeight: number;
     private measureNumberLabelHeight: number;
     private measureNumberLabelOffset: number;
     /** Whether tuplets should display ratio (3:2 instead of 3 for triplet). Default false. */
@@ -220,6 +219,8 @@ export class EngravingRules {
     /** Position of fingering label in relation to corresponding note (left, right supported, above, below experimental) */
     private fingeringPosition: PlacementEnum;
     private fingeringInsideStafflines: boolean;
+    private fingeringLabelFontHeight: number;
+    private fingeringOffsetX: number;
     private newSystemAtXMLNewSystemAttribute: boolean;
     private newPageAtXMLNewPageAttribute: boolean;
     private pageFormat: PageFormat;
@@ -337,7 +338,6 @@ export class EngravingRules {
         this.chordSymbolTextHeight = 2.0;
         this.chordSymbolXSpacing = 1.0;
         this.chordSymbolYOffset = 2.0;
-        this.fingeringLabelFontHeight = 1.7;
 
         // Tuplets, MeasureNumber and TupletNumber Labels
         this.measureNumberLabelHeight = 1.5 * EngravingRules.unit;
@@ -413,7 +413,7 @@ export class EngravingRules {
 
         // Line Widths
         this.minimumStaffLineDistance = 4.0;
-        this.minimumSkyBottomLineDistance = 2.0; // default. 1.0 for compacttight mode (1.0 can cause overlaps)
+        this.minimumSkyBottomLineDistance = 1.0; // default. compacttight mode sets it to 1.0 (as well).
         this.minimumCrossedBeamDifferenceMargin = 0.0001;
 
         // xSpacing Variables
@@ -457,6 +457,8 @@ export class EngravingRules {
         this.renderLyrics = true;
         this.fingeringPosition = PlacementEnum.Left; // easier to get bounding box, and safer for vertical layout
         this.fingeringInsideStafflines = false;
+        this.fingeringLabelFontHeight = 1.7;
+        this.fingeringOffsetX = 0.0;
         this.newSystemAtXMLNewSystemAttribute = false;
         this.newPageAtXMLNewPageAttribute = false;
         this.restoreCursorAfterRerender = true;
@@ -952,12 +954,6 @@ export class EngravingRules {
     public set ChordSymbolYOffset(value: number) {
         this.chordSymbolYOffset = value;
     }
-    public get FingeringLabelFontHeight(): number {
-        return this.fingeringLabelFontHeight;
-    }
-    public set FingeringLabelFontHeight(value: number) {
-        this.fingeringLabelFontHeight = value;
-    }
     public get MeasureNumberLabelHeight(): number {
         return this.measureNumberLabelHeight;
     }
@@ -1579,6 +1575,9 @@ export class EngravingRules {
     }
     public set RenderPartNames(value: boolean) {
         this.renderPartNames = value;
+        if (!this.renderPartNames) {
+            this.renderPartAbbreviations = false;
+        }
     }
     public get RenderPartAbbreviations(): boolean {
         return this.renderPartAbbreviations;
@@ -1616,6 +1615,18 @@ export class EngravingRules {
     public set FingeringInsideStafflines(value: boolean) {
         this.fingeringInsideStafflines = value;
     }
+    public get FingeringLabelFontHeight(): number {
+        return this.fingeringLabelFontHeight;
+    }
+    public set FingeringLabelFontHeight(value: number) {
+        this.fingeringLabelFontHeight = value;
+    }
+    public get FingeringOffsetX(): number {
+        return this.fingeringOffsetX;
+    }
+    public set FingeringOffsetX(value: number) {
+        this.fingeringOffsetX = value;
+    }
     public get NewSystemAtXMLNewSystemAttribute(): boolean {
         return this.newSystemAtXMLNewSystemAttribute;
     }

+ 18 - 2
src/MusicalScore/Graphical/GraphicalContinuousDynamicExpression.ts

@@ -69,10 +69,22 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
         if (!this.IsVerbal && this.lines.length < 2) {
             log.warn("Not enough lines for SkyBottomLine calculation");
         }
+        if (!this.IsVerbal) {
+            if (this.ContinuousDynamic.DynamicType !== ContDynamicEnum.crescendo &&
+                this.ContinuousDynamic.DynamicType !== ContDynamicEnum.diminuendo) {
+                // for now there is only crescendo or decrescendo anyways, but this will catch errors when we add new types in the future
+                log.warn("GraphicalContinuousDynamicExpression.updateSkyBottomLine(): " +
+                    "unhandled continuous dynamic type. start measure: " + this.startMeasure?.MeasureNumber);
+            }
+        }
         switch (this.Placement) {
             case PlacementEnum.Above:
                 if (!this.IsVerbal) {
-                    skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
+                    if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+                        skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
+                    } else if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.diminuendo) {
+                        skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].End, this.lines[0].Start);
+                    } // else covered with the log.warn above
                 } else {
                     const yValue: number = this.label.PositionAndShape.BorderMarginTop + this.label.PositionAndShape.RelativePosition.y;
                     skyBottomLineCalculator.updateSkyLineInRange(left, right, yValue);
@@ -80,7 +92,11 @@ export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpre
                 break;
             case PlacementEnum.Below:
                 if (!this.IsVerbal) {
-                    skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].Start, this.lines[1].End);
+                    if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+                        skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].Start, this.lines[1].End);
+                    } else if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.diminuendo) {
+                        skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].End, this.lines[1].Start);
+                    } // else covered with the log.warn above
                 } else {
                     const yValue: number = this.label.PositionAndShape.BorderMarginBottom + this.label.PositionAndShape.RelativePosition.y;
                     skyBottomLineCalculator.updateBottomLineInRange(left, right, yValue);

+ 1 - 1
src/MusicalScore/Graphical/GraphicalCurve.ts

@@ -33,7 +33,7 @@ export class GraphicalCurve {
      * @param relativePosition
      */
     public calculateCurvePointAtIndex(relativePosition: number): PointF2D {
-        const index: number =  Math.round(relativePosition);
+        const index: number =  Math.round(relativePosition * GraphicalCurve.bezierCurveStepSize);
         if (index < 0 || index >= GraphicalCurve.bezierCurveStepSize) {
             return new PointF2D();
         }

+ 27 - 24
src/MusicalScore/Graphical/GraphicalSlur.ts

@@ -566,32 +566,35 @@ export class GraphicalSlur extends GraphicalCurve {
         const startStaffEntry: GraphicalStaffEntry = this.staffEntries[0];
         const endStaffEntry: GraphicalStaffEntry = this.staffEntries[this.staffEntries.length - 1];
 
-        // Deactivated: single Voice, opposite to StemDirection
-        // if (startStaffEntry.hasStem() && endStaffEntry.hasStem() && startStaffEntry.getStemDirection() === endStaffEntry.getStemDirection()) {
-        //     this.placement = (startStaffEntry.getStemDirection() === StemDirectionType.Up) ? PlacementEnum.Below : PlacementEnum.Above;
-        // } else {
-
-        // Placement at the side with the minimum border
-        let sX: number = startStaffEntry.PositionAndShape.BorderLeft + startStaffEntry.PositionAndShape.RelativePosition.x
-                    + startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
-        let eX: number = endStaffEntry.PositionAndShape.BorderRight + endStaffEntry.PositionAndShape.RelativePosition.x
-                    + endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
-
-        if (this.graceStart) {
-            sX += endStaffEntry.PositionAndShape.RelativePosition.x;
-        }
-        if (this.graceEnd) {
-            eX += endStaffEntry.staffEntryParent.PositionAndShape.RelativePosition.x;
-        }
+        // single Voice, opposite to StemDirection
+        // here should only be one voiceEntry, so we can take graphicalVoiceEntries[0]:
+        const startStemDirection: StemDirectionType = startStaffEntry.graphicalVoiceEntries[0].parentVoiceEntry.StemDirection;
+        const endStemDirection: StemDirectionType = endStaffEntry.graphicalVoiceEntries[0].parentVoiceEntry.StemDirection;
+        if (startStemDirection  ===
+            endStemDirection) {
+            this.placement = (startStemDirection === StemDirectionType.Up) ? PlacementEnum.Below : PlacementEnum.Above;
+        } else {
+            // Placement at the side with the minimum border
+            let sX: number = startStaffEntry.PositionAndShape.BorderLeft + startStaffEntry.PositionAndShape.RelativePosition.x
+                        + startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
+            let eX: number = endStaffEntry.PositionAndShape.BorderRight + endStaffEntry.PositionAndShape.RelativePosition.x
+                        + endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
+
+            if (this.graceStart) {
+                sX += endStaffEntry.PositionAndShape.RelativePosition.x;
+            }
+            if (this.graceEnd) {
+                eX += endStaffEntry.staffEntryParent.PositionAndShape.RelativePosition.x;
+            }
 
-        // get SkyBottomLine borders
-        const minAbove: number = skyBottomLineCalculator.getSkyLineMinInRange(sX, eX) * -1;
-        const maxBelow: number = skyBottomLineCalculator.getBottomLineMaxInRange(sX, eX) - staffLine.StaffHeight;
+            // get SkyBottomLine borders
+            const minAbove: number = skyBottomLineCalculator.getSkyLineMinInRange(sX, eX) * -1;
+            const maxBelow: number = skyBottomLineCalculator.getBottomLineMaxInRange(sX, eX) - staffLine.StaffHeight;
 
-        if (maxBelow > minAbove) {
-            this.placement = PlacementEnum.Above;
-        } else { this.placement = PlacementEnum.Below; }
-        //}
+            if (maxBelow > minAbove) {
+                this.placement = PlacementEnum.Above;
+            } else { this.placement = PlacementEnum.Below; }
+        }
     }
 
     /**

+ 33 - 0
src/MusicalScore/Graphical/GraphicalUnknownExpression.ts

@@ -0,0 +1,33 @@
+
+import { StaffLine } from "./StaffLine";
+import { GraphicalLabel } from "./GraphicalLabel";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
+import { PlacementEnum } from "../VoiceData/Expressions";
+import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
+import log from "loglevel";
+
+export class GraphicalUnknownExpression extends AbstractGraphicalExpression {
+    constructor(staffLine: StaffLine, label: GraphicalLabel) {
+        super(staffLine);
+        this.label = label;
+    }
+
+    public updateSkyBottomLine(): void {
+        // update Sky-BottomLine
+        const skyBottomLineCalculator: SkyBottomLineCalculator = this.parentStaffLine.SkyBottomLineCalculator;
+        const left: number = this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginLeft;
+        const right: number = this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginRight;
+        switch (this.Placement) {
+            case PlacementEnum.Above:
+                const yValueAbove: number = this.label.PositionAndShape.BorderMarginTop + this.label.PositionAndShape.RelativePosition.y;
+                skyBottomLineCalculator.updateSkyLineInRange(left, right, yValueAbove);
+                break;
+            case PlacementEnum.Below:
+                const yValueBelow: number = this.label.PositionAndShape.BorderMarginBottom + this.label.PositionAndShape.RelativePosition.y;
+                skyBottomLineCalculator.updateBottomLineInRange(left, right, yValueBelow);
+                break;
+            default:
+                log.error("Placement for GraphicalUnknownExpression is unknown");
+        }
+    }
+}

+ 42 - 2
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -21,7 +21,7 @@ import { Tuplet } from "../VoiceData/Tuplet";
 import { MusicSystem } from "./MusicSystem";
 import { GraphicalTie } from "./GraphicalTie";
 import { RepetitionInstruction } from "../VoiceData/Instructions/RepetitionInstruction";
-import { MultiExpression } from "../VoiceData/Expressions/MultiExpression";
+import { MultiExpression, MultiExpressionEntry } from "../VoiceData/Expressions/MultiExpression";
 import { StaffEntryLink } from "../VoiceData/StaffEntryLink";
 import { MusicSystemBuilder } from "./MusicSystemBuilder";
 import { MultiTempoExpression } from "../VoiceData/Expressions/MultiTempoExpression";
@@ -68,6 +68,7 @@ import { ContDynamicEnum } from "../VoiceData/Expressions/ContinuousExpressions/
 import { GraphicalContinuousDynamicExpression } from "./GraphicalContinuousDynamicExpression";
 import { FillEmptyMeasuresWithWholeRests } from "../../OpenSheetMusicDisplay/OSMDOptions";
 import { IStafflineNoteCalculator } from "../Interfaces/IStafflineNoteCalculator";
+import { GraphicalUnknownExpression } from "./GraphicalUnknownExpression";
 
 /**
  * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
@@ -545,7 +546,46 @@ export abstract class MusicSheetCalculator {
      * @param staffIndex
      */
     protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
-        throw new Error("abstract, not implemented");
+        // calculate absolute Timestamp
+        const absoluteTimestamp: Fraction = multiExpression.AbsoluteTimestamp;
+        const measures: GraphicalMeasure[] = this.graphicalMusicSheet.MeasureList[measureIndex];
+        let relative: PointF2D = new PointF2D();
+
+        if ((multiExpression.MoodList.length > 0) || (multiExpression.UnknownList.length > 0)) {
+        let combinedExprString: string  = "";
+        for (let idx: number = 0, len: number = multiExpression.EntriesList.length; idx < len; ++idx) {
+            const entry: MultiExpressionEntry = multiExpression.EntriesList[idx];
+            if (entry.prefix !== "") {
+                if (combinedExprString === "") {
+                    combinedExprString += entry.prefix;
+                } else {
+                    combinedExprString += " " + entry.prefix;
+                }
+            }
+            if (combinedExprString === "") {
+                combinedExprString += entry.label;
+            } else {
+                combinedExprString += " " + entry.label;
+            }
+        }
+        const staffLine: StaffLine = measures[staffIndex].ParentStaffLine;
+        relative = this.getRelativePositionInStaffLineFromTimestamp(absoluteTimestamp, staffIndex, staffLine, staffLine.isPartOfMultiStaffInstrument());
+
+        if (Math.abs(relative.x - 0) < 0.0001) {
+            relative.x = measures[staffIndex].beginInstructionsWidth + this.rules.RhythmRightMargin;
+        }
+
+        const fontHeight: number = this.rules.UnknownTextHeight;
+
+        const graphLabel: GraphicalLabel  = this.calculateLabel(staffLine,
+                                                                relative, combinedExprString,
+                                                                multiExpression.getFontstyleOfFirstEntry(),
+                                                                multiExpression.getPlacementOfFirstEntry(),
+                                                                fontHeight);
+
+        const gue: GraphicalUnknownExpression = new GraphicalUnknownExpression(staffLine, graphLabel);
+        staffLine.AbstractExpressions.push(gue);
+        }
     }
 
     /**

+ 2 - 2
src/MusicalScore/Graphical/SkyBottomLineCalculator.ts

@@ -166,8 +166,8 @@ export class SkyBottomLineCalculator {
 
     /**
      * This method updates the SkyLine for a given Wedge.
-     * @param start Start point of the wedge
-     * @param end End point of the wedge
+     * @param start Start point of the wedge (the point where both lines meet)
+     * @param end End point of the wedge (the end of the most extreme line: upper line for skyline, lower line for bottomline)
      */
     public updateSkyLineWithWedge(start: PointF2D, end: PointF2D): void {
         // FIXME: Refactor if wedges will be added. Current status is that vexflow will be used for this

+ 2 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -1241,6 +1241,7 @@ export class VexFlowMeasure extends GraphicalMeasure {
 
             const fretFinger: Vex.Flow.FretHandFinger = new Vex.Flow.FretHandFinger(fingeringInstruction.value);
             fretFinger.setPosition(modifierPosition);
+            fretFinger.setOffsetX(this.rules.FingeringOffsetX);
             if (fingeringPosition === PlacementEnum.Above || fingeringPosition === PlacementEnum.Below) {
                 const offsetYSign: number = fingeringPosition === PlacementEnum.Above ? -1 : 1; // minus y is up
                 const ordering: number = fingeringPosition === PlacementEnum.Above ? fingeringIndex :
@@ -1250,7 +1251,7 @@ export class VexFlowMeasure extends GraphicalMeasure {
                     // set y-shift. vexflow fretfinger simply places directly above/below note
                     const perFingeringShift: number = fretFinger.getWidth() / 2;
                     const shiftCount: number = fingeringsCount * 2.5;
-                    (<any>fretFinger).setOffsetY(offsetYSign * (ordering + shiftCount) * perFingeringShift);
+                    fretFinger.setOffsetY(offsetYSign * (ordering + shiftCount) * perFingeringShift);
                 } else if (!this.rules.FingeringInsideStafflines) { // use StringNumber for placement above/below stafflines
                     const stringNumber: Vex.Flow.StringNumber = new Vex.Flow.StringNumber(fingeringInstruction.value);
                     (<any>stringNumber).radius = 0; // hack to remove the circle around the number

+ 9 - 12
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -694,26 +694,23 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
   }
 
-  protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
-    return;
-  }
-
   /**
    * Re-adjust the x positioning of expressions. Update the skyline afterwards
    */
   protected calculateExpressionAlignements(): void {
     for (const musicSystem of this.musicSystems) {
-            for (const staffLine of musicSystem.StaffLines) {
-              try {
-                (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
-                staffLine.AbstractExpressions.forEach(ae => ae.updateSkyBottomLine());
-              } catch (e) {
-                // TODO still necessary when calculation of expression fails, see calculateDynamicExpressionsForMultiExpression()
-                //   see calculateGraphicalContinuousDynamic(), also in MusicSheetCalculator.
+      for (const staffLine of musicSystem.StaffLines) {
+        try {
+          (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
+          staffLine.AbstractExpressions.forEach(ae => ae.updateSkyBottomLine());
+        } catch (e) {
+          // TODO still necessary when calculation of expression fails, see calculateDynamicExpressionsForMultiExpression()
+          //   see calculateGraphicalContinuousDynamic(), also in MusicSheetCalculator.
         }
+      }
     }
   }
-  }
+
 
   /**
    * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.

+ 7 - 8
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -27,6 +27,7 @@ import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicEx
 import { DrawingParameters } from "../DrawingParameters";
 import { GraphicalMusicPage } from "../GraphicalMusicPage";
 import { GraphicalMusicSheet } from "../GraphicalMusicSheet";
+import { GraphicalUnknownExpression } from "../GraphicalUnknownExpression";
 
 /**
  * This is a global constant which denotes the height in pixels of the space between two lines of the stave
@@ -222,7 +223,8 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     private drawSampledLine(line: number[], startPosition: PointF2D, width: number, color: string = "#FF0000FF"): void {
         const indices: number[] = [];
         let currentValue: number = 0;
-
+        //Loops through bottom line, grabs all indices that don't equal the previously grabbed index
+        //Starting with 0 (gets index of all line changes)
         for (let i: number = 0; i < line.length; i++) {
             if (line[i] !== currentValue) {
                 indices.push(i);
@@ -355,13 +357,10 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
                 // // Draw Mood
                 // } else if (abstractGraphicalExpression instanceof GraphicalMoodExpression) {
                 //     GraphicalMoodExpression; graphicalMood = (GraphicalMoodExpression); abstractGraphicalExpression;
-                //     drawLabel(graphicalMood.GetGraphicalLabel, (int)GraphicalLayers.Notes);
-                // // Draw Unknown
-                // } else if (abstractGraphicalExpression instanceof GraphicalUnknownExpression) {
-                //     GraphicalUnknownExpression; graphicalUnknown =
-                //         (GraphicalUnknownExpression); abstractGraphicalExpression;
-                //     drawLabel(graphicalUnknown.GetGraphicalLabel, (int)GraphicalLayers.Notes);
-                // }
+                //     drawLabel(graphicalMood.GetGraphicalLabel, <number>GraphicalLayers.Notes);
+            // Draw Unknown
+            } else if (abstractGraphicalExpression instanceof GraphicalUnknownExpression) {
+                this.drawLabel(abstractGraphicalExpression.Label, <number>GraphicalLayers.Notes);
             } else {
                 log.warn("Unkown type of expression!");
             }

+ 41 - 35
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -3,7 +3,7 @@ import {Fraction} from "../../../Common/DataObjects/Fraction";
 import {MultiTempoExpression} from "../../VoiceData/Expressions/MultiTempoExpression";
 import {ContDynamicEnum, ContinuousDynamicExpression} from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
 import {ContinuousTempoExpression} from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousTempoExpression";
-import {DynamicEnum, InstantaneousDynamicExpression} from "../../VoiceData/Expressions/InstantaneousDynamicExpression";
+import {InstantaneousDynamicExpression} from "../../VoiceData/Expressions/InstantaneousDynamicExpression";
 import {OctaveShift} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import {Instrument} from "../../Instrument";
 import {MultiExpression} from "../../VoiceData/Expressions/MultiExpression";
@@ -308,34 +308,37 @@ export class ExpressionReader {
                 expressionText = dynamicsNode.elements()[0].value;
             }
             if (expressionText !== undefined) {
-                let dynamicEnum: DynamicEnum;
-                try {
-                    dynamicEnum = DynamicEnum[expressionText];
-                } catch (err) {
-                    const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DynamicError", "Error while reading dynamic.");
-                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                    return;
-                }
-
-                if (this.activeInstantaneousDynamic === undefined ||
-                    (this.activeInstantaneousDynamic !== undefined && 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();
-                    }
-                    const instantaneousDynamicExpression: InstantaneousDynamicExpression = new InstantaneousDynamicExpression(expressionText,
-                                                                                                                              this.soundDynamic,
-                                                                                                                              this.placement,
-                                                                                                                              this.staffNumber);
-                    this.getMultiExpression.addExpression(instantaneousDynamicExpression, "");
-                    this.initialize();
-                    if (this.activeInstantaneousDynamic !== undefined) {
-                        this.activeInstantaneousDynamic.DynEnum = instantaneousDynamicExpression.DynEnum;
-                    } else { this.activeInstantaneousDynamic = new InstantaneousDynamicExpression(expressionText, 0, PlacementEnum.NotYetDefined, 1); }
+                // // ToDo: add doublettes recognition again as a afterReadingModule, as we can't check here if there is a repetition:
+                // // Make here a comparison with the active dynamic expression and only add it, if there is a change in dynamic
+                // // Exception is when there starts a repetition, where this might be different when repeating.
+                // // see PR #767 where this was removed
+                // let dynamicEnum: DynamicEnum;
+                // try {
+                //     dynamicEnum = DynamicEnum[expressionText];
+                // } catch (err) {
+                //     const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DynamicError", "Error while reading dynamic.");
+                //     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                //     return;
+                // }
+                // if (this.activeInstantaneousDynamic === undefined ||
+                //     (this.activeInstantaneousDynamic !== undefined && 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();
                 }
+                const instantaneousDynamicExpression: InstantaneousDynamicExpression = new InstantaneousDynamicExpression(  expressionText,
+                                                                                                                            this.soundDynamic,
+                                                                                                                            this.placement,
+                                                                                                                            this.staffNumber);
+                this.getMultiExpression.addExpression(instantaneousDynamicExpression, "");
+                this.initialize();
+                if (this.activeInstantaneousDynamic !== undefined) {
+                    this.activeInstantaneousDynamic.DynEnum = instantaneousDynamicExpression.DynEnum;
+                } else { this.activeInstantaneousDynamic = new InstantaneousDynamicExpression(expressionText, 0, PlacementEnum.NotYetDefined, 1); }
+                //}
             }
         }
     }
@@ -416,11 +419,11 @@ export class ExpressionReader {
         }
         const tmpInputString: string = inputString.trim();
         // split string at enumerating words or signs
-        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);
+        //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("", splitStr, currentMeasure, inputString);
-        }
+        //for (const splitStr of splitStrings) {
+        this.createExpressionFromString("", tmpInputString, currentMeasure, inputString);
+        //}
     }
     /*
     private splitStringRecursive(input: [string, string], stringSeparators: string[]): [string, string][] {
@@ -525,6 +528,7 @@ export class ExpressionReader {
 
         // create unknown:
         this.createNewMultiExpressionIfNeeded(currentMeasure);
+        // 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) {
                 const multiTempoExpression: MultiTempoExpression = currentMeasure.TempoExpressions[idx];
@@ -532,9 +536,11 @@ export class ExpressionReader {
                     multiTempoExpression.InstantaneousTempo !== undefined &&
                     multiTempoExpression.EntriesList.length > 0 &&
                     !this.hasDigit(stringTrimmed)) {
-                    if (this.globalStaffIndex > 0) {
-                        if (multiTempoExpression.EntriesList[0].label.indexOf(stringTrimmed) >= 0) {
-                            return false;
+                        // if at other parts of the score
+                        if (this.globalStaffIndex > 0) {
+                            // don't add duplicate TempoExpression
+                            if (multiTempoExpression.EntriesList[0].label.indexOf(stringTrimmed) >= 0) {
+                                return false;
                         } else {
                             break;
                         }

+ 15 - 13
src/MusicalScore/VoiceData/Expressions/MultiExpression.ts

@@ -7,6 +7,7 @@ import {MoodExpression} from "./MoodExpression";
 import {UnknownExpression} from "./UnknownExpression";
 import {AbstractExpression} from "./AbstractExpression";
 import {PlacementEnum} from "./AbstractExpression";
+import { FontStyles } from "../../../Common/Enums/FontStyles";
 
 export class MultiExpression {
 
@@ -109,19 +110,20 @@ export class MultiExpression {
         }
         return placement;
     }
-    // (*)
-    //public getFontstyleOfFirstEntry(): PSFontStyles {
-    //    let fontStyle: PSFontStyles = PSFontStyles.Regular;
-    //    if (this.expressions.length > 0) {
-    //        if (this.expressions[0].expression instanceof ContinuousDynamicExpression)
-    //            fontStyle = PSFontStyles.Italic;
-    //        else if (this.expressions[0].expression instanceof MoodExpression)
-    //            fontStyle = PSFontStyles.Italic;
-    //        else if (this.expressions[0].expression instanceof UnknownExpression)
-    //            fontStyle = PSFontStyles.Regular;
-    //    }
-    //    return fontStyle;
-    //}
+
+    public getFontstyleOfFirstEntry(): FontStyles {
+       let fontStyle: FontStyles = FontStyles.Regular;
+       if (this.expressions.length > 0) {
+           if (this.expressions[0].expression instanceof ContinuousDynamicExpression) {
+            fontStyle = FontStyles.Italic;
+           } else if (this.expressions[0].expression instanceof MoodExpression) {
+            fontStyle = FontStyles.Italic;
+           } else if (this.expressions[0].expression instanceof UnknownExpression) {
+            fontStyle = FontStyles.Regular;
+           }
+       }
+       return fontStyle;
+    }
     //public getFirstEntry(staffLine: StaffLine, graphLabel: GraphicalLabel): AbstractGraphicalExpression {
     //    let indexOfFirstNotInstDynExpr: number = 0;
     //    if (this.expressions[0].expression instanceof InstantaneousDynamicExpression)

+ 14 - 0
src/MusicalScore/VoiceData/Expressions/MultiTempoExpression.ts

@@ -81,6 +81,10 @@ export class MultiTempoExpression {
     //    return undefined;
     //}
     public addExpression(abstractTempoExpression: AbstractTempoExpression, prefix: string): void {
+        if (this.checkIfAlreadyExists(abstractTempoExpression)) {
+            return;
+        }
+
         if (abstractTempoExpression instanceof InstantaneousTempoExpression) {
             this.instantaneousTempo = <InstantaneousTempoExpression>abstractTempoExpression;
         } else if (abstractTempoExpression instanceof ContinuousTempoExpression) {
@@ -102,6 +106,16 @@ export class MultiTempoExpression {
             return 0;
         }
     }
+
+    private checkIfAlreadyExists(abstractTempoExpression: AbstractTempoExpression ): boolean {
+        for (const entry of this.expressions) {
+            if (entry.label === abstractTempoExpression.Label) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
 
 export class TempoExpressionEntry {

+ 0 - 10
test/data/OSMD_function_test_Ornaments.xml

@@ -83,16 +83,6 @@
           <line>2</line>
           </clef>
         </attributes>
-      <direction placement="above">
-        <direction-type>
-          <words default-y="40.00" relative-x="-75.25" relative-y="19.26" font-weight="bold" font-size="12">Ornaments</words>
-          </direction-type>
-        </direction>
-      <direction placement="above">
-        <direction-type>
-          <words default-y="40.00" relative-x="-75.25" relative-y="19.26" font-weight="bold" font-size="12">Ornaments</words>
-          </direction-type>
-        </direction>
       <note default-x="137.53" default-y="-20.00">
         <pitch>
           <step>B</step>

+ 0 - 5
test/data/OSMD_function_test_expressions.musicxml

@@ -98,11 +98,6 @@
         </direction>
       <direction placement="above">
         <direction-type>
-          <words default-y="40.00">Notenzeilentext</words>
-          </direction-type>
-        </direction>
-      <direction placement="above">
-        <direction-type>
           <octave-shift type="down" size="8" number="1" default-y="30.00"/>
           </direction-type>
         </direction>