Преглед на файлове

merge branch develop into master

sschmidTU преди 6 години
родител
ревизия
b8b2f529b5
променени са 62 файла, в които са добавени 5227 реда и са изтрити 1195 реда
  1. 5 0
      AUTHORS
  2. 4 0
      README.md
  3. 4 0
      demo/demo.css
  4. 9 2
      demo/index.html
  5. 60 74
      demo/index.js
  6. 25 12
      external/vexflow/vexflow.d.ts
  7. 2 2
      package.json
  8. 34 0
      src/MusicalScore/Graphical/AbstractGraphicalExpression.ts
  9. 97 0
      src/MusicalScore/Graphical/AlignmentManager.ts
  10. 10 0
      src/MusicalScore/Graphical/BoundingBox.ts
  11. 11 0
      src/MusicalScore/Graphical/DrawingParameters.ts
  12. 94 2
      src/MusicalScore/Graphical/EngravingRules.ts
  13. 345 0
      src/MusicalScore/Graphical/GraphicalContinuousDynamicExpression.ts
  14. 25 7
      src/MusicalScore/Graphical/GraphicalInstantaneousDynamicExpression.ts
  15. 9 13
      src/MusicalScore/Graphical/GraphicalInstantaneousTempoExpression.ts
  16. 1 0
      src/MusicalScore/Graphical/GraphicalLine.ts
  17. 40 0
      src/MusicalScore/Graphical/GraphicalSlur.ts
  18. 0 21
      src/MusicalScore/Graphical/GraphicalSlurSorter.ts
  19. 13 0
      src/MusicalScore/Graphical/GraphicalVoiceEntry.ts
  20. 7 0
      src/MusicalScore/Graphical/ISqueezable.ts
  21. 476 91
      src/MusicalScore/Graphical/MusicSheetCalculator.ts
  22. 17 10
      src/MusicalScore/Graphical/MusicSheetDrawer.ts
  23. 0 2
      src/MusicalScore/Graphical/SkyBottomLineCalculator.ts
  24. 11 3
      src/MusicalScore/Graphical/StaffLine.ts
  25. 2 2
      src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts
  26. 26 0
      src/MusicalScore/Graphical/VexFlow/VexFlowContinuousDynamicExpression.ts
  27. 29 12
      src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
  28. 5 3
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts
  29. 1 0
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts
  30. 6 17
      src/MusicalScore/Graphical/VexFlow/VexFlowInstantaneousDynamicExpression.ts
  31. 244 18
      src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts
  32. 389 467
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts
  33. 59 43
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts
  34. 1 1
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts
  35. 3 0
      src/MusicalScore/Graphical/VexFlow/VexFlowVoiceEntry.ts
  36. 98 67
      src/MusicalScore/ScoreIO/InstrumentReader.ts
  37. 20 0
      src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts
  38. 3 3
      src/MusicalScore/ScoreIO/MusicSymbolModules/ChordSymbolReader.ts
  39. 12 9
      src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts
  40. 37 10
      src/MusicalScore/ScoreIO/VoiceGenerator.ts
  41. 34 0
      src/MusicalScore/VoiceData/Arpeggio.ts
  42. 28 4
      src/MusicalScore/VoiceData/Expressions/AbstractExpression.ts
  43. 3 4
      src/MusicalScore/VoiceData/Expressions/AbstractTempoExpression.ts
  44. 1 3
      src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression.ts
  45. 13 13
      src/MusicalScore/VoiceData/Expressions/InstantaneousDynamicExpression.ts
  46. 1 3
      src/MusicalScore/VoiceData/Expressions/MoodExpression.ts
  47. 2 4
      src/MusicalScore/VoiceData/Expressions/UnknownExpression.ts
  48. 3 0
      src/MusicalScore/VoiceData/Instructions/TechnicalInstruction.ts
  49. 26 3
      src/MusicalScore/VoiceData/Note.ts
  50. 9 5
      src/MusicalScore/VoiceData/NoteHead.ts
  51. 38 8
      src/MusicalScore/VoiceData/VoiceEntry.ts
  52. 35 9
      src/OpenSheetMusicDisplay/OSMDOptions.ts
  53. 165 92
      src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts
  54. 10 0
      src/Util/CollectionUtil.ts
  55. 1 1
      test/Util/TestUtils.ts
  56. 66 66
      test/data/JohannSebastianBach_PraeludiumInCDur_BWV846_1.xml
  57. 366 14
      test/data/OSMD_function_test_all.xml
  58. 1669 0
      test/data/OSMD_function_test_autobeam.musicxml
  59. 40 12
      test/data/OSMD_function_test_expressions.musicxml
  60. 365 0
      test/data/OSMD_function_test_expressions_overlap.musicxml
  61. 115 59
      test/data/OSMD_function_test_noteHeadShapes.musicxml
  62. 3 4
      webpack.sourcemap.js

+ 5 - 0
AUTHORS

@@ -3,3 +3,8 @@ Oliver Hörbinger <o.hoerbinger@phonicscore.com> ()
 Matthias Uiberacker <m.uiberacker@phonicscore.com> ()
 Sebastian Haas <s.haas@phonicscore.com> (http://sebastianhaas.at)
 Benjamin Giesinger <benjamin.giesinger@gmail.com> (http://benjamingiesinger.de)
+Simon Schmid <s.schmid@phonicscore.com> (https://github.com/sschmidTU)
+
+Contributors:
+Pieter Hartzer (https://github.com/PieterHartzer) (https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/pull/327)
+(https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/graphs/contributors)

+ 4 - 0
README.md

@@ -1,4 +1,5 @@
 <img alt="OSMD logo" src="https://osmd.org/wp-content/uploads/2016/05/OSMD_3_icon_only.svg" width="200"/>
+<!--img alt="Brought to you by PhonicScore" src="https://phonicscore.com/neu/wp-content/uploads/2018/06/phonicscore_brown.svg"/-->
 
 # OpenSheetMusicDisplay
 
@@ -24,6 +25,9 @@ See the [Wiki](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/wi
 
 Try the [Demo](https://opensheetmusicdisplay.github.io/demo/) to see what OSMD can do.
 
+Brought to you by [PhonicScore](https://phonicscore.com/) and [our Github Contributors](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/graphs/contributors)<br>
+(Creators of [PracticeBird for iOS](https://itunes.apple.com/us/app/practice-bird-pro/id1253492926?ls=1&mt=8) and [PhonicScore for Android](https://play.google.com/store/apps/details?id=phonicscore.phonicscore_lite))
+
 
 <!--# <a name="license"></a>License
 The MIT License (MIT)

+ 4 - 0
demo/demo.css

@@ -12,6 +12,10 @@ select {
   width: 320px;
 }
 
+#selectBounding {
+  width: 80%;
+}
+
 .bignum {
   width: 1em;
   text-align: center;

+ 9 - 2
demo/index.html

@@ -89,6 +89,7 @@
             <option value="VexFlowStaffLine">StaffLines</option>
             <option value="SystemLine">SystemLines</option>
             <option value="StaffLineActivitySymbol">ActivitySymbols</option>
+            <option value="VexFlowContinuousDynamicExpression">DynamicExpressions</option>
         </select>
     </div>
     <div class="column">
@@ -110,8 +111,14 @@
     </div>
     <div class="column">
         <h3 class="ui header">Debug controls:</h3>
-        <div class="ui vertical buttons">
-            <div class="ui button" id="debug-re-render-btn">Re-render</div>
+        <div>
+            <div class="ui vertical buttons">
+                <div class="ui button" id="debug-re-render-btn">Re-render</div>
+                
+            </div>
+            <div class="ui vertical buttons">
+                <div class="ui button" id="debug-clear-btn"">Clear</div>
+            </div>
         </div>
     </div>
 </div>

+ 60 - 74
demo/index.js

@@ -4,8 +4,6 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
 (function () {
     "use strict";
     var openSheetMusicDisplay;
-    var sampleLoaded = false;
-    // folder of the sample files
     var sampleFolder = process.env.STATIC_FILES_SUBFOLDER ? process.env.STATIC_FILES_SUBFOLDER + "/" : "",
     samples = {
         "Beethoven, L.v. - An die ferne Geliebte": "Beethoven_AnDieFerneGeliebte.xml",
@@ -24,12 +22,14 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         "Mozart, W.A.- Clarinet Quintet (Excerpt)": "Mozart_Clarinet_Quintet_Excerpt.mxl",
         "Mozart/Holzer - Land der Berge (national anthem of Austria)": "Land_der_Berge.musicxml",
         "OSMD Function Test - All": "OSMD_function_test_all.xml",
-        "OSMD Function Test - Grace Notes": "OSMD_function_test_GraceNotes.xml",
-        "OSMD Function Test - Ornaments": "OSMD_function_test_Ornaments.xml",
+        "OSMD Function Test - Autobeam": "OSMD_function_test_autobeam.musicxml",
         "OSMD Function Test - Accidentals": "OSMD_function_test_accidentals.musicxml",
+        "OSMD Function Test - Drumset": "OSMD_function_test_drumset.musicxml",
         "OSMD Function Test - Expressions": "OSMD_function_test_expressions.musicxml",
+        "OSMD Function Test - Expressions Overlap": "OSMD_function_test_expressions_overlap.musicxml",
+        "OSMD Function Test - Grace Notes": "OSMD_function_test_GraceNotes.xml",
         "OSMD Function Test - NoteHeadShapes": "OSMD_function_test_noteHeadShapes.musicxml",
-        "OSMD Function Test - Drumset": "OSMD_function_test_drumset.musicxml",
+        "OSMD Function Test - Ornaments": "OSMD_function_test_Ornaments.xml",
         "Schubert, F. - An Die Musik": "Schubert_An_die_Musik.xml",
         "Actor, L. - Prelude (Sample)": "ActorPreludeSample.xml",
         "Anonymous - Saltarello": "Saltarello.mxl",
@@ -58,7 +58,8 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     showCursorBtn,
     hideCursorBtn,
     backendSelect,
-    debugReRenderBtn;
+    debugReRenderBtn,
+    debugClearBtn;
 
     // Initialization code
     function init() {
@@ -81,7 +82,7 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         hideCursorBtn = document.getElementById("hide-cursor-btn");
         backendSelect = document.getElementById("backend-select");
         debugReRenderBtn = document.getElementById("debug-re-render-btn");
-
+        debugClearBtn = document.getElementById("debug-clear-btn");
 
         // Hide error
         error();
@@ -96,7 +97,9 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             selectSample.appendChild(option);
         }
         selectSample.onchange = selectSampleOnChange;
-        selectBounding.onchange = selectBoundingOnChange;
+        if (selectBounding) {
+            selectBounding.onchange = selectBoundingOnChange;
+        }
 
         // Pre-select default music piece
 
@@ -112,60 +115,59 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             scale();
         };
 
-        skylineDebug.onclick = function() {
-            openSheetMusicDisplay.DrawSkyLine = !openSheetMusicDisplay.DrawSkyLine;
+        if (skylineDebug) {
+            skylineDebug.onclick = function() {
+                openSheetMusicDisplay.DrawSkyLine = !openSheetMusicDisplay.DrawSkyLine;
+            }
         }
 
-        bottomlineDebug.onclick = function() {
-            openSheetMusicDisplay.DrawBottomLine = !openSheetMusicDisplay.DrawBottomLine;
+        if (bottomlineDebug) {
+            bottomlineDebug.onclick = function() {
+                openSheetMusicDisplay.DrawBottomLine = !openSheetMusicDisplay.DrawBottomLine;
+            }
         }
 
-        debugReRenderBtn.onclick = function() {
-            rerender();
+        if (debugReRenderBtn) {
+            debugReRenderBtn.onclick = function() {
+                rerender();
+            }
+        }
+
+        if (debugClearBtn) {
+            debugClearBtn.onclick = function() {
+                openSheetMusicDisplay.clear();
+            }
         }
 
         // Create OSMD object and canvas
         openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, {
             autoResize: true,
             backend: backendSelect.value,
-            drawingParameters: "default", // try compact (instead of default)
             disableCursor: false,
+            drawingParameters: "default", // try compact (instead of default)
             drawPartNames: true, // try false
             // drawTitle: false,
             // drawSubtitle: false,
+            drawFingerings: true,
+            fingeringPosition: "auto", // left is default. try right. experimental: auto, above, below.
+            // fingeringInsideStafflines: "true", // default: false. true draws fingerings directly above/below notes
+            setWantedStemDirectionByXml: true, // try false, which was previously the default behavior
+
+            autoBeam: false, // try true, OSMD Function Test AutoBeam sample
+            autoBeamOptions: {
+                beam_rests: false,
+                beam_middle_rests_only: false,
+                //groups: [[3,4], [1,1]],
+                maintain_stem_directions: false
+            },
 
-            // tupletsBracketed: true,
+            // tupletsBracketed: true, // creates brackets for all tuplets except triplets, even when not set by xml
             // tripletsBracketed: true,
             // tupletsRatioed: true, // unconventional; renders ratios for tuplets (3:2 instead of 3 for triplets)
         });
         openSheetMusicDisplay.setLogLevel('info');
         document.body.appendChild(canvas);
 
-        // Set resize event handler
-        new Resize(
-            function(){
-                if (!sampleLoaded) {
-                    return;
-                }
-
-                disable();
-                },
-            function(){
-                if (!sampleLoaded) {
-                    return;
-                }
-
-                var width = document.body.clientWidth;
-                canvas.width = width;
-                try {
-                    openSheetMusicDisplay.render();
-                } catch (e) {
-                    errorLoadingOrRenderingSheet(e, "rendering");
-                }
-                enable();
-            }
-        );
-
         window.addEventListener("keydown", function(e) {
             var event = window.event ? window.event : e;
             if (event.keyCode === 39) {
@@ -191,40 +193,21 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
 
         backendSelect.addEventListener("change", function(e) {
             var value = e.target.value;
-            // clears the canvas element
-            canvas.innerHTML = "";
-            openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, false, value);
-            openSheetMusicDisplay.setLogLevel('info');
-            selectSampleOnChange();
+            var createNewOsmd = true;
 
-        });
-    }
-
-    function Resize(startCallback, endCallback) {
-      var rtime;
-      var timeout = false;
-      var delta = 200;
-
-      function resizeEnd() {
-        timeout = window.clearTimeout(timeout);
-        if (new Date() - rtime < delta) {
-          timeout = setTimeout(resizeEnd, delta);
-        } else {
-          endCallback();
-        }
-      }
+            if (createNewOsmd) {
+                // clears the canvas element
+                canvas.innerHTML = "";
+                openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, {backend: value});
+                openSheetMusicDisplay.setLogLevel('info');
+            } else {
+                // alternative, doesn't work yet, see setOptions():
+                openSheetMusicDisplay.setOptions({backend: value});
+            }
 
-      window.addEventListener("resize", function () {
-        rtime = new Date();
-        if (!timeout) {
-          startCallback();
-          rtime = new Date();
-          timeout = window.setTimeout(resizeEnd, delta);
-        }
-      });
+            selectSampleOnChange();
 
-      window.setTimeout(startCallback, 0);
-      window.setTimeout(endCallback, 1);
+        });
     }
 
     function selectBoundingOnChange(evt) {
@@ -270,7 +253,6 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     }
 
     function onLoadingEnd(isCustom) {
-        sampleLoaded = true;
         // Remove option from select
         if (!isCustom && custom.parentElement === selectSample) {
             selectSample.removeChild(custom);
@@ -295,7 +277,11 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     function rerender() {
         disable();
         window.setTimeout(function(){
-            openSheetMusicDisplay.render();
+            if (openSheetMusicDisplay.IsReadyToRender()) {
+                openSheetMusicDisplay.render();
+            } else {
+                selectSampleOnChange(); // reload sample e.g. after osmd.clear()
+            }
             enable();
         }, 0);
     }

+ 25 - 12
external/vexflow/vexflow.d.ts

@@ -4,6 +4,7 @@ declare namespace Vex {
 
     export module Flow {
         const RESOLUTION: any;
+        const DEFAULT_NOTATION_FONT_SCALE: number;
 
         export class Formatter {
             constructor();
@@ -67,6 +68,7 @@ declare namespace Vex {
         }
 
         export class Note extends Tickable {
+            public addStroke(index: number, stroke: Stroke): void;
         }
 
         export class TextBracket {
@@ -127,6 +129,8 @@ declare namespace Vex {
         }
 
         export class GraceNote extends StaveNote {
+            static SCALE: number;
+            static LEDGER_LINE_OFFSET: number;
             constructor(note_struct: any);
         }
 
@@ -219,6 +223,22 @@ declare namespace Vex {
             public getPosition(): number;
 
             public setPosition(position: number): Modifier;
+
+            public setIndex(index: number): void;
+        }
+
+        export class FretHandFinger extends Modifier {
+            constructor(finger: string);
+        }
+
+        export class StringNumber extends Modifier {
+            constructor(string: string);
+            setOffsetY(value: number);
+        }
+        
+        export class Stroke extends Modifier {
+            constructor(type: number);
+            public static Type: any; // unreliable values, use Arpeggio.ArpeggioType instead
         }
 
         export class NoteSubGroup extends Modifier {
@@ -226,17 +246,6 @@ declare namespace Vex {
         }
 
         export class StaveModifier extends Modifier {
-            // public static get Position() {
-            //     return {
-            //         LEFT: 1,
-            //         RIGHT: 2,
-            //         ABOVE: 3,
-            //         BELOW: 4,
-            //         BEGIN: 5,
-            //         END: 6,
-            //     };
-            // }
-
             public getPosition(): number;
 
         }
@@ -310,8 +319,12 @@ declare namespace Vex {
             constructor(notes: StaveNote[], auto_stem: boolean);
 
             public setContext(ctx: RenderContext): Beam;
-
             public draw(): void;
+            public static generateBeams(notes: Vex.Flow.StemmableNote[], optionsObject?: any): Beam[];
+        }
+
+        export class Fraction { // Vex.Flow.Fraction, used for generateBeams
+            constructor(nominator: number, denominator: number);
         }
 
         export class Tuplet {

+ 2 - 2
package.json

@@ -54,7 +54,7 @@
     "loglevel": "^1.5.0",
     "shortid": "^2.2.6",
     "typescript-collections": "^1.1.2",
-    "vexflow": "^1.2.85"
+    "vexflow": "^1.2.87"
   },
   "devDependencies": {
     "@types/chai": "^4.0.3",
@@ -71,7 +71,7 @@
     "eslint-plugin-node": "^7.0.1",
     "eslint-plugin-promise": "^4.0.1",
     "eslint-plugin-standard": "^4.0.0",
-    "file-loader": "^1.1.8",
+    "file-loader": "^2.0.0",
     "html-webpack-plugin": "^3.2.0",
     "http-server": "^0.11.0",
     "jquery": "^3.2.1",

+ 34 - 0
src/MusicalScore/Graphical/AbstractGraphicalExpression.ts

@@ -0,0 +1,34 @@
+import { GraphicalObject } from "./GraphicalObject";
+import { GraphicalLabel } from "./GraphicalLabel";
+import { StaffLine } from "./StaffLine";
+import { BoundingBox } from "./BoundingBox";
+import { AbstractExpression, PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
+import { EngravingRules } from "./EngravingRules";
+
+export abstract class AbstractGraphicalExpression extends GraphicalObject {
+    protected label: GraphicalLabel;
+    protected parentStaffLine: StaffLine;
+    /** Internal cache of read expression */
+    protected expression: AbstractExpression;
+    /** EngravingRules for positioning */
+    protected rules: EngravingRules = EngravingRules.Rules;
+
+    constructor(parentStaffline: StaffLine, expression: AbstractExpression) {
+        super();
+        this.expression = expression;
+        this.boundingBox = new BoundingBox(this, parentStaffline.PositionAndShape);
+        this.parentStaffLine = parentStaffline;
+        this.parentStaffLine.AbstractExpressions.push(this);
+    }
+
+    /** Graphical label of the expression if available */
+    get Label(): GraphicalLabel { return this.label; }
+    /** Staffline where the expression is attached to */
+    public get ParentStaffLine(): StaffLine { return this.parentStaffLine; }
+    public get SourceExpression(): AbstractExpression { return this.expression; }
+    public get Placement(): PlacementEnum { return this.expression.Placement; }
+
+    //#region abstract methods
+    public abstract updateSkyBottomLine(): void;
+    //#endregion
+}

+ 97 - 0
src/MusicalScore/Graphical/AlignmentManager.ts

@@ -0,0 +1,97 @@
+import { StaffLine } from "./StaffLine";
+import { BoundingBox } from "./BoundingBox";
+import { VexFlowContinuousDynamicExpression } from "./VexFlow/VexFlowContinuousDynamicExpression";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
+import { PointF2D } from "../../Common/DataObjects/PointF2D";
+import { EngravingRules } from "./EngravingRules";
+
+export class AlignmentManager {
+    private parentStaffline: StaffLine;
+    private rules: EngravingRules = EngravingRules.Rules;
+
+    constructor(staffline: StaffLine) {
+        this.parentStaffline = staffline;
+    }
+
+    public alignDynamicExpressions(): void {
+        // Find close expressions along the staffline. Group them into tuples
+        const groups: AbstractGraphicalExpression[][] = [];
+        let tmpList: AbstractGraphicalExpression[] = new Array<AbstractGraphicalExpression>();
+        for (let aeIdx: number = 0; aeIdx < this.parentStaffline.AbstractExpressions.length - 1; aeIdx++) {
+            const currentExpression: AbstractGraphicalExpression = this.parentStaffline.AbstractExpressions[aeIdx];
+            const nextExpression: AbstractGraphicalExpression = this.parentStaffline.AbstractExpressions[aeIdx + 1];
+            if (currentExpression.Placement === nextExpression.Placement) {
+                const dist: PointF2D = this.getDistance(currentExpression.PositionAndShape, nextExpression.PositionAndShape);
+                if (dist.x < this.rules.DynamicExpressionMaxDistance) {
+                    // Prevent last found expression to be added twice. e.g. p<f as three close expressions
+                    if (tmpList.indexOf(currentExpression) === -1) {
+                        tmpList.push(currentExpression);
+                    }
+                    tmpList.push(nextExpression);
+                } else {
+                    groups.push(tmpList);
+                    tmpList = new Array<AbstractGraphicalExpression>();
+                }
+            }
+        }
+        // If expressions are colliding at end, we need to add them too
+        groups.push(tmpList);
+
+        for (const aes of groups) {
+            if (aes.length > 0) {
+                // Get the median y position and shift all group members to that position
+                const centerYs: number[] = aes.map(expr => expr.PositionAndShape.Center.y);
+                const yIdeal: number = Math.max(...centerYs);
+                for (let exprIdx: number = 0; exprIdx < aes.length; exprIdx++) {
+                    const expr: AbstractGraphicalExpression = aes[exprIdx];
+                    const centerOffset: number = centerYs[exprIdx] - yIdeal;
+                    // FIXME: Expressions should not behave differently.
+                    if (expr instanceof VexFlowContinuousDynamicExpression) {
+                        (expr as VexFlowContinuousDynamicExpression).shiftYPosition(-centerOffset);
+                    } else {
+                        // TODO: The 0.8 are because the letters are a bit to far done
+                        expr.PositionAndShape.RelativePosition.y -= centerOffset * 0.8;
+                    }
+                    expr.PositionAndShape.calculateBoundingBox();
+                    // Squeeze wedges
+                    if ((expr as VexFlowContinuousDynamicExpression).squeeze) {
+                        const nextExpression: AbstractGraphicalExpression = exprIdx < aes.length - 1 ? aes[exprIdx + 1] : undefined;
+                        const prevExpression: AbstractGraphicalExpression = exprIdx > 0 ? aes[exprIdx - 1] : undefined;
+                        if (nextExpression) {
+                            const overlapRight: PointF2D = this.getOverlap(expr.PositionAndShape, nextExpression.PositionAndShape);
+                            (expr as VexFlowContinuousDynamicExpression).squeeze(-(overlapRight.x + this.rules.DynamicExpressionSpacer));
+                        }
+                        if (prevExpression) {
+                            const overlapLeft: PointF2D = this.getOverlap(prevExpression.PositionAndShape, expr.PositionAndShape);
+                            (expr as VexFlowContinuousDynamicExpression).squeeze(overlapLeft.x + this.rules.DynamicExpressionSpacer);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Get distance between two bounding boxes
+     * @param a First bounding box
+     * @param b Second bounding box
+     */
+    private getDistance(a: BoundingBox, b: BoundingBox): PointF2D {
+        const rightBorderA: number = a.RelativePosition.x + a.BorderMarginRight;
+        const leftBorderB: number = b.RelativePosition.x + b.BorderMarginLeft;
+        const bottomBorderA: number = b.RelativePosition.y + a.BorderMarginBottom;
+        const topBorderB: number = b.RelativePosition.y + b.BorderMarginTop;
+        return new PointF2D(leftBorderB - rightBorderA,
+                            topBorderB - bottomBorderA);
+    }
+
+    /**
+     * Get overlap of two bounding boxes
+     * @param a First bounding box
+     * @param b Second bounding box
+     */
+    private getOverlap(a: BoundingBox, b: BoundingBox): PointF2D {
+        return new PointF2D((a.RelativePosition.x + a.BorderMarginRight) - (b.RelativePosition.x + b.BorderMarginLeft),
+                            (a.RelativePosition.y + a.BorderMarginBottom) - (b.RelativePosition.y + b.BorderMarginTop));
+    }
+}

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

@@ -214,6 +214,16 @@ export class BoundingBox {
         return this.dataObject;
     }
 
+
+    /**
+     * Get the center of a bounding box
+     * @param boundingBox Bounding box to check
+     */
+    public get Center(): PointF2D {
+        return new PointF2D(this.RelativePosition.x + (this.BorderMarginRight + this.BorderMarginLeft),
+                            this.RelativePosition.y + (this.BorderMarginBottom + this.BorderMarginTop));
+    }
+
     public setAbsolutePositionFromParent(): void {
         if (this.parent !== undefined) {
             this.absolutePosition.x = this.parent.AbsolutePosition.x + this.relativePosition.x;

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

@@ -1,4 +1,5 @@
 import { EngravingRules } from "./EngravingRules";
+import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 
 export enum DrawingParametersEnum {
     allon = "allon",
@@ -27,6 +28,7 @@ export class DrawingParameters {
     public drawComposer: boolean = true;
     public drawCredits: boolean = true;
     public drawPartNames: boolean = true;
+    public fingeringPosition: PlacementEnum = PlacementEnum.Left;
     /** Draw notes set to be invisible (print-object="no" in XML). */
     public drawHiddenNotes: boolean = false;
     public defaultColorNoteHead: string; // TODO not yet supported
@@ -177,4 +179,13 @@ export class DrawingParameters {
         this.drawPartNames = value;
         EngravingRules.Rules.RenderInstrumentNames = value;
     }
+
+    public get FingeringPosition(): PlacementEnum {
+        return this.fingeringPosition;
+    }
+
+    public set FingeringPosition(value: PlacementEnum) {
+        this.fingeringPosition = value;
+        EngravingRules.Rules.FingeringPosition = value;
+    }
 }

+ 94 - 2
src/MusicalScore/Graphical/EngravingRules.ts

@@ -1,13 +1,17 @@
-import {PagePlacementEnum} from "./GraphicalMusicPage";
+import { PagePlacementEnum } from "./GraphicalMusicPage";
 //import {MusicSymbol} from "./MusicSymbol";
 import * as log from "loglevel";
 import { TextAlignmentEnum } from "../../Common/Enums/TextAlignment";
+import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
+import { AutoBeamOptions } from "../../OpenSheetMusicDisplay/OSMDOptions";
 
 export class EngravingRules {
     private static rules: EngravingRules;
+    /** A unit of distance. 1.0 is the distance between lines of a stave for OSMD, which is 10 pixels in Vexflow. */
     private static unit: number = 1.0;
     private samplingUnit: number;
     private staccatoShorteningFactor: number;
+    /** Height (size) of the sheet title. */
     private sheetTitleHeight: number;
     private sheetSubtitleHeight: number;
     private sheetMinimumDistanceBetweenTitleAndSubtitle: number;
@@ -36,6 +40,10 @@ export class EngravingRules {
     private betweenStaffDistance: number;
     private staffHeight: number;
     private betweenStaffLinesDistance: number;
+    /** Whether to automatically beam notes that don't already have beams in XML. */
+    private autoBeamNotes: boolean;
+    /** Options for autoBeaming like whether to beam over rests. See AutoBeamOptions interface. */
+    private autoBeamOptions: AutoBeamOptions;
     private beamWidth: number;
     private beamSpaceWidth: number;
     private beamForwardLength: number;
@@ -59,6 +67,7 @@ export class EngravingRules {
     private stemMaxLength: number;
     private beamSlopeMaxAngle: number;
     private stemMinAllowedDistanceBetweenNoteHeadAndBeamLine: number;
+    private setWantedStemDirectionByXml: boolean;
     private graceNoteScalingFactor: number;
     private graceNoteXOffset: number;
     private wedgeOpeningLength: number;
@@ -77,7 +86,7 @@ export class EngravingRules {
     private betweenDotsDistance: number;
     private ornamentAccidentalScalingFactor: number;
     private chordSymbolTextHeight: number;
-    //private chordSymbolYOffset: number;
+    private chordSymbolYOffset: number;
     private fingeringLabelFontHeight: number;
     private measureNumberLabelHeight: number;
     private measureNumberLabelOffset: number;
@@ -102,6 +111,10 @@ export class EngravingRules {
     private repetitionEndingLabelYOffset: number;
     private repetitionEndingLineYLowerOffset: number;
     private repetitionEndingLineYUpperOffset: number;
+    /** Default alignment of lyrics.
+     * Left alignments will extend text to the right of the bounding box,
+     * which facilitates spacing by extending measure width.
+     */
     private lyricsAlignmentStandard: TextAlignmentEnum;
     private lyricsHeight: number;
     private lyricsYOffsetToStaffHeight: number;
@@ -160,11 +173,18 @@ export class EngravingRules {
     private noteDistancesScalingFactors: number[] = [1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0];
     private durationDistanceDict: {[_: number]: number; } = {};
     private durationScalingDistanceDict: {[_: number]: number; } = {};
+    /** Whether to render a label for the composer of the piece at the top of the sheet. */
     private renderComposer: boolean;
     private renderTitle: boolean;
     private renderSubtitle: boolean;
     private renderLyricist: boolean;
     private renderInstrumentNames: boolean;
+    private renderFingerings: boolean;
+    private dynamicExpressionMaxDistance: number;
+    private dynamicExpressionSpacer: number;
+    /** Position of fingering label in relation to corresponding note (left, right supported, above, below experimental) */
+    private fingeringPosition: PlacementEnum;
+    private fingeringInsideStafflines: boolean;
 
     constructor() {
         // global variables
@@ -204,6 +224,14 @@ export class EngravingRules {
         this.minimumAllowedDistanceBetweenSystems = 3.0;
         this.lastSystemMaxScalingFactor = 1.4;
 
+        // autoBeam options
+        this.autoBeamNotes = false;
+        this.autoBeamOptions = {
+            beam_middle_rests_only: false,
+            beam_rests: false,
+            maintain_stem_directions: false
+        };
+
         // Beam Sizing Variables
         this.beamWidth = EngravingRules.unit / 2.0;
         this.beamSpaceWidth = EngravingRules.unit / 3.0;
@@ -235,6 +263,7 @@ export class EngravingRules {
         this.stemMaxLength = 4.5;
         this.beamSlopeMaxAngle = 10.0;
         this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine = 1.0;
+        this.setWantedStemDirectionByXml = true;
 
         // GraceNote Variables
         this.graceNoteScalingFactor = 0.6;
@@ -261,6 +290,7 @@ export class EngravingRules {
         this.betweenDotsDistance = 0.8;
         this.ornamentAccidentalScalingFactor = 0.65;
         this.chordSymbolTextHeight = 2.0;
+        this.chordSymbolYOffset = 2.0;
         this.fingeringLabelFontHeight = 1.7;
 
         // Tuplets, MeasureNumber and TupletNumber Labels
@@ -315,6 +345,8 @@ export class EngravingRules {
         this.moodTextHeight = 2.3;
         this.unknownTextHeight = 2.0;
         this.continuousTempoTextHeight = 2.3;
+        this.dynamicExpressionMaxDistance = 2;
+        this.dynamicExpressionSpacer = 0.5;
 
         // Line Widths
         this.staffLineWidth = 0.12;
@@ -349,6 +381,9 @@ export class EngravingRules {
         this.renderSubtitle = true;
         this.renderLyricist = true;
         this.renderInstrumentNames = true;
+        this.renderFingerings = true;
+        this.fingeringPosition = PlacementEnum.Left; // easier to get bounding box, and safer for vertical layout
+        this.fingeringInsideStafflines = false;
 
         this.populateDictionaries();
         try {
@@ -537,6 +572,18 @@ export class EngravingRules {
     public set BetweenStaffLinesDistance(value: number) {
         this.betweenStaffLinesDistance = value;
     }
+    public get AutoBeamNotes(): boolean {
+        return this.autoBeamNotes;
+    }
+    public set AutoBeamNotes(value: boolean) {
+        this.autoBeamNotes = value;
+    }
+    public get AutoBeamOptions(): AutoBeamOptions {
+        return this.autoBeamOptions;
+    }
+    public set AutoBeamOptions(value: AutoBeamOptions) {
+        this.autoBeamOptions = value;
+    }
     public get BeamWidth(): number {
         return this.beamWidth;
     }
@@ -681,6 +728,12 @@ export class EngravingRules {
     public set StemMinAllowedDistanceBetweenNoteHeadAndBeamLine(value: number) {
         this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine = value;
     }
+    public get SetWantedStemDirectionByXml(): boolean {
+        return this.setWantedStemDirectionByXml;
+    }
+    public set SetWantedStemDirectionByXml(value: boolean) {
+        this.setWantedStemDirectionByXml = value;
+    }
     public get GraceNoteScalingFactor(): number {
         return this.graceNoteScalingFactor;
     }
@@ -789,6 +842,12 @@ export class EngravingRules {
     public set ChordSymbolTextHeight(value: number) {
         this.chordSymbolTextHeight = value;
     }
+    public get ChordSymbolYOffset(): number {
+        return this.chordSymbolYOffset;
+    }
+    public set ChordSymbolYOffset(value: number) {
+        this.chordSymbolYOffset = value;
+    }
     public get FingeringLabelFontHeight(): number {
         return this.fingeringLabelFontHeight;
     }
@@ -1065,6 +1124,21 @@ export class EngravingRules {
     public set ContinuousTempoTextHeight(value: number) {
         this.continuousTempoTextHeight = value;
     }
+    /** Distance of expressions inside a group */
+    public get DynamicExpressionMaxDistance(): number {
+        return this.dynamicExpressionMaxDistance;
+    }
+    public set DynamicExpressionMaxDistance(value: number) {
+        this.dynamicExpressionMaxDistance = value;
+    }
+    /** Space between expressions in a group */
+    public get DynamicExpressionSpacer(): number {
+        return this.dynamicExpressionSpacer;
+    }
+    public set DynamicExpressionSpacer(value: number) {
+        this.dynamicExpressionSpacer = value;
+    }
+
     public get UnknownTextHeight(): number {
         return this.unknownTextHeight;
     }
@@ -1251,6 +1325,24 @@ export class EngravingRules {
     public set RenderInstrumentNames(value: boolean) {
         this.renderInstrumentNames = value;
     }
+    public get RenderFingerings(): boolean {
+        return this.renderFingerings;
+    }
+    public set RenderFingerings(value: boolean) {
+        this.renderFingerings = value;
+    }
+    public get FingeringPosition(): PlacementEnum {
+        return this.fingeringPosition;
+    }
+    public set FingeringPosition(value: PlacementEnum) {
+        this.fingeringPosition = value;
+    }
+    public get FingeringInsideStafflines(): boolean {
+        return this.fingeringInsideStafflines;
+    }
+    public set FingeringInsideStafflines(value: boolean) {
+        this.fingeringInsideStafflines = value;
+    }
 
     /**
      * This method maps NoteDurations to Distances and DistancesScalingFactors.

+ 345 - 0
src/MusicalScore/Graphical/GraphicalContinuousDynamicExpression.ts

@@ -0,0 +1,345 @@
+import { GraphicalLine } from "./GraphicalLine";
+import { StaffLine } from "./StaffLine";
+import { GraphicalMeasure } from "./GraphicalMeasure";
+import { ContDynamicEnum, ContinuousDynamicExpression } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
+import { PointF2D } from "../../Common/DataObjects/PointF2D";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
+import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
+import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
+import { ISqueezable } from "./ISqueezable";
+import * as log from "loglevel";
+
+/**
+ * This class prepares the graphical elements for a continuous expression. It calculates the wedges and
+ * wrappings if they are split over system breaks.
+ */
+export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpression implements ISqueezable {
+    /** True if expression is split over system borders */
+    private isSplittedPart: boolean;
+    /** True if this expression should not be removed if re-rendered */
+    private notToBeRemoved: boolean;
+    /** Holds the line objects that can be drawn via implementation */
+    private lines: GraphicalLine[] = [];
+    private startMeasure: GraphicalMeasure;
+    private endMeasure: GraphicalMeasure;
+
+    /**
+     * Create a new instance of the GraphicalContinuousDynamicExpression
+     * @param continuousDynamic The continuous dynamic instruction read via ExpressionReader
+     * @param staffLine The staffline where the exoression is attached
+     */
+    constructor(continuousDynamic: ContinuousDynamicExpression, staffLine: StaffLine) {
+        super(staffLine, continuousDynamic);
+
+        this.isSplittedPart = false;
+        this.notToBeRemoved = false;
+    }
+
+    //#region Getter / Setter
+
+    /** The graphical measure where the parent continuous dynamic expression starts */
+    public get StartMeasure(): GraphicalMeasure { return this.startMeasure; }
+    public set StartMeasure(value: GraphicalMeasure) { this.startMeasure = value; }
+    /** The graphical measure where the parent continuous dynamic expression ends */
+    public get EndMeasure(): GraphicalMeasure { return this.endMeasure; }
+    public set EndMeasure(value: GraphicalMeasure) { this.endMeasure = value; }
+    /** The staff lin where the graphical dynamic expressions ends */
+    public get EndStaffLine(): StaffLine { return this.endMeasure ? this.endMeasure.ParentStaffLine : undefined; }
+    /**  Is true if this continuous expression is a wedge, that reaches over a system border and needs to be split into two. */
+    public get IsSplittedPart(): boolean { return this.isSplittedPart; }
+    public set IsSplittedPart(value: boolean) { this.isSplittedPart = value; }
+    /**  Is true if the dynamic is not a symbol but a text instruction. E.g. "decrescendo" */
+    public get IsVerbal(): boolean { return this.ContinuousDynamic.Label !== undefined && this.ContinuousDynamic.Label.length > 0; }
+    /** True if this expression should not be removed if re-rendered */
+    public get NotToBeRemoved(): boolean { return this.notToBeRemoved; }
+    public set NotToBeRemoved(value: boolean) { this.notToBeRemoved = value; }
+    /** Holds the line objects that can be drawn via implementation */
+    public get Lines(): GraphicalLine[] { return this.lines; }
+
+    public get ContinuousDynamic(): ContinuousDynamicExpression { return this.SourceExpression as ContinuousDynamicExpression; }
+    //#endregion
+
+    //#region Public methods
+
+    public updateSkyBottomLine(): void {
+        // update Sky-BottomLine
+        const skyBottomLineCalculator: SkyBottomLineCalculator = this.parentStaffLine.SkyBottomLineCalculator;
+        const left: number = this.IsVerbal ? this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginLeft : 0;
+        const right: number = this.IsVerbal ? this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginRight : 0;
+        if (!this.IsVerbal && this.lines.length < 2) {
+            log.warn("Not enough lines for SkyBottomLine calculation");
+        }
+        switch (this.Placement) {
+            case PlacementEnum.Above:
+                if (!this.IsVerbal) {
+                    skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
+                } else {
+                    const yValue: number = this.label.PositionAndShape.BorderMarginTop + this.label.PositionAndShape.RelativePosition.y;
+                    skyBottomLineCalculator.updateSkyLineInRange(left, right, yValue);
+                }
+                break;
+            case PlacementEnum.Below:
+                if (!this.IsVerbal) {
+                    skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].Start, this.lines[1].End);
+                } else {
+                    const yValue: number = this.label.PositionAndShape.BorderMarginBottom + this.label.PositionAndShape.RelativePosition.y;
+                    skyBottomLineCalculator.updateBottomLineInRange(left, right, yValue);
+                }
+                break;
+            default:
+                log.error("Placement for GraphicalContinuousDynamicExpression is unknown");
+        }
+    }
+
+    /**
+     * Calculate crescendo lines for (full).
+     * @param startX left most starting point
+     * @param endX right mist ending point
+     * @param y y placement
+     * @param wedgeOpeningLength length of the opening
+     * @param wedgeLineWidth line width of the wedge
+     */
+    public createCrescendoLines(startX: number, endX: number, y: number,
+                                wedgeOpeningLength: number = this.rules.WedgeOpeningLength, wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
+        const lineStart: PointF2D = new PointF2D(startX, y);
+        const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeOpeningLength / 2);
+        const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeOpeningLength / 2);
+        this.addWedgeLines(lineStart, upperLineEnd, lowerLineEnd, wedgeLineWidth);
+    }
+
+    /**
+     * Calculate crescendo lines for system break (first part).
+     * @param startX left most starting point
+     * @param endX right mist ending point
+     * @param y y placement
+     * @param wedgeMeasureEndOpeningLength length of opening at measure end
+     * @param wedgeOpeningLength length of the opening
+     * @param wedgeLineWidth line width of the wedge
+     */
+    public createFirstHalfCrescendoLines(startX: number, endX: number, y: number,
+                                         wedgeMeasureEndOpeningLength: number = this.rules.WedgeMeasureEndOpeningLength,
+                                         wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
+        const lineStart: PointF2D = new PointF2D(startX, y);
+        const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeMeasureEndOpeningLength / 2);
+        const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeMeasureEndOpeningLength / 2);
+        this.addWedgeLines(lineStart, upperLineEnd, lowerLineEnd, wedgeLineWidth);
+    }
+
+
+    /**
+     * Calculate crescendo lines for system break (second part).
+     * @param startX left most starting point
+     * @param endX right mist ending point
+     * @param y y placement
+     * @param wedgeMeasureBeginOpeningLength length of opening at measure start
+     * @param wedgeOpeningLength length of the opening
+     * @param wedgeLineWidth line width of the wedge
+     */
+    public createSecondHalfCrescendoLines(startX: number, endX: number, y: number,
+                                          wedgeMeasureBeginOpeningLength: number = this.rules.WedgeMeasureBeginOpeningLength,
+                                          wedgeOpeningLength: number = this.rules.WedgeOpeningLength,
+                                          wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
+        const upperLineStart: PointF2D = new PointF2D(startX, y - wedgeMeasureBeginOpeningLength / 2);
+        const lowerLineStart: PointF2D = new PointF2D(startX, y + wedgeMeasureBeginOpeningLength / 2);
+        const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeOpeningLength / 2);
+        const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeOpeningLength / 2);
+        this.addDoubleLines(upperLineStart, upperLineEnd, lowerLineStart, lowerLineEnd, wedgeLineWidth);
+    }
+
+    /**
+     * This method recalculates the Crescendo Lines (for all cases).
+     * @param startX left most starting point
+     * @param endX right most ending point
+     * @param y y placement
+     */
+    public recalculateCrescendoLines(startX: number, endX: number, y: number): void {
+        const isSecondHalfSplit: boolean = Math.abs(this.lines[0].Start.y - this.lines[1].Start.y) > 0.0001;
+        this.lines.clear();
+
+        if (isSecondHalfSplit) {
+            this.createSecondHalfCrescendoLines(startX, endX, y);
+        } else if (this.isSplittedPart) {
+            this.createFirstHalfCrescendoLines(startX, endX, y);
+        } else {
+            this.createCrescendoLines(startX, endX, y);
+        }
+    }
+
+    /**
+     * Calculate diminuendo lines for system break (full).
+     * @param startX left most starting point
+     * @param endX right mist ending point
+     * @param y y placement
+     * @param wedgeOpeningLength length of the opening
+     * @param wedgeLineWidth line width of the wedge
+     */
+    public createDiminuendoLines(startX: number, endX: number, y: number,
+                                 wedgeOpeningLength: number = this.rules.WedgeOpeningLength, wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
+        const upperWedgeStart: PointF2D = new PointF2D(startX, y - wedgeOpeningLength / 2);
+        const lowerWedgeStart: PointF2D = new PointF2D(startX, y + wedgeOpeningLength / 2);
+        const wedgeEnd: PointF2D = new PointF2D(endX, y);
+        this.addWedgeLines(wedgeEnd, upperWedgeStart, lowerWedgeStart, wedgeLineWidth);
+    }
+
+    /**
+     * Calculate diminuendo lines for system break (first part).
+     * @param startX left most starting point
+     * @param endX right mist ending point
+     * @param y y placement
+     * @param wedgeOpeningLength length of the opening
+     * @param wedgeMeasureEndOpeningLength length of opening at measure end
+     * @param wedgeLineWidth line width of the wedge
+     */
+    public createFirstHalfDiminuendoLines(startX: number, endX: number, y: number,
+                                          wedgeOpeningLength: number = this.rules.WedgeOpeningLength,
+                                          wedgeMeasureEndOpeningLength: number = this.rules.WedgeMeasureEndOpeningLength,
+                                          wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
+        const upperLineStart: PointF2D = new PointF2D(startX, y - wedgeOpeningLength / 2);
+        const lowerLineStart: PointF2D = new PointF2D(startX, y + wedgeOpeningLength / 2);
+        const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeMeasureEndOpeningLength / 2);
+        const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeMeasureEndOpeningLength / 2);
+        this.addDoubleLines(upperLineStart, upperLineEnd, lowerLineStart, lowerLineEnd, wedgeLineWidth);
+    }
+
+    /**
+     * Calculate diminuendo lines for system break (second part).
+     * @param startX left most starting point
+     * @param endX right mist ending point
+     * @param y y placement
+     * @param wedgeMeasureBeginOpeningLength length of opening at measure start
+     * @param wedgeLineWidth line width of the wedge
+     */
+    public createSecondHalfDiminuendoLines(startX: number, endX: number, y: number,
+                                           wedgeMeasureBeginOpeningLength: number = this.rules.WedgeMeasureBeginOpeningLength,
+                                           wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
+        const upperLineStart: PointF2D = new PointF2D(startX, y - wedgeMeasureBeginOpeningLength / 2);
+        const lowerLineStart: PointF2D = new PointF2D(startX, y + wedgeMeasureBeginOpeningLength / 2);
+        const lineEnd: PointF2D = new PointF2D(endX, y);
+        this.addWedgeLines(lineEnd, upperLineStart, lowerLineStart, wedgeLineWidth);
+    }
+
+    /**
+     * This method recalculates the diminuendo lines (for all cases).
+     * @param startX left most starting point
+     * @param endX right most ending point
+     * @param y y placement
+     */
+    public recalculateDiminuendoLines(startX: number, endX: number, yPosition: number): void {
+        const isFirstHalfSplit: boolean = Math.abs(this.lines[0].End.y - this.lines[1].End.y) > 0.0001;
+        this.lines.clear();
+        if (isFirstHalfSplit) {
+            this.createFirstHalfDiminuendoLines(startX, endX, yPosition);
+        } else if (this.isSplittedPart) {
+            this.createSecondHalfDiminuendoLines(startX, endX, yPosition);
+        } else {
+            this.createDiminuendoLines(startX, endX, yPosition);
+        }
+    }
+
+    /**
+     * Calculate the BoundingBox (as a box around the Wedge).
+     */
+    public calcPsi(): void {
+        if (this.IsVerbal) {
+            this.PositionAndShape.calculateBoundingBox();
+            return;
+        }
+        this.PositionAndShape.RelativePosition = this.lines[0].Start;
+        this.PositionAndShape.BorderMarginTop = this.lines[0].End.y - this.lines[0].Start.y;
+        this.PositionAndShape.BorderMarginBottom = this.lines[1].End.y - this.lines[1].Start.y;
+
+        if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+            this.PositionAndShape.BorderMarginLeft = 0;
+            this.PositionAndShape.BorderMarginRight = this.lines[0].End.x - this.lines[0].Start.x;
+        } else {
+            this.PositionAndShape.BorderMarginLeft = this.lines[0].End.x - this.lines[0].Start.x;
+            this.PositionAndShape.BorderMarginRight = 0;
+        }
+    }
+
+    /**
+     * Clear Lines
+     */
+    public cleanUp(): void {
+        this.lines.clear();
+    }
+
+    /**
+     * Shift wedge in y position
+     * @param shift Number to shift
+     */
+    public shiftYPosition(shift: number): void {
+        if (this.IsVerbal) {
+            this.PositionAndShape.RelativePosition.y += shift;
+            this.PositionAndShape.calculateBoundingBox();
+        } else {
+            this.lines[0].Start.y += shift;
+            this.lines[0].End.y += shift;
+            this.lines[1].End.y += shift;
+        }
+    }
+
+    public squeeze(value: number): void {
+        // Verbal expressions are not squeezable and squeezing below the width is also not possible
+        if (this.IsVerbal) {
+            return;
+        }
+        const width: number = Math.abs(this.lines[0].End.x - this.lines[0].Start.x);
+        if (width < Math.abs(value)) {
+            return;
+        }
+        if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+            if (value > 0) {
+                this.lines[0].Start.x += value;
+            } else {
+                this.lines[0].End.x += value;
+                this.lines[1].End.x += value;
+            }
+        } else {
+            if (value < 0) {
+                this.lines[0].Start.x += value;
+            } else {
+                this.lines[0].End.x += value;
+                this.lines[1].End.x += value;
+            }
+        }
+        this.calcPsi();
+    }
+
+    //#endregion
+
+    //#region Private methods
+
+    /**
+     * Create lines from points and add them to the memory
+     * @param wedgePoint start of the expression
+     * @param upperWedgeEnd end of the upper line
+     * @param lowerWedgeEnd end of lower line
+     * @param wedgeLineWidth line width
+     */
+    private addWedgeLines(wedgePoint: PointF2D, upperWedgeEnd: PointF2D, lowerWedgeEnd: PointF2D, wedgeLineWidth: number): void {
+        const upperLine: GraphicalLine = new GraphicalLine(wedgePoint, upperWedgeEnd, wedgeLineWidth);
+        const lowerLine: GraphicalLine = new GraphicalLine(wedgePoint, lowerWedgeEnd, wedgeLineWidth);
+
+        this.lines.push(upperLine);
+        this.lines.push(lowerLine);
+    }
+
+    /**
+     * Create top and bottom lines for continuing wedges
+     * @param upperLineStart start of the upper line
+     * @param upperLineEnd end of the upper line
+     * @param lowerLineStart start of the lower line
+     * @param lowerLineEnd end of lower line
+     * @param wedgeLineWidth line width
+     */
+    private addDoubleLines(upperLineStart: PointF2D, upperLineEnd: PointF2D, lowerLineStart: PointF2D, lowerLineEnd: PointF2D, wedgeLineWidth: number): void {
+        const upperLine: GraphicalLine = new GraphicalLine(upperLineStart, upperLineEnd, wedgeLineWidth);
+        const lowerLine: GraphicalLine = new GraphicalLine(lowerLineStart, lowerLineEnd, wedgeLineWidth);
+
+        this.lines.push(upperLine);
+        this.lines.push(lowerLine);
+    }
+
+    //#endregion
+}

+ 25 - 7
src/MusicalScore/Graphical/GraphicalInstantaneousDynamicExpression.ts

@@ -1,19 +1,37 @@
-import { GraphicalObject } from "./GraphicalObject";
 import { StaffLine } from "./StaffLine";
 import { InstantaneousDynamicExpression } from "../VoiceData/Expressions/InstantaneousDynamicExpression";
 import { GraphicalMeasure } from "./GraphicalMeasure";
-import { BoundingBox } from "./BoundingBox";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
+import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
+import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
+import * as log from "loglevel";
 
-export class GraphicalInstantaneousDynamicExpression extends GraphicalObject {
+export class GraphicalInstantaneousDynamicExpression extends AbstractGraphicalExpression {
     protected mInstantaneousDynamicExpression: InstantaneousDynamicExpression;
-    protected mParentStaffLine: StaffLine;
     protected mMeasure: GraphicalMeasure;
 
     constructor(instantaneousDynamic: InstantaneousDynamicExpression, staffLine: StaffLine, measure: GraphicalMeasure) {
-        super();
-        this.boundingBox = new BoundingBox(this, staffLine.PositionAndShape);
+        super(staffLine, instantaneousDynamic);
         this.mInstantaneousDynamicExpression = instantaneousDynamic;
-        this.mParentStaffLine = staffLine;
         this.mMeasure = measure;
     }
+
+    public updateSkyBottomLine(): void {
+        const skyBottomLineCalculator: SkyBottomLineCalculator = this.parentStaffLine.SkyBottomLineCalculator;
+        const left: number = this.PositionAndShape.RelativePosition.x + this.PositionAndShape.BorderMarginLeft;
+        const right: number = this.PositionAndShape.RelativePosition.x + this.PositionAndShape.BorderMarginRight;
+        let yValue: number = 0;
+        switch (this.Placement) {
+            case PlacementEnum.Above:
+                yValue = this.PositionAndShape.RelativePosition.y + this.PositionAndShape.BorderMarginTop;
+                skyBottomLineCalculator.updateSkyLineInRange(left, right, yValue);
+                break;
+            case PlacementEnum.Below:
+                yValue = this.PositionAndShape.RelativePosition.y + this.PositionAndShape.BorderMarginBottom;
+                skyBottomLineCalculator.updateBottomLineInRange(left, right, yValue);
+                break;
+            default:
+                log.error("Placement for GraphicalInstantaneousDynamicExpression is unknown");
+        }
+    }
 }

+ 9 - 13
src/MusicalScore/Graphical/GraphicalInstantaneousTempoExpression.ts

@@ -1,25 +1,21 @@
-import { GraphicalObject } from "./GraphicalObject";
+
 import { StaffLine } from "./StaffLine";
 import { AbstractTempoExpression } from "../VoiceData/Expressions/AbstractTempoExpression";
 import { GraphicalLabel } from "./GraphicalLabel";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
 
-export class GraphicalInstantaneousTempoExpression extends GraphicalObject {
-    protected mTempoExpresssion: AbstractTempoExpression;
-    protected mParentStaffLine: StaffLine;
-    protected mLabel: GraphicalLabel;
+export class GraphicalInstantaneousTempoExpression extends AbstractGraphicalExpression {
 
     constructor(tempoExpresssion: AbstractTempoExpression, label: GraphicalLabel) {
-        super();
-        // this.boundingBox = new BoundingBox(this, staffLine.PositionAndShape);
-        this.mTempoExpresssion = tempoExpresssion;
-        this.mLabel = label;
+        super((label.PositionAndShape.Parent.DataObject as StaffLine), tempoExpresssion);
+        this.label = label;
     }
 
-    public get InstantaneousTempoExpression(): AbstractTempoExpression {
-        return this.mTempoExpresssion;
+    public get GraphicalLabel(): GraphicalLabel {
+        return this.label;
     }
 
-    public get GraphicalLabel(): GraphicalLabel {
-        return this.mLabel;
+    public updateSkyBottomLine(): void {
+        // Not implemented
     }
 }

+ 1 - 0
src/MusicalScore/Graphical/GraphicalLine.ts

@@ -1,3 +1,4 @@
+
 import {OutlineAndFillStyleEnum} from "./DrawingEnums";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
 

+ 40 - 0
src/MusicalScore/Graphical/GraphicalSlur.ts

@@ -11,6 +11,8 @@ import { Matrix2D } from "../../Common/DataObjects/Matrix2D";
 import { LinkedVoice } from "../VoiceData/LinkedVoice";
 import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry";
 import { GraphicalStaffEntry } from "./GraphicalStaffEntry";
+import { Fraction } from "../../Common/DataObjects/Fraction";
+import { StemDirectionType } from "../VoiceData/VoiceEntry";
 
 export class GraphicalSlur extends GraphicalCurve {
     // private intersection: PointF2D;
@@ -27,6 +29,28 @@ export class GraphicalSlur extends GraphicalCurve {
     public graceEnd: boolean;
 
     /**
+     * Compares the timespan of two Graphical Slurs
+     * @param x
+     * @param y
+     */
+    public static Compare (x: GraphicalSlur, y: GraphicalSlur ): number {
+        const xTimestampSpan: Fraction = Fraction.minus(x.staffEntries[x.staffEntries.length - 1].getAbsoluteTimestamp(),
+                                                        x.staffEntries[0].getAbsoluteTimestamp());
+        const yTimestampSpan: Fraction = Fraction.minus(y.staffEntries[y.staffEntries.length - 1].getAbsoluteTimestamp(),
+                                                        y.staffEntries[0].getAbsoluteTimestamp());
+
+        if (xTimestampSpan.RealValue > yTimestampSpan.RealValue) {
+            return 1;
+        }
+
+        if (yTimestampSpan.RealValue > xTimestampSpan.RealValue) {
+            return -1;
+        }
+
+        return 0;
+    }
+
+    /**
      *
      * @param rules
      */
@@ -396,6 +420,14 @@ export class GraphicalSlur extends GraphicalCurve {
                 startY = slurStartVE.PositionAndShape.RelativePosition.y + slurStartVE.PositionAndShape.BorderBottom;
             }
 
+            // If the stem points towards the starting point of the slur, shift the slur by a small amount to start (approximately) at the x-position
+            // of the notehead. Note: an exact calculation using the position of the note is too complicate for the payoff
+            if ( slurStartVE.parentVoiceEntry.StemDirection === StemDirectionType.Down && this.placement === PlacementEnum.Below ) {
+                startX -= 0.5;
+            }
+            if (slurStartVE.parentVoiceEntry.StemDirection === StemDirectionType.Up && this.placement === PlacementEnum.Above) {
+                startX += 0.5;
+            }
             // if (first.NoteStem !== undefined && first.NoteStem.Direction === StemEnum.StemUp && this.placement === PlacementEnum.Above) {
             //     startX += first.NoteStem.PositionAndShape.RelativePosition.x;
             //     startY = skyBottomLineCalculator.getSkyLineMinAtPoint(staffLine, startX);
@@ -427,6 +459,14 @@ export class GraphicalSlur extends GraphicalCurve {
                 endY = slurEndVE.PositionAndShape.RelativePosition.y + slurEndVE.PositionAndShape.BorderBottom;
             }
 
+            // If the stem points towards the endpoint of the slur, shift the slur by a small amount to start (approximately) at the x-position
+            // of the notehead. Note: an exact calculation using the position of the note is too complicate for the payoff
+            if ( slurEndVE.parentVoiceEntry.StemDirection === StemDirectionType.Down && this.placement === PlacementEnum.Below ) {
+                endX -= 0.5;
+            }
+            if (slurEndVE.parentVoiceEntry.StemDirection === StemDirectionType.Up && this.placement === PlacementEnum.Above) {
+                endX += 0.5;
+            }
             // const first: GraphicalNote = <GraphicalNote>slurEndNote.parentVoiceEntry.notes[0];
             // if (first.NoteStem !== undefined && first.NoteStem.Direction === StemEnum.StemUp && this.placement === PlacementEnum.Above) {
             //     endX += first.NoteStem.PositionAndShape.RelativePosition.x;

+ 0 - 21
src/MusicalScore/Graphical/GraphicalSlurSorter.ts

@@ -1,21 +0,0 @@
-import { Fraction } from "../../Common/DataObjects/Fraction";
-import { GraphicalSlur } from "./GraphicalSlur";
-
-export interface GraphicalSlurSorterKeyValuePair {
-    key: Fraction;
-    value: GraphicalSlur;
-}
-
-export class GraphicalSlurSorter {
-    public Compare (x: GraphicalSlurSorterKeyValuePair, y: GraphicalSlurSorterKeyValuePair ): number {
-        if (x.key > y.key) {
-            return 1;
-        }
-
-        if (y.key > x.key) {
-            return -1;
-        }
-
-        return 0;
-    }
-}

+ 13 - 0
src/MusicalScore/Graphical/GraphicalVoiceEntry.ts

@@ -3,6 +3,7 @@ import { VoiceEntry } from "../VoiceData/VoiceEntry";
 import { BoundingBox } from "./BoundingBox";
 import { GraphicalNote } from "./GraphicalNote";
 import { GraphicalStaffEntry } from "./GraphicalStaffEntry";
+import { OctaveEnum } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 
 /**
  * The graphical counterpart of a [[VoiceEntry]].
@@ -19,4 +20,16 @@ export class GraphicalVoiceEntry extends GraphicalObject {
     public parentVoiceEntry: VoiceEntry;
     public parentStaffEntry: GraphicalStaffEntry;
     public notes: GraphicalNote[];
+    /** Contains octave shifts affecting this voice entry, caused by octave brackets. */
+    public octaveShiftValue: OctaveEnum;
+
+    /** Sort this entry's notes by pitch.
+     * Notes need to be sorted for Vexflow StaveNote creation.
+     * Note that Vexflow needs the reverse order, see VexFlowConverter.StaveNote().
+     */
+    public sort(): void {
+        this.notes.sort((a, b) => {
+            return b.sourceNote.Pitch.getHalfTone() - a.sourceNote.Pitch.getHalfTone();
+        });
+    }
 }

+ 7 - 0
src/MusicalScore/Graphical/ISqueezable.ts

@@ -0,0 +1,7 @@
+export interface ISqueezable {
+    /**
+     * Squeezes the wedge by the given amount.
+     * @param value Squeeze amount. Positive values squeeze from the left, negative from the right
+     */
+    squeeze(value: number): void;
+}

+ 476 - 91
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -1,52 +1,52 @@
-import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
-import {StaffLine} from "./StaffLine";
-import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
-import {EngravingRules} from "./EngravingRules";
-import {Tie} from "../VoiceData/Tie";
-import {Fraction} from "../../Common/DataObjects/Fraction";
-import {Note} from "../VoiceData/Note";
-import {MusicSheet} from "../MusicSheet";
-import {GraphicalMeasure} from "./GraphicalMeasure";
-import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
-import {LyricWord} from "../VoiceData/Lyrics/LyricsWord";
-import {SourceMeasure} from "../VoiceData/SourceMeasure";
-import {GraphicalMusicPage} from "./GraphicalMusicPage";
-import {GraphicalNote} from "./GraphicalNote";
-import {Beam} from "../VoiceData/Beam";
-import {OctaveEnum} from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
-import {VoiceEntry, StemDirectionType} from "../VoiceData/VoiceEntry";
-import {OrnamentContainer} from "../VoiceData/OrnamentContainer";
-import {ArticulationEnum} from "../VoiceData/VoiceEntry";
-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 {StaffEntryLink} from "../VoiceData/StaffEntryLink";
-import {MusicSystemBuilder} from "./MusicSystemBuilder";
-import {MultiTempoExpression} from "../VoiceData/Expressions/MultiTempoExpression";
-import {Repetition} from "../MusicSource/Repetition";
-import {PointF2D} from "../../Common/DataObjects/PointF2D";
-import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
-import {BoundingBox} from "./BoundingBox";
-import {Instrument} from "../Instrument";
-import {GraphicalLabel} from "./GraphicalLabel";
-import {TextAlignmentEnum} from "../../Common/Enums/TextAlignment";
-import {VerticalGraphicalStaffEntryContainer} from "./VerticalGraphicalStaffEntryContainer";
-import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
-import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
-import {TechnicalInstruction} from "../VoiceData/Instructions/TechnicalInstruction";
-import {Pitch} from "../../Common/DataObjects/Pitch";
-import {LinkedVoice} from "../VoiceData/LinkedVoice";
-import {ColDirEnum} from "./BoundingBox";
-import {IGraphicalSymbolFactory} from "../Interfaces/IGraphicalSymbolFactory";
-import {ITextMeasurer} from "../Interfaces/ITextMeasurer";
-import {ITransposeCalculator} from "../Interfaces/ITransposeCalculator";
-import {OctaveShiftParams} from "./OctaveShiftParams";
-import {AccidentalCalculator} from "./AccidentalCalculator";
-import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
-import {Staff} from "../VoiceData/Staff";
-import {OctaveShift} from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
+import { GraphicalStaffEntry } from "./GraphicalStaffEntry";
+import { StaffLine } from "./StaffLine";
+import { GraphicalMusicSheet } from "./GraphicalMusicSheet";
+import { EngravingRules } from "./EngravingRules";
+import { Tie } from "../VoiceData/Tie";
+import { Fraction } from "../../Common/DataObjects/Fraction";
+import { Note } from "../VoiceData/Note";
+import { MusicSheet } from "../MusicSheet";
+import { GraphicalMeasure } from "./GraphicalMeasure";
+import { ClefInstruction } from "../VoiceData/Instructions/ClefInstruction";
+import { LyricWord } from "../VoiceData/Lyrics/LyricsWord";
+import { SourceMeasure } from "../VoiceData/SourceMeasure";
+import { GraphicalMusicPage } from "./GraphicalMusicPage";
+import { GraphicalNote } from "./GraphicalNote";
+import { Beam } from "../VoiceData/Beam";
+import { OctaveEnum } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
+import { VoiceEntry, StemDirectionType } from "../VoiceData/VoiceEntry";
+import { OrnamentContainer } from "../VoiceData/OrnamentContainer";
+import { ArticulationEnum } from "../VoiceData/VoiceEntry";
+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 { StaffEntryLink } from "../VoiceData/StaffEntryLink";
+import { MusicSystemBuilder } from "./MusicSystemBuilder";
+import { MultiTempoExpression } from "../VoiceData/Expressions/MultiTempoExpression";
+import { Repetition } from "../MusicSource/Repetition";
+import { PointF2D } from "../../Common/DataObjects/PointF2D";
+import { SourceStaffEntry } from "../VoiceData/SourceStaffEntry";
+import { BoundingBox } from "./BoundingBox";
+import { Instrument } from "../Instrument";
+import { GraphicalLabel } from "./GraphicalLabel";
+import { TextAlignmentEnum } from "../../Common/Enums/TextAlignment";
+import { VerticalGraphicalStaffEntryContainer } from "./VerticalGraphicalStaffEntryContainer";
+import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction";
+import { AbstractNotationInstruction } from "../VoiceData/Instructions/AbstractNotationInstruction";
+import { TechnicalInstruction } from "../VoiceData/Instructions/TechnicalInstruction";
+import { Pitch } from "../../Common/DataObjects/Pitch";
+import { LinkedVoice } from "../VoiceData/LinkedVoice";
+import { ColDirEnum } from "./BoundingBox";
+import { IGraphicalSymbolFactory } from "../Interfaces/IGraphicalSymbolFactory";
+import { ITextMeasurer } from "../Interfaces/ITextMeasurer";
+import { ITransposeCalculator } from "../Interfaces/ITransposeCalculator";
+import { OctaveShiftParams } from "./OctaveShiftParams";
+import { AccidentalCalculator } from "./AccidentalCalculator";
+import { MidiInstrument } from "../VoiceData/Instructions/ClefInstruction";
+import { Staff } from "../VoiceData/Staff";
+import { OctaveShift } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import * as log from "loglevel";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
 import { GraphicalLyricEntry } from "./GraphicalLyricEntry";
@@ -62,6 +62,10 @@ import { GraphicalInstantaneousTempoExpression } from "./GraphicalInstantaneousT
 import { InstantaneousTempoExpression, TempoEnum } from "../VoiceData/Expressions/InstantaneousTempoExpression";
 import { ContinuousTempoExpression } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousTempoExpression";
 import { FontStyles } from "../../Common/Enums/FontStyles";
+import { AbstractTempoExpression } from "../VoiceData/Expressions/AbstractTempoExpression";
+import { GraphicalInstantaneousDynamicExpression } from "./GraphicalInstantaneousDynamicExpression";
+import { ContDynamicEnum } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
+import { GraphicalContinuousDynamicExpression } from "./GraphicalContinuousDynamicExpression";
 
 /**
  * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
@@ -75,7 +79,6 @@ export abstract class MusicSheetCalculator {
     protected staffEntriesWithOrnaments: GraphicalStaffEntry[] = [];
     protected staffEntriesWithChordSymbols: GraphicalStaffEntry[] = [];
     protected staffLinesWithLyricWords: StaffLine[] = [];
-    protected staffLinesWithGraphicalExpressions: StaffLine[] = [];
 
     protected graphicalLyricWords: GraphicalLyricWord[] = [];
 
@@ -123,7 +126,7 @@ export abstract class MusicSheetCalculator {
         this.staffEntriesWithOrnaments = [];
         this.staffEntriesWithChordSymbols = [];
         this.staffLinesWithLyricWords = [];
-        this.staffLinesWithGraphicalExpressions = [];
+        // this.staffLinesWithGraphicalExpressions = [];
 
         this.graphicalMusicSheet.Initialize();
         const measureList: GraphicalMeasure[][] = this.graphicalMusicSheet.MeasureList;
@@ -739,10 +742,10 @@ export abstract class MusicSheetCalculator {
         if (!this.leadSheet) {
             // calculate all Instantaneous/Continuous Dynamics Expressions
             this.calculateDynamicExpressions();
-            // place neighbouring DynamicExpressions at the same height
-            this.optimizeStaffLineDynamicExpressionsPositions();
             // calculate all Mood and Unknown Expression
             this.calculateMoodAndUnknownExpressions();
+            // Calculate the alignment of close expressions
+            this.calculateExpressionAlignements();
             // calculate all OctaveShifts
             this.calculateOctaveShifts();
             // calcualte RepetitionInstructions (Dal Segno, Coda, etc)
@@ -832,13 +835,6 @@ export abstract class MusicSheetCalculator {
         return;
     }
 
-    /**
-     * Iterate through all the [[StaffLine]]s in order to check for possible optimizations in the placement of the [[GraphicalExpression]]s.
-     */
-    protected optimizeStaffLineDynamicExpressionsPositions(): void {
-        return;
-    }
-
     protected calculateChordSymbols(): void {
         return;
     }
@@ -887,6 +883,370 @@ export abstract class MusicSheetCalculator {
         return;
     }
 
+
+    /**
+     * This method calculates the RelativePosition of a single verbal GraphicalContinuousDynamic.
+     * @param graphicalContinuousDynamic Graphical continous dynamic to be calculated
+     * @param startPosInStaffline Starting point in staff line
+     */
+    protected calculateGraphicalVerbalContinuousDynamic(graphicalContinuousDynamic: GraphicalContinuousDynamicExpression,
+                                                        startPosInStaffline: PointF2D): void {
+        // if ContinuousDynamicExpression is given from words
+        const graphLabel: GraphicalLabel = graphicalContinuousDynamic.Label;
+        const left: number = startPosInStaffline.x + graphLabel.PositionAndShape.BorderMarginLeft;
+        const right: number = startPosInStaffline.x + graphLabel.PositionAndShape.BorderMarginRight;
+        // placement always below the currentStaffLine, with the exception of Voice Instrument (-> above)
+        const placement: PlacementEnum = graphicalContinuousDynamic.ContinuousDynamic.Placement;
+        const staffLine: StaffLine = graphicalContinuousDynamic.ParentStaffLine;
+        const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
+
+        let drawingHeight: number;
+        if (placement === PlacementEnum.Below) {
+            drawingHeight = skyBottomLineCalculator.getBottomLineMaxInRange(left, right);    // Bottom line
+            graphLabel.PositionAndShape.RelativePosition = new PointF2D(startPosInStaffline.x, drawingHeight - graphLabel.PositionAndShape.BorderMarginTop);
+        } else {
+            drawingHeight = skyBottomLineCalculator.getSkyLineMinInRange(left, right);
+            graphLabel.PositionAndShape.RelativePosition = new PointF2D(startPosInStaffline.x, drawingHeight - graphLabel.PositionAndShape.BorderMarginBottom);
+        }
+    }
+
+   /**
+    * This method calculates the RelativePosition of a single GraphicalContinuousDynamic.
+    * @param graphicalContinuousDynamic Graphical continous dynamic to be calculated
+    * @param startPosInStaffline Starting point in staff line
+    */
+    public calculateGraphicalContinuousDynamic(graphicalContinuousDynamic: GraphicalContinuousDynamicExpression, startPosInStaffline: PointF2D): void {
+        const staffIndex: number = graphicalContinuousDynamic.ParentStaffLine.ParentStaff.idInMusicSheet;
+        // TODO: Previously the staffIndex was passed down. BUT you can (and this function actually does this) get it from
+        // the musicSystem OR from the ParentStaffLine. Is this the same index?
+        // const staffIndex: number = musicSystem.StaffLines.indexOf(staffLine);
+
+        // We know we have an end measure because otherwise we won't be called
+        const endMeasure: GraphicalMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(
+            graphicalContinuousDynamic.ContinuousDynamic.EndMultiExpression.SourceMeasureParent, staffIndex);
+        if (!endMeasure) {
+            log.warn("Not working");
+            return;
+        }
+
+        graphicalContinuousDynamic.EndMeasure = endMeasure;
+        const endStaffLine: StaffLine = endMeasure.ParentStaffLine;
+        const endAbsoluteTimestamp: Fraction = Fraction.createFromFraction(graphicalContinuousDynamic.ContinuousDynamic.EndMultiExpression.AbsoluteTimestamp);
+
+        const endPosInStaffLine: PointF2D = this.getRelativePositionInStaffLineFromTimestamp(
+            endAbsoluteTimestamp, staffIndex, endStaffLine, endStaffLine.isPartOfMultiStaffInstrument(), 0);
+
+        const staffLine: StaffLine = graphicalContinuousDynamic.ParentStaffLine;
+        //currentMusicSystem and currentStaffLine
+        const musicSystem: MusicSystem = staffLine.ParentMusicSystem;
+        const currentStaffLineIndex: number = musicSystem.StaffLines.indexOf(staffLine);
+        const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
+        // let expressionIndex: number;
+
+        // placement always below the currentStaffLine, with the exception of Voice Instrument (-> above)
+        const placement: PlacementEnum = graphicalContinuousDynamic.ContinuousDynamic.Placement;
+
+        // if ContinuousDynamicExpression is given from wedge
+        let secondGraphicalContinuousDynamic: GraphicalContinuousDynamicExpression = undefined;
+
+        // check if Expression spreads over the same StaffLine or not
+        const sameStaffLine: boolean = endStaffLine !== undefined && staffLine === endStaffLine;
+
+        // last length check
+        if (sameStaffLine && endPosInStaffLine.x - startPosInStaffline.x < this.rules.WedgeMinLength) {
+            endPosInStaffLine.x = startPosInStaffline.x + this.rules.WedgeMinLength;
+        }
+
+        // Upper staff wedge always starts at the given position and the lower staff wedge always starts at the begin of measure
+        const upperStartX: number = startPosInStaffline.x;
+        const lowerStartX: number = endStaffLine.Measures[0].beginInstructionsWidth - this.rules.WedgeHorizontalMargin - 2;
+        let upperEndX: number = 0;
+        let lowerEndX: number = 0;
+
+        if (!sameStaffLine) {
+            upperEndX = staffLine.PositionAndShape.Size.width;
+            lowerEndX = endPosInStaffLine.x;
+
+            // must create a new Wedge
+            secondGraphicalContinuousDynamic = new GraphicalContinuousDynamicExpression(graphicalContinuousDynamic.ContinuousDynamic, endStaffLine);
+            secondGraphicalContinuousDynamic.IsSplittedPart = true;
+            graphicalContinuousDynamic.IsSplittedPart = true;
+        } else {
+            upperEndX = endPosInStaffLine.x;
+        }
+
+        // the Height of the Expression's placement
+        let idealY: number = 0;
+        let secondIdealY: number = 0;
+
+        if (placement === PlacementEnum.Below) {
+            // can be a single Staff Instrument or an Instrument with 2 Staves
+            let nextStaffLineIndex: number = 0;
+            if (currentStaffLineIndex < musicSystem.StaffLines.length - 1) {
+                nextStaffLineIndex = currentStaffLineIndex + 1;
+            }
+
+            // check, maybe currentStaffLine is the last of the MusicSystem (and it has a ContinuousDynamicExpression with placement below)
+            if (nextStaffLineIndex > currentStaffLineIndex) {
+                // currentStaffLine isn't the last of the MusicSystem
+                const nextStaffLine: StaffLine = musicSystem.StaffLines[nextStaffLineIndex];
+
+                const distanceBetweenStaffLines: number = nextStaffLine.PositionAndShape.RelativePosition.y -
+                    staffLine.PositionAndShape.RelativePosition.y -
+                    this.rules.StaffHeight;
+
+                // ideal Height is exactly between the two StaffLines
+                idealY = this.rules.StaffHeight + distanceBetweenStaffLines / 2;
+            } else {
+                // currentStaffLine is the MusicSystem's last
+                idealY = this.rules.WedgePlacementBelowY;
+            }
+
+            // must consider the upperWedge starting/ending tip for the comparison with the BottomLine
+            idealY -= this.rules.WedgeOpeningLength / 2;
+            if (!sameStaffLine) {
+                // Set the value for the splitted y position to the ideal position before we check and modify it with
+                // the skybottom calculator detection
+                secondIdealY = idealY;
+            }
+            // must check BottomLine for possible collisions within the Length of the Expression
+            // find the corresponding max value for the given Length
+            let maxBottomLineValueForExpressionLength: number = skyBottomLineCalculator.getBottomLineMaxInRange(upperStartX, upperEndX);
+
+            // if collisions, then set the Height accordingly
+            if (maxBottomLineValueForExpressionLength > idealY) {
+                idealY = maxBottomLineValueForExpressionLength;
+            }
+
+            // special case - wedge must be drawn within the boundaries of a crossedBeam
+            const withinCrossedBeam: boolean = false;
+
+            if (currentStaffLineIndex < musicSystem.StaffLines.length - 1) {
+                // find GraphicalStaffEntries closest to wedge's xPositions
+                const closestToEndStaffEntry: GraphicalStaffEntry = staffLine.findClosestStaffEntry(upperEndX);
+                const closestToStartStaffEntry: GraphicalStaffEntry = staffLine.findClosestStaffEntry(upperStartX);
+
+                if (closestToStartStaffEntry && closestToEndStaffEntry) {
+                    // must check both StaffLines
+                    const startVerticalContainer: VerticalGraphicalStaffEntryContainer = closestToStartStaffEntry.parentVerticalContainer;
+                    // const endVerticalContainer: VerticalGraphicalStaffEntryContainer = closestToEndStaffEntry.parentVerticalContainer;
+                    if (startVerticalContainer) {
+                        // TODO: Needs to be implemented?
+                        // withinCrossedBeam = areStaffEntriesWithinCrossedBeam(startVerticalContainer,
+                        // endVerticalContainer, currentStaffLineIndex, nextStaffLineIndex);
+                    }
+
+                    if (withinCrossedBeam) {
+                        const nextStaffLine: StaffLine = musicSystem.StaffLines[nextStaffLineIndex];
+                        const nextStaffLineMinSkyLineValue: number = nextStaffLine.SkyBottomLineCalculator.getSkyLineMinInRange(upperStartX, upperEndX);
+                        const distanceBetweenStaffLines: number = nextStaffLine.PositionAndShape.RelativePosition.y -
+                            staffLine.PositionAndShape.RelativePosition.y;
+                        const relativeSkyLineHeight: number = distanceBetweenStaffLines + nextStaffLineMinSkyLineValue;
+
+                        if (relativeSkyLineHeight - this.rules.WedgeOpeningLength > this.rules.StaffHeight) {
+                            idealY = relativeSkyLineHeight - this.rules.WedgeVerticalMargin;
+                        } else {
+                            idealY = this.rules.StaffHeight + this.rules.WedgeOpeningLength;
+                        }
+
+                        graphicalContinuousDynamic.NotToBeRemoved = true;
+                    }
+                }
+            }
+
+            // do the same in case of a Wedge ending at another StaffLine
+            if (!sameStaffLine) {
+                maxBottomLineValueForExpressionLength = endStaffLine.SkyBottomLineCalculator.getBottomLineMaxInRange(lowerStartX, lowerEndX);
+
+                if (maxBottomLineValueForExpressionLength > secondIdealY) {
+                    secondIdealY = maxBottomLineValueForExpressionLength;
+                }
+
+                secondIdealY += this.rules.WedgeOpeningLength / 2;
+                secondIdealY += this.rules.WedgeVerticalMargin;
+            }
+
+            if (!withinCrossedBeam) {
+                idealY += this.rules.WedgeOpeningLength / 2;
+                idealY += this.rules.WedgeVerticalMargin;
+            }
+
+        } else if (placement === PlacementEnum.Above) {
+            // single Staff Instrument (eg Voice)
+            if (staffLine.ParentStaff.ParentInstrument.Staves.length === 1) {
+                // single Staff Voice Instrument
+                idealY = this.rules.WedgePlacementAboveY;
+            } else {
+                // Staff = not the first Staff of a 2-staved Instrument
+                let previousStaffLineIndex: number = 0;
+                if (currentStaffLineIndex > 0) {
+                    previousStaffLineIndex = currentStaffLineIndex - 1;
+                }
+
+                const previousStaffLine: StaffLine = musicSystem.StaffLines[previousStaffLineIndex];
+                const distanceBetweenStaffLines: number = staffLine.PositionAndShape.RelativePosition.y -
+                    previousStaffLine.PositionAndShape.RelativePosition.y -
+                    this.rules.StaffHeight;
+
+                // ideal Height is exactly between the two StaffLines
+                idealY = -distanceBetweenStaffLines / 2;
+            }
+
+            // must consider the upperWedge starting/ending tip for the comparison with the SkyLine
+            idealY += this.rules.WedgeOpeningLength / 2;
+            if (!sameStaffLine) {
+                secondIdealY = idealY;
+            }
+
+            // must check SkyLine for possible collisions within the Length of the Expression
+            // find the corresponding min value for the given Length
+            let minSkyLineValueForExpressionLength: number = skyBottomLineCalculator.getSkyLineMinInRange(upperStartX, upperEndX);
+
+            // if collisions, then set the Height accordingly
+            if (minSkyLineValueForExpressionLength < idealY) {
+                idealY = minSkyLineValueForExpressionLength;
+            }
+            const withinCrossedBeam: boolean = false;
+
+            // special case - wedge must be drawn within the boundaries of a crossedBeam
+            if (staffLine.ParentStaff.ParentInstrument.Staves.length > 1 && currentStaffLineIndex > 0) {
+                // find GraphicalStaffEntries closest to wedge's xPositions
+                const closestToStartStaffEntry: GraphicalStaffEntry = staffLine.findClosestStaffEntry(upperStartX);
+                const closestToEndStaffEntry: GraphicalStaffEntry = staffLine.findClosestStaffEntry(upperEndX);
+
+                if (closestToStartStaffEntry && closestToEndStaffEntry) {
+                    // must check both StaffLines
+                    const startVerticalContainer: VerticalGraphicalStaffEntryContainer = closestToStartStaffEntry.parentVerticalContainer;
+                    // const endVerticalContainer: VerticalGraphicalStaffEntryContainer = closestToEndStaffEntry.parentVerticalContainer;
+                    const formerStaffLineIndex: number = currentStaffLineIndex - 1;
+                    if (startVerticalContainer) {
+                        // withinCrossedBeam = this.areStaffEntriesWithinCrossedBeam(startVerticalContainer,
+                        // endVerticalContainer, currentStaffLineIndex, formerStaffLineIndex);
+                    }
+
+                    if (withinCrossedBeam) {
+                        const formerStaffLine: StaffLine = musicSystem.StaffLines[formerStaffLineIndex];
+                        const formerStaffLineMaxBottomLineValue: number = formerStaffLine.SkyBottomLineCalculator.
+                                                                          getBottomLineMaxInRange(upperStartX, upperEndX);
+                        const distanceBetweenStaffLines: number = staffLine.PositionAndShape.RelativePosition.y -
+                            formerStaffLine.PositionAndShape.RelativePosition.y;
+                        const relativeSkyLineHeight: number = distanceBetweenStaffLines - formerStaffLineMaxBottomLineValue;
+                        idealY = (relativeSkyLineHeight - this.rules.StaffHeight) / 2 + this.rules.StaffHeight;
+                    }
+                }
+            }
+
+            // do the same in case of a Wedge ending at another StaffLine
+            if (!sameStaffLine) {
+                minSkyLineValueForExpressionLength = endStaffLine.SkyBottomLineCalculator.getSkyLineMinInRange(lowerStartX, lowerEndX);
+
+                if (minSkyLineValueForExpressionLength < secondIdealY) {
+                    secondIdealY = minSkyLineValueForExpressionLength;
+                }
+
+                secondIdealY -= this.rules.WedgeOpeningLength / 2;
+            }
+
+            if (!withinCrossedBeam) {
+                idealY -= this.rules.WedgeOpeningLength / 2;
+                idealY -= this.rules.WedgeVerticalMargin;
+            }
+            if (!sameStaffLine) {
+                secondIdealY -= this.rules.WedgeVerticalMargin;
+            }
+        }
+
+        // now we have the correct placement Height for the Expression
+        // the idealY is calculated relative to the currentStaffLine
+
+        // Crescendo (point to the left, opening to the right)
+        graphicalContinuousDynamic.Lines.clear();
+        if (graphicalContinuousDynamic.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
+            if (sameStaffLine) {
+                graphicalContinuousDynamic.createCrescendoLines(upperStartX, upperEndX, idealY);
+                graphicalContinuousDynamic.calcPsi();
+            } else {
+                // two different Wedges
+                graphicalContinuousDynamic.createFirstHalfCrescendoLines(upperStartX, upperEndX, idealY);
+                graphicalContinuousDynamic.calcPsi();
+
+                secondGraphicalContinuousDynamic.createSecondHalfCrescendoLines(lowerStartX, lowerEndX, secondIdealY);
+                secondGraphicalContinuousDynamic.calcPsi();
+            }
+        } else if (graphicalContinuousDynamic.ContinuousDynamic.DynamicType === ContDynamicEnum.diminuendo) {
+            if (sameStaffLine) {
+                graphicalContinuousDynamic.createDiminuendoLines(upperStartX, upperEndX, idealY);
+                graphicalContinuousDynamic.calcPsi();
+            } else {
+                graphicalContinuousDynamic.createFirstHalfDiminuendoLines(upperStartX, upperEndX, idealY);
+                graphicalContinuousDynamic.calcPsi();
+
+                secondGraphicalContinuousDynamic.createSecondHalfDiminuendoLines(lowerStartX, lowerEndX, secondIdealY);
+                secondGraphicalContinuousDynamic.calcPsi();
+            }
+        } //End Diminuendo
+    }
+
+    /**
+     * This method calculates the RelativePosition of a single GraphicalInstantaneousDynamicExpression.
+     * @param graphicalInstantaneousDynamic Dynamic expression to be calculated
+     * @param startPosInStaffline Starting point in staff line
+     */
+    protected calculateGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic: GraphicalInstantaneousDynamicExpression,
+                                                               startPosInStaffline: PointF2D): void {
+        // get Margin Dimensions
+        const staffLine: StaffLine = graphicalInstantaneousDynamic.ParentStaffLine;
+        const left: number = startPosInStaffline.x + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginLeft;
+        const right: number = startPosInStaffline.x + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginRight;
+        const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
+        let yPosition: number = 0;
+
+        // calculate yPosition according to Placement
+        if (graphicalInstantaneousDynamic.Placement === PlacementEnum.Above) {
+            const skyLineValue: number = skyBottomLineCalculator.getSkyLineMinInRange(left, right);
+
+            // if StaffLine part of multiStaff Instrument and not the first one, ideal yPosition middle of distance between Staves
+            if (staffLine.isPartOfMultiStaffInstrument() && staffLine.ParentStaff !== staffLine.ParentStaff.ParentInstrument.Staves[0]) {
+                const formerStaffLine: StaffLine = staffLine.ParentMusicSystem.StaffLines[staffLine.ParentMusicSystem.StaffLines.indexOf(staffLine) - 1];
+                const difference: number = staffLine.PositionAndShape.RelativePosition.y -
+                    formerStaffLine.PositionAndShape.RelativePosition.y - this.rules.StaffHeight;
+
+                // take always into account the size of the Dynamic
+                if (skyLineValue > -difference / 2) {
+                    yPosition = -difference / 2;
+                } else {
+                    yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
+                }
+            } else {
+                yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
+            }
+
+            graphicalInstantaneousDynamic.PositionAndShape.RelativePosition = new PointF2D(startPosInStaffline.x, yPosition);
+        } else if (graphicalInstantaneousDynamic.Placement === PlacementEnum.Below) {
+            const bottomLineValue: number = skyBottomLineCalculator.getBottomLineMaxInRange(left, right);
+            // if StaffLine part of multiStaff Instrument and not the last one, ideal yPosition middle of distance between Staves
+            const lastStaff: Staff = staffLine.ParentStaff.ParentInstrument.Staves[staffLine.ParentStaff.ParentInstrument.Staves.length - 1];
+            if (staffLine.isPartOfMultiStaffInstrument() && staffLine.ParentStaff !== lastStaff) {
+                const nextStaffLine: StaffLine = staffLine.ParentMusicSystem.StaffLines[staffLine.ParentMusicSystem.StaffLines.indexOf(staffLine) + 1];
+                const difference: number = nextStaffLine.PositionAndShape.RelativePosition.y -
+                    staffLine.PositionAndShape.RelativePosition.y - this.rules.StaffHeight;
+                const border: number = graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
+
+                // take always into account the size of the Dynamic
+                if (bottomLineValue + border < this.rules.StaffHeight + difference / 2) {
+                    yPosition = this.rules.StaffHeight + difference / 2;
+                } else {
+                    yPosition = bottomLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop;
+                }
+            } else {
+                yPosition = bottomLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop;
+            }
+
+            graphicalInstantaneousDynamic.PositionAndShape.RelativePosition = new PointF2D(startPosInStaffline.x, yPosition);
+        }
+        graphicalInstantaneousDynamic.updateSkyBottomLine();
+    }
+
     protected calcGraphicalRepetitionEndingsRecursively(repetition: Repetition): void {
         return;
     }
@@ -1033,7 +1393,7 @@ export abstract class MusicSheetCalculator {
                     let alreadyAdded: boolean = false;
                     for (const expr of staffLine.AbstractExpressions) {
                         if (expr instanceof GraphicalInstantaneousTempoExpression &&
-                           (expr as GraphicalInstantaneousTempoExpression).InstantaneousTempoExpression.Label === entry.Expression.Label) {
+                            (expr.SourceExpression as AbstractTempoExpression).Label === entry.Expression.Label) {
                             alreadyAdded = true;
                         }
                     }
@@ -1043,13 +1403,17 @@ export abstract class MusicSheetCalculator {
                     }
 
                     const graphicalTempoExpr: GraphicalInstantaneousTempoExpression = new GraphicalInstantaneousTempoExpression(entry.Expression, graphLabel);
+                    if (graphicalTempoExpr.ParentStaffLine === undefined) {
+                        log.warn("Adding staffline didn't work");
+                        // I am actually fooling the linter her and use the created object. This method needs refactoring,
+                        // all graphical expression creations should be in one place and ahve basic stuff like labels, lines, ...
+                        // in their constructor
+                    }
                     // in case of metronome mark:
                     if ((entry.Expression as InstantaneousTempoExpression).Enum === TempoEnum.metronomeMark) {
                         // use smaller font:
                         graphLabel.Label.fontHeight = 1.2;
                     }
-
-                    staffLine.AbstractExpressions.push(graphicalTempoExpr);
                 } else if (entry.Expression instanceof ContinuousTempoExpression) {
                     // FIXME: Not yet implemented
                     // let alreadyAdded: boolean = false;
@@ -1122,21 +1486,18 @@ export abstract class MusicSheetCalculator {
                                openTuplets: Tuplet[], openBeams: Beam[],
                                octaveShiftValue: OctaveEnum, linkedNotes: Note[] = undefined,
                                sourceStaffEntry: SourceStaffEntry = undefined): OctaveEnum {
-        let voiceEntryHasPrintableNotes: boolean = false;
-        for (const note of voiceEntry.Notes) {
-            if (note.PrintObject) {
-                voiceEntryHasPrintableNotes = true;
-                break;
-            }
-        }
-        if (!voiceEntryHasPrintableNotes) {
-            return; // do not create a GraphicalVoiceEntry without graphical notes in it, will cause problems
+        if (voiceEntry.WantedStemDirectionXml !== StemDirectionType.Undefined &&
+            EngravingRules.Rules.SetWantedStemDirectionByXml &&
+            voiceEntry.WantedStemDirectionXml !== undefined) {
+                voiceEntry.WantedStemDirection = voiceEntry.WantedStemDirectionXml;
+        } else {
+            this.calculateStemDirectionFromVoices(voiceEntry);
         }
-        this.calculateStemDirectionFromVoices(voiceEntry);
         const gve: GraphicalVoiceEntry = graphicalStaffEntry.findOrCreateGraphicalVoiceEntry(voiceEntry);
+        gve.octaveShiftValue = octaveShiftValue;
         for (let idx: number = 0, len: number = voiceEntry.Notes.length; idx < len; ++idx) {
             const note: Note = voiceEntry.Notes[idx];
-            if (note === undefined || !note.PrintObject) {
+            if (note === undefined) {
                 continue;
             }
             if (sourceStaffEntry !== undefined && sourceStaffEntry.Link !== undefined && linkedNotes !== undefined && linkedNotes.indexOf(note) > -1) {
@@ -1155,10 +1516,10 @@ export abstract class MusicSheetCalculator {
             graphicalStaffEntry.addGraphicalNoteToListAtCorrectYPosition(gve, graphicalNote);
             graphicalNote.PositionAndShape.calculateBoundingBox();
             if (!this.leadSheet) {
-                if (note.NoteBeam !== undefined) {
+                if (note.NoteBeam !== undefined && note.PrintObject) {
                     this.handleBeam(graphicalNote, note.NoteBeam, openBeams);
                 }
-                if (note.NoteTuplet !== undefined) {
+                if (note.NoteTuplet !== undefined && note.PrintObject) {
                     this.handleTuplet(graphicalNote, note.NoteTuplet, openTuplets);
                 }
             }
@@ -1451,6 +1812,9 @@ export abstract class MusicSheetCalculator {
             endGse = this.graphicalMusicSheet.GetGraphicalFromSourceStaffEntry(tie.Notes[i].ParentStaffEntry);
             endNote = endGse.findEndTieGraphicalNoteFromNote(tie.Notes[i]);
             if (startNote !== undefined && endNote !== undefined && endGse !== undefined) {
+                if (!startNote.sourceNote.PrintObject || !endNote.sourceNote.PrintObject) {
+                    continue;
+                }
                 const graphicalTie: GraphicalTie = this.createGraphicalTie(tie, startGse, endGse, startNote, endNote);
                 startGse.GraphicalTies.push(graphicalTie);
                 if (this.staffEntriesWithGraphicalTies.indexOf(startGse) >= 0) {
@@ -1628,6 +1992,12 @@ export abstract class MusicSheetCalculator {
                 openOctaveShifts[staffIndex] = undefined;
             }
         }
+        // check wantedStemDirections of beam notes at end of measure (e.g. for beam with grace notes)
+        for (const staffEntry of measure.staffEntries) {
+            for (const voiceEntry of staffEntry.graphicalVoiceEntries) {
+                this.setBeamNotesWantedStemDirections(voiceEntry.parentVoiceEntry);
+            }
+        }
         // if there are no staffEntries in this measure, create a rest for the whole measure:
         if (measure.staffEntries.length === 0) {
             const sourceStaffEntry: SourceStaffEntry = new SourceStaffEntry(
@@ -1705,6 +2075,20 @@ export abstract class MusicSheetCalculator {
         }
     }
 
+    /**
+     * Re-adjust the x positioning of expressions. Update the skyline afterwards
+     */
+    private calculateExpressionAlignements(): void {
+        for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
+            for (const musicSystem of graphicalMusicPage.MusicSystems) {
+                for (const staffLine of musicSystem.StaffLines) {
+                    staffLine.AlignmentManager.alignDynamicExpressions();
+                    staffLine.AbstractExpressions.forEach(ae => ae.updateSkyBottomLine());
+                }
+            }
+        }
+    }
+
     private calculateBeams(): void {
         for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
             const musicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
@@ -2084,14 +2468,14 @@ export abstract class MusicSheetCalculator {
             const startX: number = startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
                 startStaffEntry.PositionAndShape.RelativePosition.x +
                 lyricEntry.GraphicalLabel.PositionAndShape.BorderMarginRight;
-                // + startStaffLine.PositionAndShape.AbsolutePosition.x; // doesn't work, done in drawer
+            // + startStaffLine.PositionAndShape.AbsolutePosition.x; // doesn't work, done in drawer
             const endX: number = endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
                 endStaffEntry.PositionAndShape.RelativePosition.x +
                 endStaffEntry.PositionAndShape.BorderMarginRight;
-                // + endStaffLine.PositionAndShape.AbsolutePosition.x; // doesn't work, done in drawer
-                // TODO maybe add half-width of following note.
-                // though we don't have the vexflow note's bbox yet and extend layouting is unconstrained,
-                // we have more room for spacing without it.
+            // + endStaffLine.PositionAndShape.AbsolutePosition.x; // doesn't work, done in drawer
+            // TODO maybe add half-width of following note.
+            // though we don't have the vexflow note's bbox yet and extend layouting is unconstrained,
+            // we have more room for spacing without it.
             // needed in order to line up with the Label's text bottom line (is the y position of the underscore)
             startY -= lyricEntry.GraphicalLabel.PositionAndShape.Size.height / 4;
             // create a Line (as underscore after the LyricLabel's End)
@@ -2285,31 +2669,32 @@ export abstract class MusicSheetCalculator {
             // in case of StaffEntryLink don't check mainVoice / linkedVoice
             if (voiceEntry === voiceEntry.ParentSourceStaffEntry.VoiceEntries[0]) {
                 // set stem up:
-                voiceEntry.StemDirection = StemDirectionType.Up;
+                voiceEntry.WantedStemDirection = StemDirectionType.Up;
                 return;
             } else {
                 // set stem down:
-                voiceEntry.StemDirection = StemDirectionType.Down;
+                voiceEntry.WantedStemDirection = StemDirectionType.Down;
                 return;
             }
         } else {
             if (voiceEntry.ParentVoice instanceof LinkedVoice) {
                 // Linked voice: set stem down:
-                voiceEntry.StemDirection = StemDirectionType.Down;
+                voiceEntry.WantedStemDirection = StemDirectionType.Down;
             } else {
                 // if this voiceEntry belongs to the mainVoice:
                 // check first that there are also more voices present:
                 if (voiceEntry.ParentSourceStaffEntry.VoiceEntries.length > 1) {
                     // as this voiceEntry belongs to the mainVoice: stem Up
-                    voiceEntry.StemDirection = StemDirectionType.Up;
+                    voiceEntry.WantedStemDirection = StemDirectionType.Up;
                 }
             }
         }
+        // setBeamNotesWantedStemDirections() will be called at end of measure (createGraphicalMeasure)
+    }
 
-        // ToDo: shift code to end of measure to only check once for all beams
-        // check for a beam:
-        // if this voice entry currently has no desired direction yet:
-        if (voiceEntry.StemDirection === StemDirectionType.Undefined &&
+    /** Sets a voiceEntry's stem direction to one already set in other notes in its beam, if it has one. */
+    private setBeamNotesWantedStemDirections(voiceEntry: VoiceEntry): void {
+        if (voiceEntry.WantedStemDirection === StemDirectionType.Undefined &&
             voiceEntry.Notes.length > 0) {
             const beam: Beam = voiceEntry.Notes[0].NoteBeam;
             if (beam !== undefined) {
@@ -2317,9 +2702,9 @@ export abstract class MusicSheetCalculator {
                 for (const note of beam.Notes) {
                     if (note.ParentVoiceEntry === voiceEntry) {
                         continue;
-                    } else if (note.ParentVoiceEntry.StemDirection !== StemDirectionType.Undefined) {
+                    } else if (note.ParentVoiceEntry.WantedStemDirection !== StemDirectionType.Undefined) {
                         // set the stem direction
-                        voiceEntry.StemDirection = note.ParentVoiceEntry.StemDirection;
+                        voiceEntry.WantedStemDirection = note.ParentVoiceEntry.WantedStemDirection;
                         break;
                     }
                 }

+ 17 - 10
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -24,6 +24,7 @@ import {Instrument} from "../Instrument";
 import {MusicSymbolDrawingStyle, PhonicScoreModes} from "./DrawingMode";
 import {GraphicalObject} from "./GraphicalObject";
 import { GraphicalInstantaneousDynamicExpression } from "./GraphicalInstantaneousDynamicExpression";
+import { GraphicalContinuousDynamicExpression } from "./GraphicalContinuousDynamicExpression";
 
 /**
  * Draw a [[GraphicalMusicSheet]] (through the .drawSheet method)
@@ -429,17 +430,23 @@ export abstract class MusicSheetDrawer {
     //         drawLineAsVerticalRectangle(ending.Right, absolutePosition, <number>GraphicalLayers.Notes);
     //     this.drawLabel(ending.Label, <number>GraphicalLayers.Notes);
     // }
+
+    /**
+     * Draws an instantaneous dynamic expression (p, pp, f, ff, ...) to the canvas
+     * @param instantaneousDynamic GraphicalInstantaneousDynamicExpression to be drawn
+     */
     protected drawInstantaneousDynamic(instantaneousDynamic: GraphicalInstantaneousDynamicExpression): void {
-        // expression.ExpressionSymbols.forEach(function (expressionSymbol) {
-        //     let position: PointF2D = expressionSymbol.PositionAndShape.AbsolutePosition;
-        //     let symbol: MusicSymbol = expressionSymbol.GetSymbol;
-        //     drawSymbol(symbol, MusicSymbolDrawingStyle.Normal, position);
-        // });
-    }
-    // protected drawContinuousDynamic(expression: GraphicalContinuousDynamicExpression,
-    //     absolute: PointF2D): void {
-    //     throw new Error("not implemented");
-    // }
+        throw new Error("not implemented");
+    }
+
+    /**
+     * Draws a continuous dynamic expression (wedges) to the canvas
+     * @param expression GraphicalContinuousDynamicExpression to be drawn
+     */
+    protected drawContinuousDynamic(expression: GraphicalContinuousDynamicExpression): void {
+        throw new Error("not implemented");
+    }
+
     protected drawSymbol(symbol: MusicSymbol, symbolStyle: MusicSymbolDrawingStyle, position: PointF2D,
                          scalingFactor: number = 1, layer: number = <number>GraphicalLayers.Notes): void {
         //empty

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

@@ -157,7 +157,6 @@ export class SkyBottomLineCalculator {
 
     /**
      * This method updates the SkyLine for a given Wedge.
-     * @param  to update the SkyLine for
      * @param start Start point of the wedge
      * @param end End point of the wedge
      */
@@ -194,7 +193,6 @@ export class SkyBottomLineCalculator {
 
     /**
      * This method updates the BottomLine for a given Wedge.
-     * @param  to update the bottomline for
      * @param start Start point of the wedge
      * @param end End point of the wedge
      */

+ 11 - 3
src/MusicalScore/Graphical/StaffLine.ts

@@ -12,6 +12,8 @@ import {GraphicalLabel} from "./GraphicalLabel";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
 import { GraphicalOctaveShift } from "./GraphicalOctaveShift";
 import { GraphicalSlur } from "./GraphicalSlur";
+import { AlignmentManager } from "./AlignmentManager";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
 
 /**
  * A StaffLine contains the [[Measure]]s in one line of the music sheet
@@ -24,9 +26,10 @@ export abstract class StaffLine extends GraphicalObject {
     protected parentStaff: Staff;
     protected octaveShifts: GraphicalOctaveShift[] = [];
     protected skyBottomLine: SkyBottomLineCalculator;
+    protected alignmentManager: AlignmentManager;
     protected lyricLines: GraphicalLine[] = [];
     protected lyricsDashes: GraphicalLabel[] = [];
-    protected abstractExpressions: GraphicalObject[] = [];
+    protected abstractExpressions: AbstractGraphicalExpression[] = [];
 
     // For displaying Slurs
     protected graphicalSlurs: GraphicalSlur[] = [];
@@ -37,6 +40,7 @@ export abstract class StaffLine extends GraphicalObject {
         this.parentStaff = parentStaff;
         this.boundingBox = new BoundingBox(this, parentSystem.PositionAndShape);
         this.skyBottomLine = new SkyBottomLineCalculator(this);
+        this.alignmentManager = new AlignmentManager(this);
     }
 
     public get Measures(): GraphicalMeasure[] {
@@ -64,11 +68,11 @@ export abstract class StaffLine extends GraphicalObject {
         return this.lyricLines;
     }
 
-    public get AbstractExpressions(): GraphicalObject[] {
+    public get AbstractExpressions(): AbstractGraphicalExpression[] {
         return this.abstractExpressions;
     }
 
-    public set AbstractExpressions(value: GraphicalObject[]) {
+    public set AbstractExpressions(value: AbstractGraphicalExpression[]) {
         this.abstractExpressions = value;
     }
 
@@ -100,6 +104,10 @@ export abstract class StaffLine extends GraphicalObject {
         this.parentStaff = value;
     }
 
+    public get AlignmentManager(): AlignmentManager {
+        return this.alignmentManager;
+    }
+
     public get SkyBottomLineCalculator(): SkyBottomLineCalculator {
         return this.skyBottomLine;
     }

+ 2 - 2
src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts

@@ -31,10 +31,10 @@ export class SvgVexFlowBackend extends VexFlowBackend {
     }
 
     public clear(): void {
-        const { svg } = this.ctx;
-        if (!svg) {
+        if (!this.ctx) {
             return;
         }
+        const { svg } = this.ctx;
         // removes all children from the SVG element,
         // effectively clearing the SVG viewport
         while (svg.lastChild) {

+ 26 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowContinuousDynamicExpression.ts

@@ -0,0 +1,26 @@
+import { GraphicalContinuousDynamicExpression } from "../GraphicalContinuousDynamicExpression";
+import { ContinuousDynamicExpression } from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
+import { StaffLine } from "../StaffLine";
+import { GraphicalLabel } from "../GraphicalLabel";
+import { Label } from "../../Label";
+import { TextAlignmentEnum } from "../../../Common/Enums/TextAlignment";
+import { FontStyles } from "../../../Common/Enums/FontStyles";
+
+/**
+ * This class extends the GraphicalContinuousDynamicExpression and creates all necessary methods for drawing
+ */
+export class VexFlowContinuousDynamicExpression extends GraphicalContinuousDynamicExpression {
+    constructor(continuousDynamic: ContinuousDynamicExpression, staffLine: StaffLine, textHeight?: number) {
+        super(continuousDynamic, staffLine);
+        if (this.IsVerbal) {
+            this.label = new GraphicalLabel(new Label(continuousDynamic.Label),
+                                            textHeight ? textHeight : this.rules.ContinuousDynamicTextHeight,
+                                            TextAlignmentEnum.LeftCenter,
+                                            this.PositionAndShape);
+
+            this.label.Label.fontStyle = FontStyles.Italic;
+            this.label.setLabelPositionAndShapeBorders();
+            this.PositionAndShape.calculateBoundingBox();
+        }
+    }
+}

+ 29 - 12
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -108,9 +108,9 @@ export class VexFlowConverter {
      */
     public static pitch(note: VexFlowGraphicalNote, pitch: Pitch): [string, string, ClefInstruction] {
         const fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
+        const acc: string = VexFlowConverter.accidental(pitch.Accidental);
         // The octave seems to need a shift of three FIXME?
         const octave: number = pitch.Octave - note.Clef().OctaveOffset + 3;
-        const acc: string = VexFlowConverter.accidental(pitch.Accidental);
         const noteHead: NoteHead = note.sourceNote.NoteHead;
         let noteHeadCode: string = "";
         if (noteHead !== undefined) {
@@ -122,7 +122,7 @@ export class VexFlowConverter {
     /** returns the Vexflow code for a note head. Some are still unsupported, see Vexflow/tables.js */
     public static NoteHeadCode(noteHead: NoteHead): string {
         const codeStart: string = "/";
-        const codeFilled: string = noteHead.Filled ? "2" : "1";
+        const codeFilled: string = noteHead.Filled ? "2" : "1"; // filled/unfilled notehead code in most vexflow glyphs
         switch (noteHead.Shape) {
             case NoteHeadShape.NORMAL:
                 return "";
@@ -133,7 +133,11 @@ export class VexFlowConverter {
             case NoteHeadShape.X:
                 return codeStart + "X" + codeFilled;
             case NoteHeadShape.CIRCLEX:
-                return codeStart + "X3"; // circleX is "X3" in Vexflow for some reason
+                return codeStart + "X3";
+            case NoteHeadShape.RECTANGLE:
+                return codeStart + "R" + codeFilled;
+            case NoteHeadShape.SQUARE:
+                return codeStart + "S" + codeFilled;
             case NoteHeadShape.SLASH:
                 return ""; // slash is specified at end of duration string in Vexflow
             default:
@@ -182,7 +186,6 @@ export class VexFlowConverter {
     }
 
     public static GhostNote(frac: Fraction): Vex.Flow.GhostNote {
-        // const frac: Fraction = notes[0].graphicalNoteLength;
         return new Vex.Flow.GhostNote({
             duration: VexFlowConverter.duration(frac, false),
         });
@@ -194,8 +197,14 @@ export class VexFlowConverter {
      * @returns {Vex.Flow.StaveNote}
      */
     public static StaveNote(gve: GraphicalVoiceEntry): Vex.Flow.StaveNote {
+        // sort notes
+        /* seems unnecessary for now
+        if (gve.octaveShiftValue !== undefined && gve.octaveShiftValue !== OctaveEnum.NONE) {
+            gve.sort(); // gves with accidentals in octave shift brackets can be unsorted
+        } */
         // VexFlow needs the notes ordered vertically in the other direction:
         const notes: GraphicalNote[] = gve.notes.reverse();
+
         const baseNote: GraphicalNote = notes[0];
         let keys: string[] = [];
         const accidentals: string[] = [];
@@ -262,16 +271,21 @@ export class VexFlowConverter {
             slash: gve.parentVoiceEntry.GraceNoteSlash,
         };
 
-        if (!gve.parentVoiceEntry.IsGrace) {
-            vfnote = new Vex.Flow.StaveNote(vfnoteStruct);
-        } else {
+        if (gve.notes[0].sourceNote.IsCueNote) {
+            (<any>vfnoteStruct).glyph_font_scale = Vex.Flow.DEFAULT_NOTATION_FONT_SCALE * Vex.Flow.GraceNote.SCALE;
+            (<any>vfnoteStruct).stroke_px = Vex.Flow.GraceNote.LEDGER_LINE_OFFSET;
+        }
+
+        if (gve.parentVoiceEntry.IsGrace || gve.notes[0].sourceNote.IsCueNote) {
             vfnote = new Vex.Flow.GraceNote(vfnoteStruct);
+        } else {
+            vfnote = new Vex.Flow.StaveNote(vfnoteStruct);
         }
 
         vfnote.x_shift = xShift;
 
         if (gve.parentVoiceEntry !== undefined) {
-            const wantedStemDirection: StemDirectionType = gve.parentVoiceEntry.StemDirection;
+            const wantedStemDirection: StemDirectionType = gve.parentVoiceEntry.WantedStemDirection;
             switch (wantedStemDirection) {
                 case(StemDirectionType.Up):
                     vfnote.setStemDirection(Vex.Flow.Stem.UP);
@@ -280,23 +294,23 @@ export class VexFlowConverter {
                     vfnote.setStemDirection(Vex.Flow.Stem.DOWN);
                     break;
                 default:
-                    break;
             }
         }
 
+        // add accidentals
         for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
             (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
             if (accidentals[i]) {
-                if (accidentals[i] === "++") {
+                if (accidentals[i] === "++") { // triple sharp
                     vfnote.addAccidental(i, new Vex.Flow.Accidental("##"));
                     vfnote.addAccidental(i, new Vex.Flow.Accidental("#"));
                     continue;
-                } else if (accidentals[i] === "bbs") {
+                } else if (accidentals[i] === "bbs") { // triple flat
                     vfnote.addAccidental(i, new Vex.Flow.Accidental("bb"));
                     vfnote.addAccidental(i, new Vex.Flow.Accidental("b"));
                     continue;
                 }
-                vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i]));
+                vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i])); // normal accidental
             }
         }
         for (let i: number = 0, len: number = numDots; i < len; ++i) {
@@ -306,6 +320,9 @@ export class VexFlowConverter {
     }
 
     public static generateArticulations(vfnote: Vex.Flow.StemmableNote, articulations: ArticulationEnum[]): void {
+        if (vfnote === undefined) {
+            return; // needed because grace notes after main note currently not implemented. maybe safer in any case
+        }
         // Articulations:
         let vfArtPosition: number = Vex.Flow.Modifier.Position.ABOVE;
 

+ 5 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts

@@ -34,8 +34,8 @@ export class VexFlowGraphicalNote extends GraphicalNote {
     private clef: ClefInstruction;
 
     /**
-     * Update the pitch of this note. Necessary in order to display correctly
-     * accidentals, this is called by VexFlowGraphicalSymbolFactory.addGraphicalAccidental.
+     * Update the pitch of this note. Necessary in order to display accidentals correctly.
+     * This is called by VexFlowGraphicalSymbolFactory.addGraphicalAccidental.
      * @param pitch
      */
     public setPitch(pitch: Pitch): void {
@@ -46,7 +46,9 @@ export class VexFlowGraphicalNote extends GraphicalNote {
                 this.vfnote[0].addAccidental(this.vfnote[1], new Vex.Flow.Accidental(acc));
             }
         } else {
-            this.vfpitch = VexFlowConverter.pitch(this, pitch);
+            // revert octave shift, as the placement of the note is independent of octave brackets
+            const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(pitch, this.octaveShift);
+            this.vfpitch = VexFlowConverter.pitch(this, drawPitch);
         }
     }
 

+ 1 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts

@@ -162,6 +162,7 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
                                           EngravingRules.Rules.ChordSymbolTextHeight,
                                           transposeHalftones);
       const graphicalLabel: GraphicalLabel = graphicalChordSymbolContainer.GetGraphicalLabel;
+      graphicalLabel.PositionAndShape.RelativePosition.y -= EngravingRules.Rules.ChordSymbolYOffset;
       graphicalLabel.setLabelPositionAndShapeBorders();
       graphicalChordSymbolContainer.PositionAndShape.calculateBoundingBox();
       graphicalStaffEntry.graphicalChordContainer = graphicalChordSymbolContainer;

+ 6 - 17
src/MusicalScore/Graphical/VexFlow/VexFlowInstantaneousDynamicExpression.ts

@@ -3,28 +3,21 @@ import { InstantaneousDynamicExpression, DynamicEnum } from "../../VoiceData/Exp
 import { GraphicalLabel } from "../GraphicalLabel";
 import { Label } from "../../Label";
 import { TextAlignmentEnum } from "../../../Common/Enums/TextAlignment";
-import { EngravingRules } from "../EngravingRules";
 import { FontStyles } from "../../../Common/Enums/FontStyles";
 import { StaffLine } from "../StaffLine";
 import { GraphicalMeasure } from "../GraphicalMeasure";
 
 export class VexFlowInstantaneousDynamicExpression extends GraphicalInstantaneousDynamicExpression {
-    private mLabel: GraphicalLabel;
-
     constructor(instantaneousDynamicExpression: InstantaneousDynamicExpression, staffLine: StaffLine, measure: GraphicalMeasure) {
         super(instantaneousDynamicExpression, staffLine, measure);
 
-        let labelAlignment: TextAlignmentEnum = TextAlignmentEnum.CenterTop;
-        if (EngravingRules.Rules.CompactMode) {
-            labelAlignment = TextAlignmentEnum.LeftBottom;
-        }
-        this.mLabel = new GraphicalLabel(new Label(this.Expression, labelAlignment),
-                                         EngravingRules.Rules.ContinuousDynamicTextHeight,
-                                         labelAlignment,
-                                         this.PositionAndShape);
+        this.label = new GraphicalLabel(new Label(this.Expression),
+                                        this.rules.ContinuousDynamicTextHeight,
+                                        TextAlignmentEnum.CenterCenter,
+                                        this.PositionAndShape);
 
-        this.mLabel.Label.fontStyle = FontStyles.BoldItalic;
-        this.mLabel.setLabelPositionAndShapeBorders();
+        this.label.Label.fontStyle = FontStyles.BoldItalic;
+        this.label.setLabelPositionAndShapeBorders();
         this.PositionAndShape.calculateBoundingBox();
     }
 
@@ -35,8 +28,4 @@ export class VexFlowInstantaneousDynamicExpression extends GraphicalInstantaneou
     get Expression(): string {
         return DynamicEnum[this.mInstantaneousDynamicExpression.DynEnum];
     }
-
-    get Label(): GraphicalLabel {
-        return this.mLabel;
-    }
 }

+ 244 - 18
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -19,16 +19,21 @@ import NoteSubGroup = Vex.Flow.NoteSubGroup;
 import * as log from "loglevel";
 import {unitInPixels} from "./VexFlowMusicSheetDrawer";
 import {Tuplet} from "../../VoiceData/Tuplet";
-import { RepetitionInstructionEnum, RepetitionInstruction, AlignmentType } from "../../VoiceData/Instructions/RepetitionInstruction";
+import {RepetitionInstructionEnum, RepetitionInstruction, AlignmentType} from "../../VoiceData/Instructions/RepetitionInstruction";
 import {SystemLinePosition} from "../SystemLinePosition";
 import {StemDirectionType} from "../../VoiceData/VoiceEntry";
 import {GraphicalVoiceEntry} from "../GraphicalVoiceEntry";
 import {VexFlowVoiceEntry} from "./VexFlowVoiceEntry";
 import {Fraction} from "../../../Common/DataObjects/Fraction";
 import {Voice} from "../../VoiceData/Voice";
-import {VexFlowInstantaneousDynamicExpression} from "./VexFlowInstantaneousDynamicExpression";
 import {LinkedVoice} from "../../VoiceData/LinkedVoice";
 import {EngravingRules} from "../EngravingRules";
+import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
+import {TechnicalInstruction} from "../../VoiceData/Instructions/TechnicalInstruction";
+import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
+import {ArpeggioType} from "../../VoiceData/Arpeggio";
+import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
+import {AutoBeamOptions} from "../../../OpenSheetMusicDisplay/OSMDOptions";
 
 export class VexFlowMeasure extends GraphicalMeasure {
     constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
@@ -47,14 +52,16 @@ export class VexFlowMeasure extends GraphicalMeasure {
     public vfTies: Vex.Flow.StaveTie[] = [];
     /** The repetition instructions given as words or symbols (coda, dal segno..) */
     public vfRepetitionWords: Vex.Flow.Repetition[] = [];
-    /** Instant dynamics */
-    public instantaneousDynamics: VexFlowInstantaneousDynamicExpression[] = [];
     /** The VexFlow Stave (= one measure in a staffline) */
     private stave: Vex.Flow.Stave;
     /** VexFlow StaveConnectors (vertical lines) */
     private connectors: Vex.Flow.StaveConnector[] = [];
     /** Intermediate object to construct beams */
     private beams: { [voiceID: number]: [Beam, VexFlowVoiceEntry[]][]; } = {};
+    /** Beams created by (optional) autoBeam function. */
+    private autoVfBeams: Vex.Flow.Beam[];
+    /** Beams of tuplet notes created by (optional) autoBeam function. */
+    private autoTupletVfBeams: Vex.Flow.Beam[];
     /** VexFlow Beams */
     private vfbeams: { [voiceID: number]: Vex.Flow.Beam[]; };
     /** Intermediate object to construct tuplets */
@@ -88,7 +95,6 @@ export class VexFlowMeasure extends GraphicalMeasure {
         this.connectors = [];
         // Clean up instructions
         this.resetLayout();
-        this.instantaneousDynamics = [];
     }
 
     /**
@@ -341,6 +347,17 @@ export class VexFlowMeasure extends GraphicalMeasure {
                 }
             }
         }
+        // Draw auto-generated beams from Beam.generateBeams()
+        if (this.autoVfBeams) {
+            for (const beam of this.autoVfBeams) {
+                beam.setContext(ctx).draw();
+            }
+        }
+        if (this.autoTupletVfBeams) {
+            for (const beam of this.autoTupletVfBeams) {
+                beam.setContext(ctx).draw();
+            }
+        }
 
         // Draw tuplets
         for (const voiceID in this.vftuplets) {
@@ -429,7 +446,6 @@ export class VexFlowMeasure extends GraphicalMeasure {
 
             // check if this voice has just been found the first time:
             if (latestVoiceTimestamp === undefined) {
-
                 // if this voice is new, check for a gap from measure start to the start of the current voice entry:
                 const gapFromMeasureStart: Fraction = Fraction.minus(gNotesStartTimestamp, this.parentSourceMeasure.AbsoluteTimestamp);
                 if (gapFromMeasureStart.RealValue > 0) {
@@ -535,6 +551,7 @@ export class VexFlowMeasure extends GraphicalMeasure {
         // created them brand new. Is this needed? And more importantly,
         // should the old beams be removed manually by the notes?
         this.vfbeams = {};
+        const beamedNotes: StaveNote[] = []; // already beamed notes, will be ignored by this.autoBeamNotes()
         for (const voiceID in this.beams) {
             if (this.beams.hasOwnProperty(voiceID)) {
                 let vfbeams: Vex.Flow.Beam[] = this.vfbeams[voiceID];
@@ -549,18 +566,28 @@ export class VexFlowMeasure extends GraphicalMeasure {
                     let autoStemBeam: boolean = true;
                     for (const gve of voiceEntries) {
                         if (gve.parentVoiceEntry.ParentVoice === psBeam.Notes[0].ParentVoiceEntry.ParentVoice) {
-                            autoStemBeam = gve.parentVoiceEntry.StemDirection === StemDirectionType.Undefined;
+                            autoStemBeam = gve.parentVoiceEntry.WantedStemDirection === StemDirectionType.Undefined;
                         }
                     }
 
+                    let isGraceBeam: boolean = false;
                     for (const entry of voiceEntries) {
                         const note: Vex.Flow.StaveNote = ((<VexFlowVoiceEntry>entry).vfStaveNote as StaveNote);
                         if (note !== undefined) {
                           notes.push(note);
+                          beamedNotes.push(note);
+                        }
+                        if (entry.parentVoiceEntry.IsGrace) {
+                            isGraceBeam = true;
                         }
                     }
                     if (notes.length > 1) {
                         const vfBeam: Vex.Flow.Beam = new Vex.Flow.Beam(notes, autoStemBeam);
+                        if (isGraceBeam) {
+                            // smaller beam, as in Vexflow.GraceNoteGroup.beamNotes()
+                            (<any>vfBeam).render_options.beam_width = 3;
+                            (<any>vfBeam).render_options.partial_beam_length = 4;
+                        }
                         vfbeams.push(vfBeam);
                         // just a test for coloring the notes:
                         // for (let note of notes) {
@@ -572,6 +599,84 @@ export class VexFlowMeasure extends GraphicalMeasure {
                 }
             }
         }
+        if (EngravingRules.Rules.AutoBeamNotes) {
+            this.autoBeamNotes(beamedNotes); // try to autobeam notes except those that are already beamed (beamedNotes).
+        }
+    }
+
+    /** Automatically creates beams for notes except beamedNotes, using Vexflow's Beam.generateBeams().
+     *  Takes options from EngravingRules.Rules.AutoBeamOptions.
+     * @param beamedNotes notes that will not be autobeamed (usually because they are already beamed)
+     */
+    private autoBeamNotes(beamedNotes: StemmableNote[]): void {
+        const notesToAutoBeam: StemmableNote[] = [];
+        let consecutiveBeamableNotes: StemmableNote[] = [];
+        let currentTuplet: Tuplet;
+        let tupletNotesToAutoBeam: StaveNote[] = [];
+        this.autoTupletVfBeams = [];
+        for (const staffEntry of this.staffEntries) {
+            for (const gve of staffEntry.graphicalVoiceEntries) {
+                const vfStaveNote: StaveNote = <StaveNote> (gve as VexFlowVoiceEntry).vfStaveNote;
+                if (gve.parentVoiceEntry.IsGrace || // don't beam grace notes
+                    gve.notes[0].graphicalNoteLength.CompareTo(new Fraction(1, 4)) >= 0 || // don't beam quarter or longer notes
+                    beamedNotes.contains(vfStaveNote)) { // don't beam already beamed notes
+                    if (consecutiveBeamableNotes.length >= 2) { // don't beam notes surrounded by quarter notes etc.
+                        for (const note of consecutiveBeamableNotes) {
+                            notesToAutoBeam.push(note);
+                        }
+                    }
+                    consecutiveBeamableNotes = [];
+                    continue;
+                }
+
+                // create beams for tuplets separately
+                const noteTuplet: Tuplet = gve.notes[0].sourceNote.NoteTuplet;
+                if (noteTuplet) {
+                    if (currentTuplet === undefined) {
+                        currentTuplet = noteTuplet;
+                    } else {
+                        if (currentTuplet !== noteTuplet) { // new tuplet, finish old one
+                            if (tupletNotesToAutoBeam.length > 1) {
+                                this.autoTupletVfBeams.push(new Vex.Flow.Beam(tupletNotesToAutoBeam, true));
+                            }
+                            tupletNotesToAutoBeam = [];
+                            currentTuplet = noteTuplet;
+                        }
+                    }
+                    tupletNotesToAutoBeam.push(<StaveNote>(gve as VexFlowVoiceEntry).vfStaveNote);
+                    continue;
+                } else {
+                    currentTuplet = undefined;
+                }
+
+                consecutiveBeamableNotes.push((gve as VexFlowVoiceEntry).vfStaveNote);
+            }
+        }
+        if (tupletNotesToAutoBeam.length >= 2) {
+            this.autoTupletVfBeams.push(new Vex.Flow.Beam(tupletNotesToAutoBeam, true));
+        }
+        if (consecutiveBeamableNotes.length >= 2) {
+            for (const note of consecutiveBeamableNotes) {
+                notesToAutoBeam.push(note);
+            }
+        }
+
+        // create options for generateBeams
+        const autoBeamOptions: AutoBeamOptions = EngravingRules.Rules.AutoBeamOptions;
+        const generateBeamOptions: any = {
+            beam_middle_only: autoBeamOptions.beam_middle_rests_only,
+            beam_rests: autoBeamOptions.beam_rests,
+            maintain_stem_directions: autoBeamOptions.maintain_stem_directions,
+        };
+        if (autoBeamOptions.groups && autoBeamOptions.groups.length) {
+            const groups: Vex.Flow.Fraction[] = [];
+            for (const fraction of autoBeamOptions.groups) {
+                groups.push(new Vex.Flow.Fraction(fraction[0], fraction[1]));
+            }
+            generateBeamOptions.groups = groups;
+        }
+
+        this.autoVfBeams = Vex.Flow.Beam.generateBeams(notesToAutoBeam, generateBeamOptions);
     }
 
     /**
@@ -620,10 +725,12 @@ export class VexFlowMeasure extends GraphicalMeasure {
     }
 
     public graphicalMeasureCreatedCalculations(): void {
+        let graceSlur: boolean;
+        let graceGVoiceEntriesBefore: GraphicalVoiceEntry[];
         for (const graphicalStaffEntry of this.staffEntries as VexFlowStaffEntry[]) {
+            graceSlur = false;
+            graceGVoiceEntriesBefore = [];
             // create vex flow Stave Notes:
-            let graceSlur: boolean = false;
-            let graceGVoiceEntriesBefore: GraphicalVoiceEntry[] = [];
             for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
                 if (gve.parentVoiceEntry.IsGrace) {
                     graceGVoiceEntriesBefore.push(gve);
@@ -635,22 +742,34 @@ export class VexFlowMeasure extends GraphicalMeasure {
                 if (gve.notes[0].sourceNote.PrintObject) {
                     (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve);
                 } else {
+                    // don't render note. add ghost note, otherwise Vexflow can have issues with layouting when voices not complete.
+                    (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.GhostNote(gve.notes[0].sourceNote.Length);
                     graceGVoiceEntriesBefore = []; // if note is not rendered, its grace notes might need to be removed
                     continue;
                 }
                 if (graceGVoiceEntriesBefore.length > 0) {
                     const graceNotes: Vex.Flow.GraceNote[] = [];
                     for (let i: number = 0; i < graceGVoiceEntriesBefore.length; i++) {
-                        if (graceGVoiceEntriesBefore[i].notes[0].sourceNote.PrintObject) {
-                            graceNotes.push(VexFlowConverter.StaveNote(graceGVoiceEntriesBefore[i]));
+                        const gveGrace: VexFlowVoiceEntry = <VexFlowVoiceEntry>graceGVoiceEntriesBefore[i];
+                        if (gveGrace.notes[0].sourceNote.PrintObject) {
+                            const vfStaveNote: StaveNote = VexFlowConverter.StaveNote(gveGrace);
+                            gveGrace.vfStaveNote = vfStaveNote;
+                            graceNotes.push(vfStaveNote);
                         }
                     }
                     const graceNoteGroup: Vex.Flow.GraceNoteGroup = new Vex.Flow.GraceNoteGroup(graceNotes, graceSlur);
-                    (gve as VexFlowVoiceEntry).vfStaveNote.addModifier(0, graceNoteGroup.beamNotes());
+                    (gve as VexFlowVoiceEntry).vfStaveNote.addModifier(0, graceNoteGroup);
                     graceGVoiceEntriesBefore = [];
                 }
             }
         }
+        // remaining grace notes at end of measure, turned into stand-alone grace notes:
+        if (graceGVoiceEntriesBefore.length > 0) {
+            for (const graceGve of graceGVoiceEntriesBefore) {
+                (graceGve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(graceGve);
+                graceGve.parentVoiceEntry.GraceAfterMainNote = true;
+            }
+        }
 
         this.finalizeBeams();
         this.finalizeTuplets();
@@ -674,12 +793,17 @@ export class VexFlowMeasure extends GraphicalMeasure {
             // create vex flow voices and add tickables to it:
             for (const voiceEntry of restFilledEntries) {
                 if (voiceEntry.parentVoiceEntry) {
-                    if (voiceEntry.parentVoiceEntry.IsGrace) {
+                    if (voiceEntry.parentVoiceEntry.IsGrace && !voiceEntry.parentVoiceEntry.GraceAfterMainNote) {
                         continue;
                     }
                 }
 
                 const vexFlowVoiceEntry: VexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry;
+                if (voiceEntry.notes.length === 0 || !voiceEntry.notes[0] || !voiceEntry.notes[0].sourceNote.PrintObject) {
+                    // GhostNote, don't add modifiers like in-measure clefs
+                    this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
+                    continue;
+                }
 
                 // check for in-measure clefs:
                 // only add clefs in main voice (to not add them twice)
@@ -691,11 +815,52 @@ export class VexFlowMeasure extends GraphicalMeasure {
                         vexFlowVoiceEntry.vfStaveNote.addModifier(0, clefModifier);
                     }
                 }
+
+                // add fingering
+                if (voiceEntry.parentVoiceEntry && EngravingRules.Rules.RenderFingerings) {
+                    this.createFingerings(voiceEntry);
+                }
+
+                // add Arpeggio
+                if (voiceEntry.parentVoiceEntry && voiceEntry.parentVoiceEntry.Arpeggio !== undefined) {
+                    const type: ArpeggioType = voiceEntry.parentVoiceEntry.Arpeggio.type;
+                    vexFlowVoiceEntry.vfStaveNote.addStroke(0, new Vex.Flow.Stroke(type));
+                }
+
                 this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
             }
         }
         this.createArticulations();
         this.createOrnaments();
+        this.setStemDirectionFromVexFlow();
+    }
+
+    /**
+     * Copy the stem directions chosen by VexFlow to the StemDirection variable of the graphical notes
+     */
+    private setStemDirectionFromVexFlow(): void {
+        //if StemDirection was not set then read out what VexFlow has chosen
+        for ( const vfStaffEntry of this.staffEntries ) {
+            for ( const gVoiceEntry of vfStaffEntry.graphicalVoiceEntries) {
+                for ( const gnote of gVoiceEntry.notes) {
+                    const vfnote: [StaveNote, number] = (gnote as VexFlowGraphicalNote).vfnote;
+                    if (vfnote === undefined || vfnote[0] === undefined) {
+                        continue;
+                    }
+
+                    const vfStemDir: number = vfnote[0].getStemDirection();
+                    switch (vfStemDir) {
+                        case (Vex.Flow.Stem.UP):
+                            gVoiceEntry.parentVoiceEntry.StemDirection = StemDirectionType.Up;
+                            break;
+                        case (Vex.Flow.Stem.DOWN):
+                            gVoiceEntry.parentVoiceEntry.StemDirection = StemDirectionType.Down;
+                            break;
+                        default:
+                    }
+                }
+            }
+        }
     }
 
     /**
@@ -708,9 +873,6 @@ export class VexFlowMeasure extends GraphicalMeasure {
             // create vex flow articulation:
             const graphicalVoiceEntries: GraphicalVoiceEntry[] = graphicalStaffEntry.graphicalVoiceEntries;
             for (const gve of graphicalVoiceEntries) {
-                if (gve.parentVoiceEntry.IsGrace) {
-                    continue;
-                }
                 const vfStaveNote: StemmableNote = (gve as VexFlowVoiceEntry).vfStaveNote;
                 VexFlowConverter.generateArticulations(vfStaveNote, gve.notes[0].sourceNote.ParentVoiceEntry.Articulations);
             }
@@ -728,14 +890,78 @@ export class VexFlowMeasure extends GraphicalMeasure {
             for (const voiceID in gvoices) {
                 if (gvoices.hasOwnProperty(voiceID)) {
                     const vfStaveNote: StemmableNote = (gvoices[voiceID] as VexFlowVoiceEntry).vfStaveNote;
-                    if (gvoices[voiceID].notes[0].sourceNote.ParentVoiceEntry.OrnamentContainer !== undefined) {
-                        VexFlowConverter.generateOrnaments(vfStaveNote, gvoices[voiceID].notes[0].sourceNote.ParentVoiceEntry.OrnamentContainer);
+                    const ornamentContainer: OrnamentContainer = gvoices[voiceID].notes[0].sourceNote.ParentVoiceEntry.OrnamentContainer;
+                    if (ornamentContainer !== undefined) {
+                        VexFlowConverter.generateOrnaments(vfStaveNote, ornamentContainer);
                     }
                 }
             }
         }
     }
 
+    private createFingerings(voiceEntry: GraphicalVoiceEntry): void {
+        const vexFlowVoiceEntry: VexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry;
+        const technicalInstructions: TechnicalInstruction[] = voiceEntry.parentVoiceEntry.TechnicalInstructions;
+        const fingeringsCount: number = technicalInstructions.length;
+        for (let i: number = 0; i < technicalInstructions.length; i++) {
+            const technicalInstruction: TechnicalInstruction = technicalInstructions[i];
+            let fingeringPosition: PlacementEnum = EngravingRules.Rules.FingeringPosition;
+            if (technicalInstruction.placement !== PlacementEnum.NotYetDefined) {
+                fingeringPosition = technicalInstruction.placement;
+            }
+            let modifierPosition: any; // Vex.Flow.Modifier.Position
+            switch (fingeringPosition) {
+                default:
+                case PlacementEnum.Left:
+                    modifierPosition = Vex.Flow.Modifier.Position.LEFT;
+                    break;
+                case PlacementEnum.Right:
+                    modifierPosition = Vex.Flow.Modifier.Position.RIGHT;
+                    break;
+                case PlacementEnum.Above:
+                    modifierPosition = Vex.Flow.Modifier.Position.ABOVE;
+                    break;
+                case PlacementEnum.Below:
+                    modifierPosition = Vex.Flow.Modifier.Position.BELOW;
+                    break;
+                case PlacementEnum.NotYetDefined: // automatic fingering placement, could be more complex/customizable
+                    const sourceStaff: Staff = voiceEntry.parentStaffEntry.sourceStaffEntry.ParentStaff;
+                    if (voiceEntry.notes.length > 1 || voiceEntry.parentStaffEntry.graphicalVoiceEntries.length > 1) {
+                        modifierPosition = Vex.Flow.Modifier.Position.LEFT;
+                    } else if (sourceStaff.idInMusicSheet === 0) {
+                        modifierPosition = Vex.Flow.Modifier.Position.ABOVE;
+                        fingeringPosition = PlacementEnum.Above;
+                    } else {
+                        modifierPosition = Vex.Flow.Modifier.Position.BELOW;
+                        fingeringPosition = PlacementEnum.Below;
+                    }
+            }
+
+            const fretFinger: Vex.Flow.FretHandFinger = new Vex.Flow.FretHandFinger(technicalInstruction.value);
+            fretFinger.setPosition(modifierPosition);
+            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 ? i :
+                    technicalInstructions.length - 1 - i; // reverse order for fingerings below staff
+                if (EngravingRules.Rules.FingeringInsideStafflines && fingeringsCount > 1) { // y-shift for single fingering is ok
+                    // experimental, bounding boxes wrong for fretFinger above/below, better would be creating Labels
+                    // 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);
+                } else if (!EngravingRules.Rules.FingeringInsideStafflines) { // use StringNumber for placement above/below stafflines
+                    const stringNumber: Vex.Flow.StringNumber = new Vex.Flow.StringNumber(technicalInstruction.value);
+                    (<any>stringNumber).radius = 0; // hack to remove the circle around the number
+                    stringNumber.setPosition(modifierPosition);
+                    stringNumber.setOffsetY(offsetYSign * ordering * stringNumber.getWidth() * 2 / 3);
+                    vexFlowVoiceEntry.vfStaveNote.addModifier(i, stringNumber);
+                    continue;
+                }
+            }
+            vexFlowVoiceEntry.vfStaveNote.addModifier(i, fretFinger);
+        }
+    }
+
     /**
      * Creates a line from 'top' to this measure, of type 'lineType'
      * @param top

+ 389 - 467
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -1,38 +1,37 @@
-import {MusicSheetCalculator} from "../MusicSheetCalculator";
-import {VexFlowGraphicalSymbolFactory} from "./VexFlowGraphicalSymbolFactory";
-import {GraphicalMeasure} from "../GraphicalMeasure";
-import {StaffLine} from "../StaffLine";
-import {VoiceEntry} from "../../VoiceData/VoiceEntry";
-import {GraphicalNote} from "../GraphicalNote";
-import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
-import {GraphicalTie} from "../GraphicalTie";
-import {Tie} from "../../VoiceData/Tie";
-import {SourceMeasure} from "../../VoiceData/SourceMeasure";
-import {MultiExpression} from "../../VoiceData/Expressions/MultiExpression";
-import {RepetitionInstruction} from "../../VoiceData/Instructions/RepetitionInstruction";
-import {Beam} from "../../VoiceData/Beam";
-import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
-import {OctaveEnum, OctaveShift} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
-import {Fraction} from "../../../Common/DataObjects/Fraction";
-import {LyricWord} from "../../VoiceData/Lyrics/LyricsWord";
-import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
-import {ArticulationEnum} from "../../VoiceData/VoiceEntry";
-import {Tuplet} from "../../VoiceData/Tuplet";
-import {VexFlowMeasure} from "./VexFlowMeasure";
-import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
+import { MusicSheetCalculator } from "../MusicSheetCalculator";
+import { VexFlowGraphicalSymbolFactory } from "./VexFlowGraphicalSymbolFactory";
+import { GraphicalMeasure } from "../GraphicalMeasure";
+import { StaffLine } from "../StaffLine";
+import { VoiceEntry } from "../../VoiceData/VoiceEntry";
+import { GraphicalNote } from "../GraphicalNote";
+import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
+import { GraphicalTie } from "../GraphicalTie";
+import { Tie } from "../../VoiceData/Tie";
+import { SourceMeasure } from "../../VoiceData/SourceMeasure";
+import { MultiExpression } from "../../VoiceData/Expressions/MultiExpression";
+import { RepetitionInstruction } from "../../VoiceData/Instructions/RepetitionInstruction";
+import { Beam } from "../../VoiceData/Beam";
+import { ClefInstruction } from "../../VoiceData/Instructions/ClefInstruction";
+import { OctaveEnum, OctaveShift } from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
+import { Fraction } from "../../../Common/DataObjects/Fraction";
+import { LyricWord } from "../../VoiceData/Lyrics/LyricsWord";
+import { OrnamentContainer } from "../../VoiceData/OrnamentContainer";
+import { ArticulationEnum } from "../../VoiceData/VoiceEntry";
+import { Tuplet } from "../../VoiceData/Tuplet";
+import { VexFlowMeasure } from "./VexFlowMeasure";
+import { VexFlowTextMeasurer } from "./VexFlowTextMeasurer";
 import Vex = require("vexflow");
 import * as log from "loglevel";
-import {unitInPixels} from "./VexFlowMusicSheetDrawer";
-import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
-import {TechnicalInstruction} from "../../VoiceData/Instructions/TechnicalInstruction";
-import {GraphicalLyricEntry} from "../GraphicalLyricEntry";
-import {GraphicalLabel} from "../GraphicalLabel";
-import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
-import {GraphicalLyricWord} from "../GraphicalLyricWord";
-import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
+import { unitInPixels } from "./VexFlowMusicSheetDrawer";
+import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote";
+import { TechnicalInstruction } from "../../VoiceData/Instructions/TechnicalInstruction";
+import { GraphicalLyricEntry } from "../GraphicalLyricEntry";
+import { GraphicalLabel } from "../GraphicalLabel";
+import { LyricsEntry } from "../../VoiceData/Lyrics/LyricsEntry";
+import { GraphicalLyricWord } from "../GraphicalLyricWord";
+import { VexFlowStaffEntry } from "./VexFlowStaffEntry";
 import { VexFlowOctaveShift } from "./VexFlowOctaveShift";
 import { VexFlowInstantaneousDynamicExpression } from "./VexFlowInstantaneousDynamicExpression";
-import {BoundingBox} from "../BoundingBox";
 import { Slur } from "../../VoiceData/Expressions/ContinuousExpressions/Slur";
 /* VexFlow Version - for later use
 // import { VexFlowSlur } from "./VexFlowSlur";
@@ -40,14 +39,12 @@ import { Slur } from "../../VoiceData/Expressions/ContinuousExpressions/Slur";
 // import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
 */
 import { EngravingRules } from "../EngravingRules";
-import { InstantaneousDynamicExpression } from "../../VoiceData/Expressions/InstantaneousDynamicExpression";
 import { PointF2D } from "../../../Common/DataObjects/PointF2D";
-import { GraphicalInstantaneousDynamicExpression } from "../GraphicalInstantaneousDynamicExpression";
-import { SkyBottomLineCalculator } from "../SkyBottomLineCalculator";
-import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
-import { Staff } from "../../VoiceData/Staff";
 import { TextAlignmentEnum, TextAlignment } from "../../../Common/Enums/TextAlignment";
 import { GraphicalSlur } from "../GraphicalSlur";
+import { BoundingBox } from "../BoundingBox";
+import { ContinuousDynamicExpression } from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
+import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
 
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   /** space needed for a dash for lyrics spacing, calculated once */
@@ -68,17 +65,17 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
   }
 
-    protected formatMeasures(): void {
-      for (const verticalMeasureList of this.graphicalMusicSheet.MeasureList) {
-        const firstMeasure: VexFlowMeasure = verticalMeasureList[0] as VexFlowMeasure;
-        // first measure has formatting method as lambda function object, but formats all measures. TODO this could be refactored
-        firstMeasure.format();
-        for (const measure of verticalMeasureList) {
-          for (const staffEntry of measure.staffEntries) {
-            (<VexFlowStaffEntry>staffEntry).calculateXPosition();
-          }
+  protected formatMeasures(): void {
+    for (const verticalMeasureList of this.graphicalMusicSheet.MeasureList) {
+      const firstMeasure: VexFlowMeasure = verticalMeasureList[0] as VexFlowMeasure;
+      // first measure has formatting method as lambda function object, but formats all measures. TODO this could be refactored
+      firstMeasure.format();
+      for (const measure of verticalMeasureList) {
+        for (const staffEntry of measure.staffEntries) {
+          (<VexFlowStaffEntry>staffEntry).calculateXPosition();
         }
       }
+    }
   }
 
   //protected clearSystemsAndMeasures(): void {
@@ -109,56 +106,56 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     const formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter();
 
     for (const measure of measures) {
-        const mvoices:  { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
-        const voices: Vex.Flow.Voice[] = [];
-        for (const voiceID in mvoices) {
-            if (mvoices.hasOwnProperty(voiceID)) {
-                voices.push(mvoices[voiceID]);
-                allVoices.push(mvoices[voiceID]);
-            }
+      const mvoices: { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
+      const voices: Vex.Flow.Voice[] = [];
+      for (const voiceID in mvoices) {
+        if (mvoices.hasOwnProperty(voiceID)) {
+          voices.push(mvoices[voiceID]);
+          allVoices.push(mvoices[voiceID]);
         }
-        if (voices.length === 0) {
-            log.info("Found a measure with no voices. Continuing anyway.", mvoices);
-            continue;
-        }
-        // all voices that belong to one stave are collectively added to create a common context in VexFlow.
-        formatter.joinVoices(voices);
+      }
+      if (voices.length === 0) {
+        log.info("Found a measure with no voices. Continuing anyway.", mvoices);
+        continue;
+      }
+      // all voices that belong to one stave are collectively added to create a common context in VexFlow.
+      formatter.joinVoices(voices);
     }
 
     let minStaffEntriesWidth: number = 200;
     if (allVoices.length > 0) {
-        // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
-        // FIXME: a more relaxed formatting of voices
-        minStaffEntriesWidth = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels + 5.0;
-        // firstMeasure.formatVoices = (w: number) => {
-        //     formatter.format(allVoices, w);
-        // };
-        MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minStaffEntriesWidth);
-        for (const measure of measures) {
-          if (measure === measures[0]) {
-            const vexflowMeasure: VexFlowMeasure = (measure as VexFlowMeasure);
-            // prepare format function for voices, will be called later for formatting measure again
-            vexflowMeasure.formatVoices = (w: number) => {
-                    formatter.format(allVoices, w);
-              // formatter.format(allVoices, w, {
-              //   align_rests: false, // TODO
-              //   // align_rests = true causes a Vexflow Exception for Mozart - An Chloe
-              //   // align_rests = false still aligns rests with beams according to Vexflow, but doesn't seem to do anything
-              // });
-                };
-            // format now for minimum width, calculateMeasureWidthFromLyrics later
-            vexflowMeasure.formatVoices(minStaffEntriesWidth * unitInPixels);
-          } else {
-            (measure as VexFlowMeasure).formatVoices = undefined;
-            }
+      // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
+      // FIXME: a more relaxed formatting of voices
+      minStaffEntriesWidth = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels + 5.0;
+      // firstMeasure.formatVoices = (w: number) => {
+      //     formatter.format(allVoices, w);
+      // };
+      MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minStaffEntriesWidth);
+      for (const measure of measures) {
+        if (measure === measures[0]) {
+          const vexflowMeasure: VexFlowMeasure = (measure as VexFlowMeasure);
+          // prepare format function for voices, will be called later for formatting measure again
+          vexflowMeasure.formatVoices = (w: number) => {
+            formatter.format(allVoices, w);
+            // formatter.format(allVoices, w, {
+            //   align_rests: false, // TODO
+            //   // align_rests = true causes a Vexflow Exception for Mozart - An Chloe
+            //   // align_rests = false still aligns rests with beams according to Vexflow, but doesn't seem to do anything
+            // });
+          };
+          // format now for minimum width, calculateMeasureWidthFromLyrics later
+          vexflowMeasure.formatVoices(minStaffEntriesWidth * unitInPixels);
+        } else {
+          (measure as VexFlowMeasure).formatVoices = undefined;
         }
+      }
     }
 
     for (const graphicalMeasure of measures) {
       for (const staffEntry of graphicalMeasure.staffEntries) {
         // here the measure modifiers are not yet set, therefore the begin instruction width will be empty
         (<VexFlowStaffEntry>staffEntry).calculateXPosition();
-  }
+      }
     }
     // calculateMeasureWidthFromLyrics() will be called from MusicSheetCalculator after this
     return minStaffEntriesWidth;
@@ -314,7 +311,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
 
 
   protected updateStaffLineBorders(staffLine: StaffLine): void {
-      staffLine.SkyBottomLineCalculator.updateStaffLineBorders();
+    staffLine.SkyBottomLineCalculator.updateStaffLineBorders();
   }
 
   protected graphicalMeasureCreatedCalculations(measure: GraphicalMeasure): void {
@@ -350,15 +347,15 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    */
   protected calculateSystemYLayout(): void {
     for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-            for (const musicSystem of graphicalMusicPage.MusicSystems) {
-                this.optimizeDistanceBetweenStaffLines(musicSystem);
-          }
-
-          // set y positions of systems using the previous system and a fixed distance.
-            this.calculateMusicSystemsRelativePositions(graphicalMusicPage);
-        }
+      for (const musicSystem of graphicalMusicPage.MusicSystems) {
+        this.optimizeDistanceBetweenStaffLines(musicSystem);
       }
 
+      // set y positions of systems using the previous system and a fixed distance.
+      this.calculateMusicSystemsRelativePositions(graphicalMusicPage);
+    }
+  }
+
   /**
    * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
    */
@@ -379,25 +376,25 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     return;
   }
 
-    /**
-     * Calculate the shape (Bezier curve) for this tie.
-     * @param tie
-     * @param tieIsAtSystemBreak
-     */
+  /**
+   * Calculate the shape (Bezier curve) for this tie.
+   * @param tie
+   * @param tieIsAtSystemBreak
+   */
   protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
     const startNote: VexFlowGraphicalNote = (tie.StartNote as VexFlowGraphicalNote);
     const endNote: VexFlowGraphicalNote = (tie.EndNote as VexFlowGraphicalNote);
 
     let vfStartNote: Vex.Flow.StaveNote = undefined;
     let startNoteIndexInTie: number = 0;
-    if (startNote !== undefined) {
+    if (startNote !== undefined && startNote.vfnote !== undefined && startNote.vfnote.length >= 2) {
       vfStartNote = startNote.vfnote[0];
       startNoteIndexInTie = startNote.vfnote[1];
     }
 
     let vfEndNote: Vex.Flow.StaveNote = undefined;
     let endNoteIndexInTie: number = 0;
-    if (endNote !== undefined) {
+    if (endNote !== undefined && endNote.vfnote !== undefined && endNote.vfnote.length >= 2) {
       vfEndNote = endNote.vfnote[0];
       endNoteIndexInTie = endNote.vfnote[1];
     }
@@ -436,127 +433,50 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     const absoluteTimestamp: Fraction = multiExpression.AbsoluteTimestamp;
     const measures: GraphicalMeasure[] = this.graphicalMusicSheet.MeasureList[measureIndex];
     const staffLine: StaffLine = measures[staffIndex].ParentStaffLine;
+    const startMeasure: GraphicalMeasure = measures[staffIndex];
 
-    if (multiExpression.InstantaneousDynamic) {
-        const instantaneousDynamic: InstantaneousDynamicExpression = multiExpression.InstantaneousDynamic;
-
-        const startPosInStaffline: PointF2D = this.getRelativePositionInStaffLineFromTimestamp(
-          absoluteTimestamp,
-          staffIndex,
-          staffLine,
-          staffLine.isPartOfMultiStaffInstrument());
-        if (Math.abs(startPosInStaffline.x) === 0) {
-          startPosInStaffline.x = measures[staffIndex].beginInstructionsWidth + this.rules.RhythmRightMargin;
-        }
-        const measure: GraphicalMeasure = this.graphicalMusicSheet.MeasureList[measureIndex][staffIndex];
-        const graphicalInstantaneousDynamic: VexFlowInstantaneousDynamicExpression = new VexFlowInstantaneousDynamicExpression(
-          instantaneousDynamic,
-          staffLine,
-          measure);
-        (measure as VexFlowMeasure).instantaneousDynamics.push(graphicalInstantaneousDynamic);
-        this.calculateGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic, staffLine, startPosInStaffline);
-    }
-  }
+    const startPosInStaffline: PointF2D = this.getRelativePositionInStaffLineFromTimestamp(
+      absoluteTimestamp,
+      staffIndex,
+      staffLine,
+      staffLine.isPartOfMultiStaffInstrument());
 
-  public calculateGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic: VexFlowInstantaneousDynamicExpression,
-                                                          staffLine: StaffLine,
-                                                          relative: PointF2D): void {
-    // // add to StaffLine and set PSI relations
-    staffLine.AbstractExpressions.push(graphicalInstantaneousDynamic);
-    staffLine.PositionAndShape.ChildElements.push(graphicalInstantaneousDynamic.PositionAndShape);
-    if (this.staffLinesWithGraphicalExpressions.indexOf(staffLine) === -1) {
-        this.staffLinesWithGraphicalExpressions.push(staffLine);
+    const dynamicStartPosition: PointF2D = startPosInStaffline;
+    if (startPosInStaffline.x <= 0) {
+      dynamicStartPosition.x = startMeasure.beginInstructionsWidth + this.rules.RhythmRightMargin;
     }
 
-    // get Margin Dimensions
-    const left: number = relative.x + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginLeft;
-    const right: number = relative.x + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginRight;
-    const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
-
-    // get possible previous Dynamic
-    let previousExpression: GraphicalInstantaneousDynamicExpression = undefined;
-    const expressionIndex: number = staffLine.AbstractExpressions.indexOf(graphicalInstantaneousDynamic);
-    if (expressionIndex > 0) {
-        previousExpression = (staffLine.AbstractExpressions[expressionIndex - 1] as GraphicalInstantaneousDynamicExpression);
+    if (multiExpression.InstantaneousDynamic) {
+      const graphicalInstantaneousDynamic: VexFlowInstantaneousDynamicExpression = new VexFlowInstantaneousDynamicExpression(
+        multiExpression.InstantaneousDynamic,
+        staffLine,
+        startMeasure);
+      this.calculateGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic, dynamicStartPosition);
     }
-
-    // TODO: Not yet implemented
-    // // is previous a ContinuousDynamic?
-    // if (previousExpression && previousExpression instanceof GraphicalContinuousDynamicExpression)
-    // {
-    //     GraphicalContinuousDynamicExpression formerGraphicalContinuousDynamic =
-    //         (GraphicalContinuousDynamicExpression)previousExpression;
-
-    //     optimizeFormerContDynamicXPositionForInstDynamic(staffLine, skyBottomLineCalculator,
-    //                                                      graphicalInstantaneousDynamic,
-    //                                                      formerGraphicalContinuousDynamic, left, right);
-    // }
-    // // is previous a instantaneousDynamic?
-    // else
-    if (previousExpression && previousExpression instanceof GraphicalInstantaneousDynamicExpression) {
-        //const formerGraphicalInstantaneousDynamic: GraphicalInstantaneousDynamicExpression = previousExpression;
-
-        // optimizeFormerInstDynamicXPositionForInstDynamic(formerGraphicalInstantaneousDynamic,
-        //                                                  graphicalInstantaneousDynamic, ref relative, ref left, ref right);
-    }// End x-positioning overlap check
-
-    // calculate yPosition according to Placement
-    if (graphicalInstantaneousDynamic.InstantaneousDynamic.Placement === PlacementEnum.Above) {
-        const skyLineValue: number = skyBottomLineCalculator.getSkyLineMinInRange(left, right);
-        let yPosition: number = 0;
-
-        // if StaffLine part of multiStafff Instrument and not the first one, ideal yPosition middle of distance between Staves
-        if (staffLine.isPartOfMultiStaffInstrument() && staffLine.ParentStaff !== staffLine.ParentStaff.ParentInstrument.Staves[0]) {
-            const formerStaffLine: StaffLine = staffLine.ParentMusicSystem.StaffLines[staffLine.ParentMusicSystem.StaffLines.indexOf(staffLine) - 1];
-            const difference: number = staffLine.PositionAndShape.RelativePosition.y -
-                               formerStaffLine.PositionAndShape.RelativePosition.y - this.rules.StaffHeight;
-
-            // take always into account the size of the Dynamic
-            if (skyLineValue > -difference / 2) {
-                yPosition = -difference / 2;
-            } else {
-                yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
-            }
-        } else {
-            yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
-        }
-
-        graphicalInstantaneousDynamic.PositionAndShape.RelativePosition = new PointF2D(relative.x, yPosition);
-        skyBottomLineCalculator.updateSkyLineInRange(left, right, yPosition + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop);
-    } else if (graphicalInstantaneousDynamic.InstantaneousDynamic.Placement === PlacementEnum.Below) {
-        const bottomLineValue: number = skyBottomLineCalculator.getBottomLineMaxInRange(left, right);
-        let yPosition: number = 0;
-
-        // if StaffLine part of multiStafff Instrument and not the last one, ideal yPosition middle of distance between Staves
-        const lastStaff: Staff = staffLine.ParentStaff.ParentInstrument.Staves[staffLine.ParentStaff.ParentInstrument.Staves.length - 1];
-        if (staffLine.isPartOfMultiStaffInstrument() && staffLine.ParentStaff !== lastStaff) {
-            const nextStaffLine: StaffLine = staffLine.ParentMusicSystem.StaffLines[staffLine.ParentMusicSystem.StaffLines.indexOf(staffLine) + 1];
-            const difference: number = nextStaffLine.PositionAndShape.RelativePosition.y -
-                               staffLine.PositionAndShape.RelativePosition.y - this.rules.StaffHeight;
-            const border: number = graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
-
-            // take always into account the size of the Dynamic
-            if (bottomLineValue + border < this.rules.StaffHeight + difference / 2) {
-                yPosition = this.rules.StaffHeight + difference / 2;
-            } else {
-                yPosition = bottomLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop;
-            }
-        } else {
-            yPosition = bottomLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop;
-        }
-
-        graphicalInstantaneousDynamic.PositionAndShape.RelativePosition = new PointF2D(relative.x, yPosition);
-        skyBottomLineCalculator.updateBottomLineInRange(left, right, yPosition + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom);
+    if (multiExpression.StartingContinuousDynamic) {
+      const continuousDynamic: ContinuousDynamicExpression = multiExpression.StartingContinuousDynamic;
+      const graphicalContinuousDynamic: VexFlowContinuousDynamicExpression = new VexFlowContinuousDynamicExpression(
+        multiExpression.StartingContinuousDynamic,
+        staffLine);
+      graphicalContinuousDynamic.StartMeasure = startMeasure;
+
+      if (!graphicalContinuousDynamic.IsVerbal && continuousDynamic.EndMultiExpression) {
+        this.calculateGraphicalContinuousDynamic(graphicalContinuousDynamic, dynamicStartPosition);
+      } else if (graphicalContinuousDynamic.IsVerbal) {
+        this.calculateGraphicalVerbalContinuousDynamic(graphicalContinuousDynamic, dynamicStartPosition);
+      } else {
+        log.warn("This continous dynamic is not covered");
+      }
     }
-}
+  }
 
-    /**
-     * Calculate a single OctaveShift for a [[MultiExpression]].
-     * @param sourceMeasure
-     * @param multiExpression
-     * @param measureIndex
-     * @param staffIndex
-     */
+  /**
+   * Calculate a single OctaveShift for a [[MultiExpression]].
+   * @param sourceMeasure
+   * @param multiExpression
+   * @param measureIndex
+   * @param staffIndex
+   */
   protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
     // calculate absolute Timestamp and startStaffLine (and EndStaffLine if needed)
     const octaveShift: OctaveShift = multiExpression.OctaveShiftStart;
@@ -568,9 +488,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
 
     let endMeasure: GraphicalMeasure = undefined;
     if (octaveShift.ParentEndMultiExpression !== undefined) {
-        endMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(octaveShift.ParentEndMultiExpression.SourceMeasureParent,
-                                                                                           staffIndex);
-  }
+      endMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(octaveShift.ParentEndMultiExpression.SourceMeasureParent,
+                                                                                         staffIndex);
+    }
     let startMeasure: GraphicalMeasure = undefined;
     if (octaveShift.ParentEndMultiExpression !== undefined) {
       startMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(octaveShift.ParentStartMultiExpression.SourceMeasureParent,
@@ -578,76 +498,76 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
 
     if (endMeasure !== undefined) {
-        // calculate GraphicalOctaveShift and RelativePositions
-        const graphicalOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, startStaffLine.PositionAndShape);
-        startStaffLine.OctaveShifts.push(graphicalOctaveShift);
-
-        // calculate RelativePosition and Dashes
-        const startStaffEntry: GraphicalStaffEntry = startMeasure.findGraphicalStaffEntryFromTimestamp(startTimeStamp);
-        const endStaffEntry: GraphicalStaffEntry = endMeasure.findGraphicalStaffEntryFromTimestamp(endTimeStamp);
-
-        graphicalOctaveShift.setStartNote(startStaffEntry);
-
-        if (endMeasure.ParentStaffLine !== startMeasure.ParentStaffLine) {
-          graphicalOctaveShift.endsOnDifferentStaffLine = true;
-          const lastMeasure: GraphicalMeasure = startMeasure.ParentStaffLine.Measures[startMeasure.ParentStaffLine.Measures.length - 1];
-          const lastNote: GraphicalStaffEntry = lastMeasure.staffEntries[lastMeasure.staffEntries.length - 1];
-          graphicalOctaveShift.setEndNote(lastNote);
-
-          // Now finish the shift on the next line
-          const remainingOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, endMeasure.PositionAndShape);
-          endMeasure.ParentStaffLine.OctaveShifts.push(remainingOctaveShift);
-          const firstMeasure: GraphicalMeasure = endMeasure.ParentStaffLine.Measures[0];
-          const firstNote: GraphicalStaffEntry = firstMeasure.staffEntries[0];
-          remainingOctaveShift.setStartNote(firstNote);
-          remainingOctaveShift.setEndNote(endStaffEntry);
-        } else {
-          graphicalOctaveShift.setEndNote(endStaffEntry);
-        }
+      // calculate GraphicalOctaveShift and RelativePositions
+      const graphicalOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, startStaffLine.PositionAndShape);
+      startStaffLine.OctaveShifts.push(graphicalOctaveShift);
+
+      // calculate RelativePosition and Dashes
+      const startStaffEntry: GraphicalStaffEntry = startMeasure.findGraphicalStaffEntryFromTimestamp(startTimeStamp);
+      const endStaffEntry: GraphicalStaffEntry = endMeasure.findGraphicalStaffEntryFromTimestamp(endTimeStamp);
+
+      graphicalOctaveShift.setStartNote(startStaffEntry);
+
+      if (endMeasure.ParentStaffLine !== startMeasure.ParentStaffLine) {
+        graphicalOctaveShift.endsOnDifferentStaffLine = true;
+        const lastMeasure: GraphicalMeasure = startMeasure.ParentStaffLine.Measures[startMeasure.ParentStaffLine.Measures.length - 1];
+        const lastNote: GraphicalStaffEntry = lastMeasure.staffEntries[lastMeasure.staffEntries.length - 1];
+        graphicalOctaveShift.setEndNote(lastNote);
+
+        // Now finish the shift on the next line
+        const remainingOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, endMeasure.PositionAndShape);
+        endMeasure.ParentStaffLine.OctaveShifts.push(remainingOctaveShift);
+        const firstMeasure: GraphicalMeasure = endMeasure.ParentStaffLine.Measures[0];
+        const firstNote: GraphicalStaffEntry = firstMeasure.staffEntries[0];
+        remainingOctaveShift.setStartNote(firstNote);
+        remainingOctaveShift.setEndNote(endStaffEntry);
+      } else {
+        graphicalOctaveShift.setEndNote(endStaffEntry);
+      }
     } else {
       log.warn("End measure for octave shift is undefined! This should not happen!");
     }
   }
 
-    /**
-     * Calculate all the textual and symbolic [[RepetitionInstruction]]s (e.g. dal segno) for a single [[SourceMeasure]].
-     * @param repetitionInstruction
-     * @param measureIndex
-     */
+  /**
+   * Calculate all the textual and symbolic [[RepetitionInstruction]]s (e.g. dal segno) for a single [[SourceMeasure]].
+   * @param repetitionInstruction
+   * @param measureIndex
+   */
   protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction, measureIndex: number): void {
-      // find first visible StaffLine
-      let uppermostMeasure: VexFlowMeasure = undefined;
-      const measures: VexFlowMeasure[]  = <VexFlowMeasure[]>this.graphicalMusicSheet.MeasureList[measureIndex];
-      for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
-        const graphicalMeasure: VexFlowMeasure = measures[idx];
-        if (graphicalMeasure.ParentStaffLine !== undefined && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
-            uppermostMeasure = <VexFlowMeasure>graphicalMeasure;
-            break;
-        }
-      }
-      // ToDo: feature/Repetitions
-      // now create corresponding graphical symbol or Text in VexFlow:
-      // use top measure and staffline for positioning.
-      if (uppermostMeasure !== undefined) {
-        uppermostMeasure.addWordRepetition(repetitionInstruction);
+    // find first visible StaffLine
+    let uppermostMeasure: VexFlowMeasure = undefined;
+    const measures: VexFlowMeasure[] = <VexFlowMeasure[]>this.graphicalMusicSheet.MeasureList[measureIndex];
+    for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
+      const graphicalMeasure: VexFlowMeasure = measures[idx];
+      if (graphicalMeasure.ParentStaffLine !== undefined && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
+        uppermostMeasure = <VexFlowMeasure>graphicalMeasure;
+        break;
       }
     }
+    // ToDo: feature/Repetitions
+    // now create corresponding graphical symbol or Text in VexFlow:
+    // use top measure and staffline for positioning.
+    if (uppermostMeasure !== undefined) {
+      uppermostMeasure.addWordRepetition(repetitionInstruction);
+    }
+  }
 
   protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
     return;
   }
 
-    /**
-     * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.
-     * @param tiedGraphicalNote
-     * @param beams
-     * @param activeClef
-     * @param octaveShiftValue
-     * @param graphicalStaffEntry
-     * @param duration
-     * @param openTie
-     * @param isLastTieNote
-     */
+  /**
+   * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.
+   * @param tiedGraphicalNote
+   * @param beams
+   * @param activeClef
+   * @param octaveShiftValue
+   * @param graphicalStaffEntry
+   * @param duration
+   * @param openTie
+   * @param isLastTieNote
+   */
   protected handleTiedGraphicalNote(tiedGraphicalNote: GraphicalNote, beams: Beam[], activeClef: ClefInstruction,
                                     octaveShiftValue: OctaveEnum, graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction,
                                     openTie: Tie, isLastTieNote: boolean): void {
@@ -665,45 +585,45 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   }
 
   protected handleVoiceEntryLyrics(voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry, lyricWords: LyricWord[]): void {
-      voiceEntry.LyricsEntries.forEach((key: number, lyricsEntry: LyricsEntry) => {
-          const graphicalLyricEntry: GraphicalLyricEntry = new GraphicalLyricEntry(lyricsEntry,
-                                                                                   graphicalStaffEntry,
-                                                                                   this.rules.LyricsHeight,
-                                                                                   this.rules.StaffHeight);
-
-          graphicalStaffEntry.LyricsEntries.push(graphicalLyricEntry);
-
-          // create corresponding GraphicalLabel
-          const graphicalLabel: GraphicalLabel = graphicalLyricEntry.GraphicalLabel;
-          graphicalLabel.setLabelPositionAndShapeBorders();
-
-          if (lyricsEntry.Word !== undefined) {
-              const lyricsEntryIndex: number = lyricsEntry.Word.Syllables.indexOf(lyricsEntry);
-              let index: number = lyricWords.indexOf(lyricsEntry.Word);
-              if (index === -1) {
-                  lyricWords.push(lyricsEntry.Word);
-                  index = lyricWords.indexOf(lyricsEntry.Word);
-              }
+    voiceEntry.LyricsEntries.forEach((key: number, lyricsEntry: LyricsEntry) => {
+      const graphicalLyricEntry: GraphicalLyricEntry = new GraphicalLyricEntry(lyricsEntry,
+                                                                               graphicalStaffEntry,
+                                                                               this.rules.LyricsHeight,
+                                                                               this.rules.StaffHeight);
+
+      graphicalStaffEntry.LyricsEntries.push(graphicalLyricEntry);
+
+      // create corresponding GraphicalLabel
+      const graphicalLabel: GraphicalLabel = graphicalLyricEntry.GraphicalLabel;
+      graphicalLabel.setLabelPositionAndShapeBorders();
+
+      if (lyricsEntry.Word !== undefined) {
+        const lyricsEntryIndex: number = lyricsEntry.Word.Syllables.indexOf(lyricsEntry);
+        let index: number = lyricWords.indexOf(lyricsEntry.Word);
+        if (index === -1) {
+          lyricWords.push(lyricsEntry.Word);
+          index = lyricWords.indexOf(lyricsEntry.Word);
+        }
 
-              if (this.graphicalLyricWords.length === 0 || index > this.graphicalLyricWords.length - 1) {
-                  const graphicalLyricWord: GraphicalLyricWord = new GraphicalLyricWord(lyricsEntry.Word);
+        if (this.graphicalLyricWords.length === 0 || index > this.graphicalLyricWords.length - 1) {
+          const graphicalLyricWord: GraphicalLyricWord = new GraphicalLyricWord(lyricsEntry.Word);
 
-                  graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
-                  graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
-                  this.graphicalLyricWords.push(graphicalLyricWord);
-              } else {
-                  const graphicalLyricWord: GraphicalLyricWord = this.graphicalLyricWords[index];
+          graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
+          graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
+          this.graphicalLyricWords.push(graphicalLyricWord);
+        } else {
+          const graphicalLyricWord: GraphicalLyricWord = this.graphicalLyricWords[index];
 
-                  graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
-                  graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
+          graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
+          graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
 
-                  if (graphicalLyricWord.isFilled()) {
-                      lyricWords.splice(index, 1);
-                      this.graphicalLyricWords.splice(this.graphicalLyricWords.indexOf(graphicalLyricWord), 1);
-                  }
-              }
+          if (graphicalLyricWord.isFilled()) {
+            lyricWords.splice(index, 1);
+            this.graphicalLyricWords.splice(this.graphicalLyricWords.indexOf(graphicalLyricWord), 1);
           }
-      });
+        }
+      }
+    });
   }
 
   protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
@@ -754,9 +674,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    */
   public findIndexGraphicalSlurFromSlur(gSlurs: GraphicalSlur[], slur: Slur): number {
     for (let slurIndex: number = 0; slurIndex < gSlurs.length; slurIndex++) {
-        if (gSlurs[slurIndex].slur === slur) {
-            return slurIndex;
-        }
+      if (gSlurs[slurIndex].slur === slur) {
+        return slurIndex;
+      }
     }
     return -1;
   }
@@ -789,175 +709,177 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     */
 
     for (const gmPage of this.graphicalMusicSheet.MusicPages) {
-        for (const musicSystem  of gmPage.MusicSystems) {
-            for (const staffLine of musicSystem.StaffLines) {
-                // if a graphical slur reaches out of the last musicsystem, we have to create another graphical slur reaching into this musicsystem
-                // (one slur needs 2 graphical slurs)
-                const openGraphicalSlurs: GraphicalSlur[] = openSlursDict[staffLine.ParentStaff.idInMusicSheet];
-                for (let slurIndex: number = 0; slurIndex < openGraphicalSlurs.length; slurIndex++) {
-                  const oldGSlur: GraphicalSlur = openGraphicalSlurs[slurIndex];
-                  const newGSlur: GraphicalSlur = new GraphicalSlur(oldGSlur.slur); //Graphicalslur.createFromSlur(oldSlur);
-                  staffLine.addSlurToStaffline(newGSlur); // every VFSlur is added to the array in the VFStaffline!
-                  openGraphicalSlurs[slurIndex] = newGSlur;
-                }
+      for (const musicSystem of gmPage.MusicSystems) {
+        for (const staffLine of musicSystem.StaffLines) {
+          // if a graphical slur reaches out of the last musicsystem, we have to create another graphical slur reaching into this musicsystem
+          // (one slur needs 2 graphical slurs)
+          const openGraphicalSlurs: GraphicalSlur[] = openSlursDict[staffLine.ParentStaff.idInMusicSheet];
+          for (let slurIndex: number = 0; slurIndex < openGraphicalSlurs.length; slurIndex++) {
+            const oldGSlur: GraphicalSlur = openGraphicalSlurs[slurIndex];
+            const newGSlur: GraphicalSlur = new GraphicalSlur(oldGSlur.slur); //Graphicalslur.createFromSlur(oldSlur);
+            staffLine.addSlurToStaffline(newGSlur); // every VFSlur is added to the array in the VFStaffline!
+            openGraphicalSlurs[slurIndex] = newGSlur;
+          }
 
-                /* VexFlow Version - for later use
-                const vfOpenSlurs: VexFlowSlur[] = vfOpenSlursDict[staffLine.ParentStaff.idInMusicSheet];
-                const vfStaffLine: VexFlowStaffLine = <VexFlowStaffLine> staffLine;
-                for (let slurIndex: number = 0; slurIndex < vfOpenSlurs.length; slurIndex++) {
-                    const oldVFSlur: VexFlowSlur = vfOpenSlurs[slurIndex];
-                    const newVFSlur: VexFlowSlur = VexFlowSlur.createFromVexflowSlur(oldVFSlur);
-                    newVFSlur.vfStartNote = undefined;
-                    vfStaffLine.addVFSlurToVFStaffline(newVFSlur); // every VFSlur is added to the array in the VFStaffline!
-                    vfOpenSlurs[slurIndex] = newVFSlur;
-                }
-                */
-
-                // add reference of slur array to the VexFlowStaffline class
-                for (const graphicalMeasure of staffLine.Measures) {
-                    for (const graphicalStaffEntry of graphicalMeasure.staffEntries) {
-                        // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesBefore.Count; idx5 < len5; ++idx5) {
-                        //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesBefore[idx5];
-                        //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
-                        //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
-                        //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
-                        //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
-                        //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
-                        //             if (graceNoteSlur.StartNote == graceNote) {
-                        //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
-                        //                 vfSlur.GraceStart = true;
-                        //                 staffLine.GraphicalSlurs.Add(vfSlur);
-                        //                 openGraphicalSlurs[i].Add(vfSlur);
-                        //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry);
-                        //                     j < graphicalStaffEntry.GraceStaffEntriesBefore.Count; j++)
-                        //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
-                        //             }
-                        //             if (graceNote == graceNoteSlur.EndNote) {
-                        //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
-                        //                 if (vfSlur != null) {
-                        //                     vfSlur.GraceEnd = true;
-                        //                     openGraphicalSlurs[i].Remove(vfSlur);
-                        //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry); j++)
-                        //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
-                        //                 }
-                        //             }
-                        //         }
-                        //     }
-                        // }
-                        // loop over "normal" notes (= no gracenotes)
-                        for (const graphicalVoiceEntry of graphicalStaffEntry.graphicalVoiceEntries) {
-                            for (const graphicalNote of graphicalVoiceEntry.notes) {
-                                for (const slur of graphicalNote.sourceNote.NoteSlurs) {
-                                    // extra check for some MusicSheets that have openSlurs (because only the first Page is available -> Recordare files)
-                                    if (slur.EndNote === undefined || slur.StartNote === undefined) {
-                                      continue;
-                                    }
-                                    // add new VexFlowSlur to List
-                                    if (slur.StartNote === graphicalNote.sourceNote) {
-                                        if (graphicalNote.sourceNote.NoteTie !== undefined) {
-                                            if (graphicalNote.parentVoiceEntry.parentStaffEntry.getAbsoluteTimestamp() !==
-                                            graphicalNote.sourceNote.NoteTie.StartNote.getAbsoluteTimestamp()) {
-                                                break;
-                                            }
-                                        }
-
-                                        // Add a Graphical Slur to the staffline, if the recent note is the Startnote of a slur
-                                        const gSlur: GraphicalSlur = new GraphicalSlur(slur);
-                                        openGraphicalSlurs.push(gSlur);
-                                        staffLine.addSlurToStaffline(gSlur);
-
-                                        /* VexFlow Version - for later use
-                                        const vfSlur: VexFlowSlur = new VexFlowSlur(slur);
-                                        vfOpenSlurs.push(vfSlur); //add open... adding / removing is JUST DONE in the open... array
-                                        vfSlur.vfStartNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
-                                        vfStaffLine.addVFSlurToVFStaffline(vfSlur); // every VFSlur is added to the array in the VFStaffline!
-                                        */
-                                    }
-                                    if (slur.EndNote === graphicalNote.sourceNote) {
-                                        // Remove the Graphical Slur from the staffline if the note is the Endnote of a slur
-                                        const index: number = this.findIndexGraphicalSlurFromSlur(openGraphicalSlurs, slur);
-                                        if (index >= 0) {
-                                            // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
-                                            const gSlur: GraphicalSlur = openGraphicalSlurs[index];
-                                            if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
-                                              gSlur.staffEntries.push(graphicalStaffEntry);
-                                            }
-
-                                            openGraphicalSlurs.splice(index, 1);
-                                        }
-
-                                        /* VexFlow Version - for later use
-                                        const vfIndex: number = this.findIndexVFSlurFromSlur(vfOpenSlurs, slur);
-                                        if (vfIndex !== undefined) {
-                                            // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
-                                            const vfSlur: VexFlowSlur = vfOpenSlurs[vfIndex];
-                                            vfSlur.vfEndNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
-                                            vfSlur.createVexFlowCurve();
-                                            vfOpenSlurs.splice(vfIndex, 1);
-                                        }
-                                        */
-                                    }
-                                }
-                            }
+          /* VexFlow Version - for later use
+          const vfOpenSlurs: VexFlowSlur[] = vfOpenSlursDict[staffLine.ParentStaff.idInMusicSheet];
+          const vfStaffLine: VexFlowStaffLine = <VexFlowStaffLine> staffLine;
+          for (let slurIndex: number = 0; slurIndex < vfOpenSlurs.length; slurIndex++) {
+              const oldVFSlur: VexFlowSlur = vfOpenSlurs[slurIndex];
+              const newVFSlur: VexFlowSlur = VexFlowSlur.createFromVexflowSlur(oldVFSlur);
+              newVFSlur.vfStartNote = undefined;
+              vfStaffLine.addVFSlurToVFStaffline(newVFSlur); // every VFSlur is added to the array in the VFStaffline!
+              vfOpenSlurs[slurIndex] = newVFSlur;
+          }
+          */
+
+          // add reference of slur array to the VexFlowStaffline class
+          for (const graphicalMeasure of staffLine.Measures) {
+            for (const graphicalStaffEntry of graphicalMeasure.staffEntries) {
+              // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesBefore.Count; idx5 < len5; ++idx5) {
+              //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesBefore[idx5];
+              //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
+              //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
+              //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
+              //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
+              //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
+              //             if (graceNoteSlur.StartNote == graceNote) {
+              //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
+              //                 vfSlur.GraceStart = true;
+              //                 staffLine.GraphicalSlurs.Add(vfSlur);
+              //                 openGraphicalSlurs[i].Add(vfSlur);
+              //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry);
+              //                     j < graphicalStaffEntry.GraceStaffEntriesBefore.Count; j++)
+              //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
+              //             }
+              //             if (graceNote == graceNoteSlur.EndNote) {
+              //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
+              //                 if (vfSlur != null) {
+              //                     vfSlur.GraceEnd = true;
+              //                     openGraphicalSlurs[i].Remove(vfSlur);
+              //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry); j++)
+              //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
+              //                 }
+              //             }
+              //         }
+              //     }
+              // }
+              // loop over "normal" notes (= no gracenotes)
+              for (const graphicalVoiceEntry of graphicalStaffEntry.graphicalVoiceEntries) {
+                for (const graphicalNote of graphicalVoiceEntry.notes) {
+                  for (const slur of graphicalNote.sourceNote.NoteSlurs) {
+                    // extra check for some MusicSheets that have openSlurs (because only the first Page is available -> Recordare files)
+                    if (slur.EndNote === undefined || slur.StartNote === undefined) {
+                      continue;
+                    }
+                    // add new VexFlowSlur to List
+                    if (slur.StartNote === graphicalNote.sourceNote) {
+                      if (graphicalNote.sourceNote.NoteTie !== undefined) {
+                        if (graphicalNote.parentVoiceEntry.parentStaffEntry.getAbsoluteTimestamp() !==
+                          graphicalNote.sourceNote.NoteTie.StartNote.getAbsoluteTimestamp()) {
+                          break;
                         }
-                        // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesAfter.Count; idx5 < len5; ++idx5) {
-                        //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesAfter[idx5];
-                        //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
-                        //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
-                        //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
-                        //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
-                        //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
-                        //             if (graceNoteSlur.StartNote == graceNote) {
-                        //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
-                        //                 vfSlur.GraceStart = true;
-                        //                 staffLine.GraphicalSlurs.Add(vfSlur);
-                        //                 openGraphicalSlurs[i].Add(vfSlur);
-                        //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry);
-                        //                      j < graphicalStaffEntry.GraceStaffEntriesAfter.Count; j++)
-                        //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
-                        //             }
-                        //             if (graceNote == graceNoteSlur.EndNote) {
-                        //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
-                        //                 if (vfSlur != null) {
-                        //                     vfSlur.GraceEnd = true;
-                        //                     openGraphicalSlurs[i].Remove(vfSlur);
-                        //                     vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry);
-                        //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry); j++)
-                        //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
-                        //                 }
-                        //             }
-                        //         }
-                        //     }
-                        // }
-
-                        //add the present Staffentry to all open slurs that don't contain this Staffentry already
-                        for (const gSlur of openGraphicalSlurs) {
-                          if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
-                            gSlur.staffEntries.push(graphicalStaffEntry);
-                          }
+                      }
+
+                      // Add a Graphical Slur to the staffline, if the recent note is the Startnote of a slur
+                      const gSlur: GraphicalSlur = new GraphicalSlur(slur);
+                      openGraphicalSlurs.push(gSlur);
+                      staffLine.addSlurToStaffline(gSlur);
+
+                      /* VexFlow Version - for later use
+                      const vfSlur: VexFlowSlur = new VexFlowSlur(slur);
+                      vfOpenSlurs.push(vfSlur); //add open... adding / removing is JUST DONE in the open... array
+                      vfSlur.vfStartNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
+                      vfStaffLine.addVFSlurToVFStaffline(vfSlur); // every VFSlur is added to the array in the VFStaffline!
+                      */
+                    }
+                    if (slur.EndNote === graphicalNote.sourceNote) {
+                      // Remove the Graphical Slur from the staffline if the note is the Endnote of a slur
+                      const index: number = this.findIndexGraphicalSlurFromSlur(openGraphicalSlurs, slur);
+                      if (index >= 0) {
+                        // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
+                        const gSlur: GraphicalSlur = openGraphicalSlurs[index];
+                        if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
+                          gSlur.staffEntries.push(graphicalStaffEntry);
                         }
-                    } // loop over StaffEntries
-                } // loop over Measures
-            } // loop over StaffLines
 
-                // Attach vfSlur array to the vfStaffline to be drawn
-                //vfStaffLine.SlursInVFStaffLine = vfSlurs;
-        } // loop over MusicSystems
+                        openGraphicalSlurs.splice(index, 1);
+                      }
+
+                      /* VexFlow Version - for later use
+                      const vfIndex: number = this.findIndexVFSlurFromSlur(vfOpenSlurs, slur);
+                      if (vfIndex !== undefined) {
+                          // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
+                          const vfSlur: VexFlowSlur = vfOpenSlurs[vfIndex];
+                          vfSlur.vfEndNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
+                          vfSlur.createVexFlowCurve();
+                          vfOpenSlurs.splice(vfIndex, 1);
+                      }
+                      */
+                    }
+                  }
+                }
+              }
+              // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesAfter.Count; idx5 < len5; ++idx5) {
+              //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesAfter[idx5];
+              //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
+              //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
+              //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
+              //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
+              //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
+              //             if (graceNoteSlur.StartNote == graceNote) {
+              //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
+              //                 vfSlur.GraceStart = true;
+              //                 staffLine.GraphicalSlurs.Add(vfSlur);
+              //                 openGraphicalSlurs[i].Add(vfSlur);
+              //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry);
+              //                      j < graphicalStaffEntry.GraceStaffEntriesAfter.Count; j++)
+              //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
+              //             }
+              //             if (graceNote == graceNoteSlur.EndNote) {
+              //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
+              //                 if (vfSlur != null) {
+              //                     vfSlur.GraceEnd = true;
+              //                     openGraphicalSlurs[i].Remove(vfSlur);
+              //                     vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry);
+              //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry); j++)
+              //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
+              //                 }
+              //             }
+              //         }
+              //     }
+              // }
+
+              //add the present Staffentry to all open slurs that don't contain this Staffentry already
+              for (const gSlur of openGraphicalSlurs) {
+                if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
+                  gSlur.staffEntries.push(graphicalStaffEntry);
+                }
+              }
+            } // loop over StaffEntries
+          } // loop over Measures
+        } // loop over StaffLines
+
+        // Attach vfSlur array to the vfStaffline to be drawn
+        //vfStaffLine.SlursInVFStaffLine = vfSlurs;
+      } // loop over MusicSystems
     } // loop over MusicPages
 
     // order slurs that were saved to the Staffline
     for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
         for (const musicSystem of graphicalMusicPage.MusicSystems) {
-            for (const staffLine of musicSystem.StaffLines) {
-                for (const gSlur of staffLine.GraphicalSlurs) {
-                    // crossed slurs will be handled later:
-                    if (gSlur.slur.isCrossed()) {
-                        continue;
-                    }
-                    gSlur.calculateCurve(this.rules);
+          for (const staffLine of musicSystem.StaffLines) {
+            // Sort all gSlurs in the staffline using the Compare function in class GraphicalSlurSorter
+            const sortedGSlurs: GraphicalSlur[] = staffLine.GraphicalSlurs.sort(GraphicalSlur.Compare);
+            for (const gSlur of sortedGSlurs) {
+                // crossed slurs will be handled later:
+                if (gSlur.slur.isCrossed()) {
+                    continue;
                 }
+                gSlur.calculateCurve(this.rules);
             }
+          }
         }
-    }
+      }
   }
 }

+ 59 - 43
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -1,29 +1,31 @@
 import Vex = require("vexflow");
-import {MusicSheetDrawer} from "../MusicSheetDrawer";
-import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
-import {VexFlowMeasure} from "./VexFlowMeasure";
-import {PointF2D} from "../../../Common/DataObjects/PointF2D";
-import {GraphicalLabel} from "../GraphicalLabel";
-import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
-import {MusicSystem} from "../MusicSystem";
-import {GraphicalObject} from "../GraphicalObject";
-import {GraphicalLayers} from "../DrawingEnums";
-import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
-import {VexFlowBackend} from "./VexFlowBackend";
-import {VexFlowOctaveShift} from "./VexFlowOctaveShift";
-import {VexFlowInstantaneousDynamicExpression} from "./VexFlowInstantaneousDynamicExpression";
+import { MusicSheetDrawer } from "../MusicSheetDrawer";
+import { RectangleF2D } from "../../../Common/DataObjects/RectangleF2D";
+import { VexFlowMeasure } from "./VexFlowMeasure";
+import { PointF2D } from "../../../Common/DataObjects/PointF2D";
+import { GraphicalLabel } from "../GraphicalLabel";
+import { VexFlowTextMeasurer } from "./VexFlowTextMeasurer";
+import { MusicSystem } from "../MusicSystem";
+import { GraphicalObject } from "../GraphicalObject";
+import { GraphicalLayers } from "../DrawingEnums";
+import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
+import { VexFlowBackend } from "./VexFlowBackend";
+import { VexFlowOctaveShift } from "./VexFlowOctaveShift";
+import { VexFlowInstantaneousDynamicExpression } from "./VexFlowInstantaneousDynamicExpression";
 import { VexFlowInstrumentBracket } from "./VexFlowInstrumentBracket";
 import { VexFlowInstrumentBrace } from "./VexFlowInstrumentBrace";
 import { GraphicalLyricEntry } from "../GraphicalLyricEntry";
 import { VexFlowStaffLine } from "./VexFlowStaffLine";
-import {StaffLine} from "../StaffLine";
-import {EngravingRules} from "../EngravingRules";
+import { StaffLine } from "../StaffLine";
+import { EngravingRules } from "../EngravingRules";
 import { GraphicalSlur } from "../GraphicalSlur";
 import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
-import {GraphicalInstantaneousTempoExpression} from "../GraphicalInstantaneousTempoExpression";
-import {GraphicalInstantaneousDynamicExpression} from "../GraphicalInstantaneousDynamicExpression";
+import { GraphicalInstantaneousTempoExpression } from "../GraphicalInstantaneousTempoExpression";
+import { GraphicalInstantaneousDynamicExpression } from "../GraphicalInstantaneousDynamicExpression";
 import log = require("loglevel");
-import {DrawingParameters} from "../DrawingParameters";
+import { GraphicalContinuousDynamicExpression } from "../GraphicalContinuousDynamicExpression";
+import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
+import { DrawingParameters } from "../DrawingParameters";
 
 /**
  * This is a global constant which denotes the height in pixels of the space between two lines of the stave
@@ -80,7 +82,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
 
     protected drawStaffLine(staffLine: StaffLine): void {
         super.drawStaffLine(staffLine);
-        const  absolutePos: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
+        const absolutePos: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
         this.drawSlurs(staffLine as VexFlowStaffLine, absolutePos);
     }
 
@@ -139,8 +141,8 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         // Draw the StaffEntries
         for (const staffEntry of measure.staffEntries) {
             this.drawStaffEntry(staffEntry);
-                    }
-                }
+        }
+    }
 
     // private drawPixel(coord: PointF2D): void {
     //     coord = this.applyScreenTransformation(coord);
@@ -155,20 +157,20 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         start = this.applyScreenTransformation(start);
         stop = this.applyScreenTransformation(stop);
         this.backend.renderLine(start, stop, color, lineWidth * unitInPixels);
-            }
+    }
 
     protected drawSkyLine(staffline: StaffLine): void {
         const startPosition: PointF2D = staffline.PositionAndShape.AbsolutePosition;
         const width: number = staffline.PositionAndShape.Size.width;
         this.drawSampledLine(staffline.SkyLine, startPosition, width);
-        }
+    }
 
     protected drawBottomLine(staffline: StaffLine): void {
         const startPosition: PointF2D = new PointF2D(staffline.PositionAndShape.AbsolutePosition.x,
                                                      staffline.PositionAndShape.AbsolutePosition.y);
         const width: number = staffline.PositionAndShape.Size.width;
         this.drawSampledLine(staffline.BottomLine, startPosition, width, "#0000FFFF");
-        }
+    }
 
     /**
      * Draw a line with a width and start point in a chosen color (used for skyline/bottom line debugging) from
@@ -186,7 +188,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
             if (line[i] !== currentValue) {
                 indices.push(i);
                 currentValue = line[i];
-    }
+            }
         }
 
         const absolute: PointF2D = startPosition;
@@ -283,25 +285,25 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
             // Draw InstantaniousDynamics
             if (abstractGraphicalExpression instanceof GraphicalInstantaneousDynamicExpression) {
                 this.drawInstantaneousDynamic((abstractGraphicalExpression as VexFlowInstantaneousDynamicExpression));
-            // Draw InstantaniousTempo
+                // Draw InstantaniousTempo
             } else if (abstractGraphicalExpression instanceof GraphicalInstantaneousTempoExpression) {
                 this.drawLabel((abstractGraphicalExpression as GraphicalInstantaneousTempoExpression).GraphicalLabel, GraphicalLayers.Notes);
-            // // Draw ContinuousDynamics
-            // } else if (abstractGraphicalExpression instanceof GraphicalContinuousDynamicExpression) {
-            // //     drawContinuousDynamic((GraphicalContinuousDynamicExpression)abstractGraphicalExpression, absolutePos);
-            // // Draw ContinuousTempo
-            // } else if (abstractGraphicalExpression instanceof GraphicalContinuousTempoExpression) {
-            //     this.drawLabel((abstractGraphicalExpression as GraphicalContinuousTempoExpression).GraphicalLabel, GraphicalLayers.Notes);
-            // // 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);
-            // }
+                // Draw ContinuousDynamics
+            } else if (abstractGraphicalExpression instanceof GraphicalContinuousDynamicExpression) {
+                this.drawContinuousDynamic((abstractGraphicalExpression as VexFlowContinuousDynamicExpression));
+                // Draw ContinuousTempo
+                // } else if (abstractGraphicalExpression instanceof GraphicalContinuousTempoExpression) {
+                //     this.drawLabel((abstractGraphicalExpression as GraphicalContinuousTempoExpression).GraphicalLabel, GraphicalLayers.Notes);
+                // // 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);
+                // }
             } else {
                 log.warn("Unkown type of expression!");
             }
@@ -312,6 +314,20 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         this.drawLabel((instantaneousDynamic as VexFlowInstantaneousDynamicExpression).Label, <number>GraphicalLayers.Notes);
     }
 
+    protected drawContinuousDynamic(graphicalExpression: VexFlowContinuousDynamicExpression): void {
+        if (graphicalExpression.IsVerbal) {
+            this.drawLabel(graphicalExpression.Label, <number>GraphicalLayers.Notes);
+        } else {
+            for (const line of graphicalExpression.Lines) {
+                const start: PointF2D = new PointF2D(graphicalExpression.ParentStaffLine.PositionAndShape.AbsolutePosition.x + line.Start.x,
+                                                     graphicalExpression.ParentStaffLine.PositionAndShape.AbsolutePosition.y + line.Start.y);
+                const end: PointF2D = new PointF2D(graphicalExpression.ParentStaffLine.PositionAndShape.AbsolutePosition.x + line.End.x,
+                                                   graphicalExpression.ParentStaffLine.PositionAndShape.AbsolutePosition.y + line.End.y);
+                this.drawLine(start, end, "black", line.Width);
+            }
+        }
+    }
+
     /**
      * Renders a Label to the screen (e.g. Title, composer..)
      * @param graphicalLabel holds the label string, the text height in units and the font parameters
@@ -337,7 +353,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      * @param alpha alpha value between 0 and 1
      */
     protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number, alpha: number): void {
-       this.backend.renderRectangle(rectangle, styleId, alpha);
+        this.backend.renderRectangle(rectangle, styleId, alpha);
     }
 
     /**

+ 1 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts

@@ -29,7 +29,7 @@ export class VexFlowStaffEntry extends GraphicalStaffEntry {
         for (const gve of this.graphicalVoiceEntries as VexFlowVoiceEntry[]) {
             if (gve.vfStaveNote) {
                 gve.vfStaveNote.setStave(stave);
-                if (!gve.vfStaveNote.preFormatted) {
+                if (!gve.vfStaveNote.preFormatted || gve.vfStaveNote.getBoundingBox() === null) {
                     continue;
                 }
                 gve.applyBordersFromVexflow();

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

@@ -12,6 +12,9 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
 
     public applyBordersFromVexflow(): void {
         const staveNote: any = (this.vfStaveNote as any);
+        if (!staveNote.getNoteHeadBeginX) {
+            return;
+        }
         const boundingBox: any = staveNote.getBoundingBox();
         const modifierWidth: number = staveNote.getNoteHeadBeginX() - boundingBox.x;
 

+ 98 - 67
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -21,8 +21,9 @@ import * as log from "loglevel";
 import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
 import {ChordSymbolReader} from "./MusicSymbolModules/ChordSymbolReader";
 import {ExpressionReader} from "./MusicSymbolModules/ExpressionReader";
-import { RepetitionInstructionReader } from "./MusicSymbolModules/RepetitionInstructionReader";
-import { SlurReader } from "./MusicSymbolModules/SlurReader";
+import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
+import {SlurReader} from "./MusicSymbolModules/SlurReader";
+import {StemDirectionType} from "../VoiceData/VoiceEntry";
 //import Dictionary from "typescript-collections/dist/lib/Dictionary";
 
 // FIXME: The following classes are missing
@@ -211,6 +212,40 @@ export class InstrumentReader {
             }
           }
 
+          // check for cue note
+          let isCueNote: boolean = false;
+          const typeNode: IXmlElement = xmlNode.element("type");
+          if (typeNode !== undefined) {
+            const sizeAttr: Attr = typeNode.attribute("size");
+            if (sizeAttr !== undefined && sizeAttr !== null) {
+              if (sizeAttr.value === "cue") {
+                isCueNote = true;
+              }
+            }
+          }
+
+          // check stem element
+          let stemDirectionXml: StemDirectionType = StemDirectionType.Undefined;
+          const stemNode: IXmlElement = xmlNode.element("stem");
+          if (stemNode !== undefined) {
+            switch (stemNode.value) {
+              case "down":
+                stemDirectionXml = StemDirectionType.Down;
+                break;
+              case "up":
+                stemDirectionXml = StemDirectionType.Up;
+                break;
+              case "double":
+                stemDirectionXml = StemDirectionType.Double;
+                break;
+              case "none":
+                stemDirectionXml = StemDirectionType.None;
+                break;
+              default:
+                stemDirectionXml = StemDirectionType.Undefined;
+            }
+          }
+
           let musicTimestamp: Fraction = currentFraction.clone();
           if (isChord) {
             musicTimestamp = previousFraction.clone();
@@ -264,7 +299,7 @@ export class InstrumentReader {
             xmlNode, noteDuration, restNote,
             this.currentStaffEntry, this.currentMeasure,
             measureStartAbsoluteTimestamp,
-            this.maxTieNoteFraction, isChord, guitarPro, printObject
+            this.maxTieNoteFraction, isChord, guitarPro, printObject, isCueNote, stemDirectionXml
           );
 
           const notationsNode: IXmlElement = xmlNode.element("notations");
@@ -568,12 +603,10 @@ export class InstrumentReader {
    * @returns {Fraction}
    */
   private getNoteDurationFromTypeNode(xmlNode: IXmlElement): Fraction {
-    if (xmlNode.element("type") !== undefined) {
-      const typeNode: IXmlElement = xmlNode.element("type");
-      if (typeNode !== undefined) {
-        const type: string = typeNode.value;
-        return this.currentVoiceGenerator.getNoteDurationFromType(type);
-      }
+    const typeNode: IXmlElement = xmlNode.element("type");
+    if (typeNode !== undefined) {
+      const type: string = typeNode.value;
+      return this.currentVoiceGenerator.getNoteDurationFromType(type);
     }
     return new Fraction(0, 4 * this.divisions);
   }
@@ -1037,65 +1070,63 @@ export class InstrumentReader {
       for (let idx: number = 0, len: number = xmlMeasureListArr.length; idx < len; ++idx) {
         const xmlNode: IXmlElement = xmlMeasureListArr[idx];
         if (xmlNode.name === "note" && xmlNode.element("time-modification") === undefined) {
-          if (xmlNode.element("duration") !== undefined && xmlNode.element("type") !== undefined) {
-            const durationNode: IXmlElement = xmlNode.element("duration");
-            const typeNode: IXmlElement = xmlNode.element("type");
-            if (durationNode !== undefined && typeNode !== undefined) {
-              const type: string = typeNode.value;
-              let noteDuration: number = 0;
-              try {
-                noteDuration = parseInt(durationNode.value, 10);
-              } catch (ex) {
-                log.debug("InstrumentReader.readDivisionsFromNotes", ex);
-                continue;
-              }
+          const durationNode: IXmlElement = xmlNode.element("duration");
+          const typeNode: IXmlElement = xmlNode.element("type");
+          if (durationNode !== undefined && typeNode !== undefined) {
+            const type: string = typeNode.value;
+            let noteDuration: number = 0;
+            try {
+              noteDuration = parseInt(durationNode.value, 10);
+            } catch (ex) {
+              log.debug("InstrumentReader.readDivisionsFromNotes", ex);
+              continue;
+            }
 
-              switch (type) {
-                case "1024th":
-                  divisionsFromNote = (noteDuration / 4) * 1024;
-                  break;
-                case "512th":
-                  divisionsFromNote = (noteDuration / 4) * 512;
-                  break;
-                case "256th":
-                  divisionsFromNote = (noteDuration / 4) * 256;
-                  break;
-                case "128th":
-                  divisionsFromNote = (noteDuration / 4) * 128;
-                  break;
-                case "64th":
-                  divisionsFromNote = (noteDuration / 4) * 64;
-                  break;
-                case "32nd":
-                  divisionsFromNote = (noteDuration / 4) * 32;
-                  break;
-                case "16th":
-                  divisionsFromNote = (noteDuration / 4) * 16;
-                  break;
-                case "eighth":
-                  divisionsFromNote = (noteDuration / 4) * 8;
-                  break;
-                case "quarter":
-                  divisionsFromNote = (noteDuration / 4) * 4;
-                  break;
-                case "half":
-                  divisionsFromNote = (noteDuration / 4) * 2;
-                  break;
-                case "whole":
-                  divisionsFromNote = (noteDuration / 4);
-                  break;
-                case "breve":
-                  divisionsFromNote = (noteDuration / 4) / 2;
-                  break;
-                case "long":
-                  divisionsFromNote = (noteDuration / 4) / 4;
-                  break;
-                case "maxima":
-                  divisionsFromNote = (noteDuration / 4) / 8;
-                  break;
-                default:
-                  break;
-              }
+            switch (type) {
+              case "1024th":
+                divisionsFromNote = (noteDuration / 4) * 1024;
+                break;
+              case "512th":
+                divisionsFromNote = (noteDuration / 4) * 512;
+                break;
+              case "256th":
+                divisionsFromNote = (noteDuration / 4) * 256;
+                break;
+              case "128th":
+                divisionsFromNote = (noteDuration / 4) * 128;
+                break;
+              case "64th":
+                divisionsFromNote = (noteDuration / 4) * 64;
+                break;
+              case "32nd":
+                divisionsFromNote = (noteDuration / 4) * 32;
+                break;
+              case "16th":
+                divisionsFromNote = (noteDuration / 4) * 16;
+                break;
+              case "eighth":
+                divisionsFromNote = (noteDuration / 4) * 8;
+                break;
+              case "quarter":
+                divisionsFromNote = (noteDuration / 4) * 4;
+                break;
+              case "half":
+                divisionsFromNote = (noteDuration / 4) * 2;
+                break;
+              case "whole":
+                divisionsFromNote = (noteDuration / 4);
+                break;
+              case "breve":
+                divisionsFromNote = (noteDuration / 4) / 2;
+                break;
+              case "long":
+                divisionsFromNote = (noteDuration / 4) / 4;
+                break;
+              case "maxima":
+                divisionsFromNote = (noteDuration / 4) / 8;
+                break;
+              default:
+                break;
             }
           }
         }

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

@@ -125,6 +125,26 @@ export class ArticulationReader {
       const currentTechnicalInstruction: TechnicalInstruction = new TechnicalInstruction();
       currentTechnicalInstruction.type = TechnicalInstructionType.Fingering;
       currentTechnicalInstruction.value = nodeFingering.value;
+      currentTechnicalInstruction.placement = PlacementEnum.NotYetDefined;
+      const placement: Attr = nodeFingering.attribute("placement");
+      if (placement !== undefined && placement !== null) {
+        switch (placement.value) {
+          case "above":
+            currentTechnicalInstruction.placement = PlacementEnum.Above;
+            break;
+          case "below":
+            currentTechnicalInstruction.placement = PlacementEnum.Below;
+            break;
+          case "left": // not valid in MusicXML 3.1
+            currentTechnicalInstruction.placement = PlacementEnum.Left;
+            break;
+          case "right": // not valid in MusicXML 3.1
+            currentTechnicalInstruction.placement = PlacementEnum.Right;
+            break;
+          default:
+            currentTechnicalInstruction.placement = PlacementEnum.NotYetDefined;
+        }
+      }
       currentVoiceEntry.TechnicalInstructions.push(currentTechnicalInstruction);
     }
   }

+ 3 - 3
src/MusicalScore/ScoreIO/MusicSymbolModules/ChordSymbolReader.ts

@@ -38,7 +38,7 @@ export class ChordSymbolReader {
         let rootAlteration: AccidentalEnum = AccidentalEnum.NONE;
         if (rootAlter !== undefined) {
             try {
-                rootAlteration = <AccidentalEnum>parseInt(rootAlter.value, undefined);
+                rootAlteration = Pitch.AccidentalFromHalfTones(parseInt(rootAlter.value, undefined));
             } catch (ex) {
                 const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
                                                                         "Invalid chord symbol");
@@ -82,7 +82,7 @@ export class ChordSymbolReader {
             let bassAlteration: AccidentalEnum = AccidentalEnum.NONE;
             if (bassAlter !== undefined) {
                 try {
-                    bassAlteration = <AccidentalEnum>parseInt(bassAlter.value, undefined);
+                    bassAlteration = Pitch.AccidentalFromHalfTones(parseInt(bassAlter.value, undefined));
                 } catch (ex) {
                     const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
                                                                             "Invalid chord symbol");
@@ -117,7 +117,7 @@ export class ChordSymbolReader {
 
             let alter: AccidentalEnum;
             try {
-                alter = <AccidentalEnum>parseInt(degreeAlter.value, undefined);
+                alter = Pitch.AccidentalFromHalfTones(parseInt(degreeAlter.value, undefined));
             } catch (ex) {
                 const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
                                                                         "Invalid chord symbol");

+ 12 - 9
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -72,7 +72,7 @@ export class ExpressionReader {
         }
 
         const placeAttr: IXmlAttribute = xmlNode.attribute("placement");
-        if (placeAttr !== undefined) {
+        if (placeAttr !== undefined && placeAttr !== null) {
             try {
                 const placementString: string = placeAttr.value;
                 if (placementString === "below") {
@@ -96,21 +96,21 @@ export class ExpressionReader {
                     const dynamicsNode: IXmlElement = directionTypeNode.element("dynamics");
                     if (dynamicsNode !== undefined) {
                         const defAttr: IXmlAttribute = dynamicsNode.attribute("default-y");
-                        if (defAttr !== undefined) {
+                        if (defAttr !== undefined && defAttr !== null) {
                             this.readExpressionPlacement(defAttr, "read dynamics y pos");
                         }
                     }
                     const wedgeNode: IXmlElement = directionTypeNode.element("wedge");
                     if (wedgeNode !== undefined) {
                         const defAttr: IXmlAttribute = wedgeNode.attribute("default-y");
-                        if (defAttr !== undefined) {
+                        if (defAttr !== undefined && defAttr !== null) {
                             this.readExpressionPlacement(defAttr, "read wedge y pos");
                         }
                     }
                     const wordsNode: IXmlElement = directionTypeNode.element("words");
                     if (wordsNode !== undefined) {
                         const defAttr: IXmlAttribute = wordsNode.attribute("default-y");
-                        if (defAttr !== undefined) {
+                        if (defAttr !== undefined && defAttr !== null) {
                             this.readExpressionPlacement(defAttr, "read words y pos");
                         }
                     }
@@ -300,11 +300,14 @@ export class ExpressionReader {
             if (dynamicsNode.hasAttributes && dynamicsNode.attribute("default-x") !== undefined) {
                 this.directionTimestamp = Fraction.createFromFraction(inSourceMeasureCurrentFraction);
             }
-            const name: string = dynamicsNode.elements()[0].name;
-            if (name !== undefined) {
+            let expressionText: string = dynamicsNode.elements()[0].name;
+            if (expressionText === "other-dynamics") {
+                expressionText = dynamicsNode.elements()[0].value;
+            }
+            if (expressionText !== undefined) {
                 let dynamicEnum: DynamicEnum;
                 try {
-                    dynamicEnum = DynamicEnum[name];
+                    dynamicEnum = DynamicEnum[expressionText];
                 } catch (err) {
                     const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DynamicError", "Error while reading dynamic.");
                     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
@@ -320,7 +323,7 @@ export class ExpressionReader {
                         this.openContinuousDynamicExpression.StartMultiExpression !== this.getMultiExpression) {
                         this.closeOpenContinuousDynamic();
                     }
-                    const instantaneousDynamicExpression: InstantaneousDynamicExpression = new InstantaneousDynamicExpression(name,
+                    const instantaneousDynamicExpression: InstantaneousDynamicExpression = new InstantaneousDynamicExpression(expressionText,
                                                                                                                               this.soundDynamic,
                                                                                                                               this.placement,
                                                                                                                               this.staffNumber);
@@ -328,7 +331,7 @@ export class ExpressionReader {
                     this.initialize();
                     if (this.activeInstantaneousDynamic !== undefined) {
                         this.activeInstantaneousDynamic.DynEnum = instantaneousDynamicExpression.DynEnum;
-                    } else { this.activeInstantaneousDynamic = new InstantaneousDynamicExpression(name, 0, PlacementEnum.NotYetDefined, 1); }
+                    } else { this.activeInstantaneousDynamic = new InstantaneousDynamicExpression(expressionText, 0, PlacementEnum.NotYetDefined, 1); }
                 }
             }
         }

+ 37 - 10
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -2,7 +2,7 @@ import { Instrument } from "../Instrument";
 import { LinkedVoice } from "../VoiceData/LinkedVoice";
 import { Voice } from "../VoiceData/Voice";
 import { MusicSheet } from "../MusicSheet";
-import { VoiceEntry } from "../VoiceData/VoiceEntry";
+import { VoiceEntry, StemDirectionType } from "../VoiceData/VoiceEntry";
 import { Note } from "../VoiceData/Note";
 import { SourceMeasure } from "../VoiceData/SourceMeasure";
 import { SourceStaffEntry } from "../VoiceData/SourceStaffEntry";
@@ -26,6 +26,7 @@ import { CollectionUtil } from "../../Util/CollectionUtil";
 import { ArticulationReader } from "./MusicSymbolModules/ArticulationReader";
 import { SlurReader } from "./MusicSymbolModules/SlurReader";
 import { NoteHead } from "../VoiceData/NoteHead";
+import { Arpeggio, ArpeggioType } from "../VoiceData/Arpeggio";
 
 export class VoiceGenerator {
   constructor(instrument: Instrument, voiceId: number, slurReader: SlurReader, mainVoice: Voice = undefined) {
@@ -105,14 +106,14 @@ export class VoiceGenerator {
   public read(noteNode: IXmlElement, noteDuration: Fraction, restNote: boolean,
               parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
               measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean,
-              printObject: boolean = true): Note {
+              printObject: boolean, isCueNote: boolean, stemDirectionXml: StemDirectionType): Note {
     this.currentStaffEntry = parentStaffEntry;
     this.currentMeasure = parentMeasure;
     //log.debug("read called:", restNote);
     try {
       this.currentNote = restNote
-        ? this.addRestNote(noteDuration, printObject)
-        : this.addSingleNote(noteNode, noteDuration, chord, guitarPro, printObject);
+        ? this.addRestNote(noteDuration, printObject, isCueNote)
+        : this.addSingleNote(noteNode, noteDuration, chord, guitarPro, printObject, isCueNote, stemDirectionXml);
       // read lyrics
       const lyricElements: IXmlElement[] = noteNode.elements("lyric");
       if (this.lyricsReader !== undefined && lyricElements !== undefined) {
@@ -140,8 +141,31 @@ export class VoiceGenerator {
           hasTupletCommand = true;
         }
         // check for Arpeggios
-        if (notationNode.element("arpeggiate") !== undefined && !this.currentVoiceEntry.IsGrace) {
-          this.currentVoiceEntry.ArpeggiosNotesIndices.push(this.currentVoiceEntry.Notes.indexOf(this.currentNote));
+        const arpeggioNode: IXmlElement = notationNode.element("arpeggiate");
+        if (arpeggioNode !== undefined && !this.currentVoiceEntry.IsGrace) {
+          let currentArpeggio: Arpeggio;
+          if (this.currentVoiceEntry.Arpeggio !== undefined) { // add note to existing Arpeggio
+            currentArpeggio = this.currentVoiceEntry.Arpeggio;
+          } else { // create new Arpeggio
+            let arpeggioType: ArpeggioType;
+            const directionAttr: Attr = arpeggioNode.attribute("direction");
+            if (directionAttr !== null) {
+              switch (directionAttr.value) {
+                case "up":
+                  arpeggioType = ArpeggioType.ROLL_UP;
+                  break;
+                case "down":
+                  arpeggioType = ArpeggioType.ROLL_DOWN;
+                  break;
+                default:
+                  arpeggioType = ArpeggioType.ARPEGGIO_DIRECTIONLESS;
+              }
+            }
+
+            currentArpeggio = new Arpeggio(this.currentVoiceEntry, arpeggioType);
+            this.currentVoiceEntry.Arpeggio = currentArpeggio;
+          }
+          currentArpeggio.addNote(this.currentNote);
         }
         // check for Ties - must be the last check
         const tiedNodeList: IXmlElement[] = notationNode.elements("tied");
@@ -297,7 +321,7 @@ export class VoiceGenerator {
    * @returns {Note}
    */
   private addSingleNote(node: IXmlElement, noteDuration: Fraction, chord: boolean, guitarPro: boolean,
-                        printObject: boolean = true): Note {
+                        printObject: boolean, isCueNote: boolean, stemDirectionXml: StemDirectionType): Note {
     //log.debug("addSingleNote called");
     let noteAlter: number = 0;
     let noteAccidental: AccidentalEnum = AccidentalEnum.NONE;
@@ -391,11 +415,14 @@ export class VoiceGenerator {
     const noteLength: Fraction = Fraction.createFromFraction(noteDuration);
     const note: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
     note.PrintObject = printObject;
+    note.IsCueNote = isCueNote;
+    note.StemDirectionXml = stemDirectionXml;
     note.PlaybackInstrumentId = playbackInstrumentId;
     if (noteHeadShapeXml !== undefined && noteHeadShapeXml !== "normal") {
       note.NoteHead = new NoteHead(note, noteHeadShapeXml, noteHeadFilledXml);
-    } // if normal, leave note head undefined to save performance
+    } // if normal, leave note head undefined to save processing/runtime
     this.currentVoiceEntry.Notes.push(note);
+    this.currentVoiceEntry.WantedStemDirectionXml = stemDirectionXml;
     if (node.elements("beam") && !chord) {
       this.createBeam(node, note);
     }
@@ -408,10 +435,11 @@ export class VoiceGenerator {
    * @param divisions
    * @returns {Note}
    */
-  private addRestNote(noteDuration: Fraction, printObject: boolean = true): Note {
+  private addRestNote(noteDuration: Fraction, printObject: boolean = true, isCueNote: boolean = false): Note {
     const restFraction: Fraction = Fraction.createFromFraction(noteDuration);
     const restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, undefined);
     restNote.PrintObject = printObject;
+    restNote.IsCueNote = isCueNote;
     this.currentVoiceEntry.Notes.push(restNote);
     if (this.openBeam !== undefined) {
       this.openBeam.ExtendedNoteList.push(restNote);
@@ -423,7 +451,6 @@ export class VoiceGenerator {
    * Handle the currentVoiceBeam.
    * @param node
    * @param note
-   * @param grace
    */
   private createBeam(node: IXmlElement, note: Note): void {
     try {

+ 34 - 0
src/MusicalScore/VoiceData/Arpeggio.ts

@@ -0,0 +1,34 @@
+import { VoiceEntry } from "./VoiceEntry";
+import { Note } from "./Note";
+
+/** Type and direction of an Arpeggio.
+ * The values should correspond to Vex.Flow.Strokes.Type values.
+ * Somehow they don't correspond to Vexflow code, but they were confirmed to work, for whatever reason.
+ * For now, we only support one Arpeggio per VoiceEntry.
+ */
+export enum ArpeggioType {
+    BRUSH_DOWN = 2,
+    BRUSH_UP = 1,
+    ROLL_DOWN = 4, // Arpeggio with downwards arrow
+    ROLL_UP = 3, // Arpeggio with upwards arrow
+    RASQUEDO_DOWN = 5, // this is UP, can't find a value for DOWN that works in Vexflow right now
+    RASQUEDO_UP = 5,
+    ARPEGGIO_DIRECTIONLESS = 7 // currently not supported in Vexflow
+}
+
+export class Arpeggio {
+    constructor(parentVoiceEntry: VoiceEntry, type: ArpeggioType = ArpeggioType.ARPEGGIO_DIRECTIONLESS) {
+        this.parentVoiceEntry = parentVoiceEntry;
+        this.type = type;
+        this.notes = [];
+    }
+
+    public parentVoiceEntry: VoiceEntry;
+    public notes: Note[];
+    public type: ArpeggioType;
+
+    public addNote(note: Note): void {
+        this.notes.push(note);
+        note.Arpeggio = this;
+    }
+}

+ 28 - 4
src/MusicalScore/VoiceData/Expressions/AbstractExpression.ts

@@ -1,7 +1,10 @@
 export class AbstractExpression {
-    //constructor() {
-    //
-    //}
+    protected placement: PlacementEnum;
+
+    constructor(placement: PlacementEnum) {
+        this.placement = placement;
+    }
+
     protected static isStringInStringList(stringList: Array<string>, inputString: string): boolean {
         for (let idx: number = 0, len: number = stringList.length; idx < len; ++idx) {
             const s: string = stringList[idx];
@@ -11,10 +14,31 @@ export class AbstractExpression {
         }
         return false;
     }
+
+    /** Placement of the expression */
+    public get Placement(): PlacementEnum { return this.placement; }
+
+    public static PlacementEnumFromString(placementString: string): PlacementEnum {
+        switch (placementString.toLowerCase()) {
+            case "above":
+                return PlacementEnum.Above;
+            case "below":
+                return PlacementEnum.Below;
+            case "left":
+                return PlacementEnum.Left;
+            case "right":
+                return PlacementEnum.Right;
+            case "auto":
+            default:
+                return PlacementEnum.NotYetDefined;
+        }
+    }
 }
 
 export enum PlacementEnum {
     Above = 0,
     Below = 1,
-    NotYetDefined = 2
+    Left = 2,
+    Right = 3,
+    NotYetDefined = 4
 }

+ 3 - 4
src/MusicalScore/VoiceData/Expressions/AbstractTempoExpression.ts

@@ -1,17 +1,16 @@
-import {PlacementEnum} from "./AbstractExpression";
+import {PlacementEnum, AbstractExpression} from "./AbstractExpression";
 import {MultiTempoExpression} from "./MultiTempoExpression";
 
-export abstract class AbstractTempoExpression {
+export abstract class AbstractTempoExpression extends AbstractExpression {
 
     constructor(label: string, placement: PlacementEnum, staffNumber: number, parentMultiTempoExpression: MultiTempoExpression) {
+        super(placement);
         this.label = label;
-        this.placement = placement;
         this.staffNumber = staffNumber;
         this.parentMultiTempoExpression = parentMultiTempoExpression;
     }
 
     protected label: string;
-    protected placement: PlacementEnum;
     protected staffNumber: number;
     protected parentMultiTempoExpression: MultiTempoExpression;
 

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

@@ -4,10 +4,9 @@ import {Fraction} from "../../../../Common/DataObjects/Fraction";
 
 export class ContinuousDynamicExpression extends AbstractExpression {
     constructor(dynamicType: ContDynamicEnum, placement: PlacementEnum, staffNumber: number, label: string = "") {
-        super();
+        super(placement);
         this.dynamicType = dynamicType;
         this.label = label;
-        this.placement = placement;
         this.staffNumber = staffNumber;
         this.startVolume = -1;
         this.endVolume = -1;
@@ -24,7 +23,6 @@ export class ContinuousDynamicExpression extends AbstractExpression {
     private endMultiExpression: MultiExpression;
     private startVolume: number;
     private endVolume: number;
-    private placement: PlacementEnum;
     private staffNumber: number;
     private label: string;
 

+ 13 - 13
src/MusicalScore/VoiceData/Expressions/InstantaneousDynamicExpression.ts

@@ -7,10 +7,9 @@ import * as log from "loglevel";
 
 export class InstantaneousDynamicExpression extends AbstractExpression {
     constructor(dynamicExpression: string, soundDynamics: number, placement: PlacementEnum, staffNumber: number) {
-        super();
+        super(placement);
         this.dynamicEnum = DynamicEnum[dynamicExpression.toLowerCase()];
         this.soundDynamic = soundDynamics;
-        this.placement = placement;
         this.staffNumber = staffNumber;
     }
     public static dynamicToRelativeVolumeDict: { [_: string]: number; } = {
@@ -33,6 +32,7 @@ export class InstantaneousDynamicExpression extends AbstractExpression {
         "rf": 0.5,
         "rfz": 0.5,
         "sf": 0.5,
+        "sff": 0.5,
         "sffz": 0.5,
         "sfp": 0.5,
         "sfpp": 0.5,
@@ -43,13 +43,12 @@ export class InstantaneousDynamicExpression extends AbstractExpression {
     private static listInstantaneousDynamics: string[] =  [
         "pppppp", "ppppp", "pppp", "ppp", "pp", "p",
         "ffffff", "fffff", "ffff", "fff", "ff", "f",
-        "mf", "mp", "sf", "sp", "spp", "fp", "rf", "rfz", "sfz", "sffz", "fz",
+        "mf", "mp", "sf", "sff", "sp", "spp", "fp", "rf", "rfz", "sfz", "sffz", "fz",
     ];
 
     private multiExpression: MultiExpression;
     private dynamicEnum: DynamicEnum;
     private soundDynamic: number;
-    private placement: PlacementEnum;
     private staffNumber: number;
     private length: number;
 
@@ -165,13 +164,14 @@ export enum DynamicEnum {
     fffff = 12,
     ffffff = 13,
     sf = 14,
-    sfp = 15,
-    sfpp = 16,
-    fp = 17,
-    rf = 18,
-    rfz = 19,
-    sfz = 20,
-    sffz = 21,
-    fz = 22,
-    other = 23
+    sff = 15,
+    sfp = 16,
+    sfpp = 17,
+    fp = 18,
+    rf = 19,
+    rfz = 20,
+    sfz = 21,
+    sffz = 22,
+    fz = 23,
+    other = 24
 }

+ 1 - 3
src/MusicalScore/VoiceData/Expressions/MoodExpression.ts

@@ -2,9 +2,8 @@ import {PlacementEnum, AbstractExpression} from "./AbstractExpression";
 
 export class MoodExpression extends AbstractExpression {
     constructor(label: string, placement: PlacementEnum, staffNumber: number) {
-        super();
+        super(placement);
         this.label = label;
-        this.placement = placement;
         this.staffNumber = staffNumber;
         this.setMoodType();
     }
@@ -47,7 +46,6 @@ export class MoodExpression extends AbstractExpression {
     private moodType: MoodEnum;
     private label: string;
     private staffNumber: number;
-    private placement: PlacementEnum;
 
     public static isInputStringMood(inputString: string): boolean {
         if (inputString === undefined) {

+ 2 - 4
src/MusicalScore/VoiceData/Expressions/UnknownExpression.ts

@@ -2,10 +2,9 @@ import {PlacementEnum, AbstractExpression} from "./AbstractExpression";
 import {TextAlignmentEnum} from "../../../Common/Enums/TextAlignment";
 
 export class UnknownExpression extends AbstractExpression {
-    constructor(label: string, placementEnum: PlacementEnum, textAlignment: TextAlignmentEnum, staffNumber: number) {
-        super();
+    constructor(label: string, placement: PlacementEnum, textAlignment: TextAlignmentEnum, staffNumber: number) {
+        super(placement);
         this.label = label;
-        this.placement = placementEnum;
         this.staffNumber = staffNumber;
         if (textAlignment === undefined) {
             textAlignment = TextAlignmentEnum.LeftBottom;
@@ -13,7 +12,6 @@ export class UnknownExpression extends AbstractExpression {
         this.textAlignment = textAlignment;
     }
     private label: string;
-    private placement: PlacementEnum;
     private textAlignment: TextAlignmentEnum;
     private staffNumber: number;
 

+ 3 - 0
src/MusicalScore/VoiceData/Instructions/TechnicalInstruction.ts

@@ -1,7 +1,10 @@
+import { PlacementEnum } from "../Expressions/AbstractExpression";
+
 export enum TechnicalInstructionType {
     Fingering
 }
 export class TechnicalInstruction {
     public type: TechnicalInstructionType;
     public value: string;
+    public placement: PlacementEnum;
 }

+ 26 - 3
src/MusicalScore/VoiceData/Note.ts

@@ -1,4 +1,4 @@
-import {VoiceEntry} from "./VoiceEntry";
+import {VoiceEntry, StemDirectionType} from "./VoiceEntry";
 import {SourceStaffEntry} from "./SourceStaffEntry";
 import {Fraction} from "../../Common/DataObjects/Fraction";
 import {Pitch} from "../../Common/DataObjects/Pitch";
@@ -9,6 +9,7 @@ import {Staff} from "./Staff";
 import {Slur} from "./Expressions/ContinuousExpressions/Slur";
 import {NoteState} from "../Graphical/DrawingEnums";
 import {NoteHead} from "./NoteHead";
+import {Arpeggio} from "./Arpeggio";
 
 /**
  * Represents a single pitch with a duration (length)
@@ -47,7 +48,12 @@ export class Note {
     private noteHead: NoteHead = undefined;
     /** States whether the note should be displayed. False if xmlNode.attribute("print-object").value = "no". */
     private printObject: boolean = true;
-
+    /** The Arpeggio this note is part of. */
+    private arpeggio: Arpeggio;
+    /** States whether this is a cue note (Stichnote) (smaller size). */
+    private isCueNote: boolean;
+    /** The stem direction asked for in XML. Not necessarily final or wanted stem direction. */
+    private stemDirectionXml: StemDirectionType;
 
     public get ParentVoiceEntry(): VoiceEntry {
         return this.voiceEntry;
@@ -109,10 +115,27 @@ export class Note {
     public get PrintObject(): boolean {
         return this.printObject;
     }
-
     public set PrintObject(value: boolean) {
         this.printObject = value;
     }
+    public get Arpeggio(): Arpeggio {
+        return this.arpeggio;
+    }
+    public set Arpeggio(value: Arpeggio) {
+        this.arpeggio = value;
+    }
+    public get IsCueNote(): boolean {
+        return this.isCueNote;
+    }
+    public set IsCueNote(value: boolean) {
+        this.isCueNote = value;
+    }
+    public get StemDirectionXml(): StemDirectionType {
+        return this.stemDirectionXml;
+    }
+    public set StemDirectionXml(value: StemDirectionType) {
+        this.stemDirectionXml = value;
+    }
 
     public isRest(): boolean {
         return this.Pitch === undefined;

+ 9 - 5
src/MusicalScore/VoiceData/NoteHead.ts

@@ -60,9 +60,13 @@ export class NoteHead {
      * Necessary because "circle-x" is not a valid enum member name.
      */
     public static ShapeTypeXmlToShape(shapeTypeXml: string): NoteHeadShape {
-        switch (shapeTypeXml) {
+        switch (shapeTypeXml.toLowerCase()) {
             case "normal":
                 return NoteHeadShape.NORMAL;
+            case "x":
+                return NoteHeadShape.X;
+            case "slash":
+                return NoteHeadShape.SLASH;
             case "diamond":
                 return NoteHeadShape.DIAMOND;
             case "square":
@@ -72,10 +76,8 @@ export class NoteHead {
             case "do":
             case "triangle":
                 return NoteHeadShape.TRIANGLE;
-            case "x":
-                return NoteHeadShape.X;
-            case "slash":
-                return NoteHeadShape.SLASH;
+            case "rectangle":
+                return NoteHeadShape.RECTANGLE;
             case "circle-x":
                 return NoteHeadShape.CIRCLEX;
             default:
@@ -90,9 +92,11 @@ export enum NoteHeadShape {
     CIRCLEX,
     DIAMOND,
     NORMAL,
+    RECTANGLE,
     SLASH,
     SQUARE,
     TRIANGLE,
     X,
     // TODO: Add the rest from https://usermanuals.musicxml.com/MusicXML/Content/ST-MusicXML-notehead-value.htm
+    // currently all Vexflow supported shapes present
 }

+ 38 - 8
src/MusicalScore/VoiceData/VoiceEntry.ts

@@ -10,6 +10,7 @@ import {KeyInstruction} from "./Instructions/KeyInstruction";
 import {OrnamentEnum} from "./OrnamentContainer";
 import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {Arpeggio} from "./Arpeggio";
 
 /**
  * A [[VoiceEntry]] contains the notes in a voice at a timestamp.
@@ -29,6 +30,7 @@ export class VoiceEntry {
         this.parentVoice = parentVoice;
         this.parentSourceStaffEntry = parentSourceStaffEntry;
         this.isGrace = isGrace;
+        this.graceAfterMainNote = false;
         this.graceNoteSlash = graceNoteSlash;
         this.graceSlur = graceSlur;
     }
@@ -38,13 +40,19 @@ export class VoiceEntry {
     private timestamp: Fraction;
     private notes: Note[] = [];
     private isGrace: boolean;
+    /** States whether the grace notes come after a main note (at end of measure). */
+    private graceAfterMainNote: boolean;
     private graceNoteSlash: boolean;
     private graceSlur: boolean; // TODO grace slur system could be refined to be non-binary
     private articulations: ArticulationEnum[] = [];
     private technicalInstructions: TechnicalInstruction[] = [];
     private lyricsEntries: Dictionary<number, LyricsEntry> = new Dictionary<number, LyricsEntry>();
-    private arpeggiosNotesIndices: number[] = [];
+    /** The Arpeggio consisting of this VoiceEntry's notes. Undefined if no arpeggio exists. */
+    private arpeggio: Arpeggio;
     private ornamentContainer: OrnamentContainer;
+    private wantedStemDirection: StemDirectionType = StemDirectionType.Undefined;
+    /** Stem direction specified in the xml stem element. */
+    private wantedStemDirectionXml: StemDirectionType = StemDirectionType.Undefined;
     private stemDirection: StemDirectionType = StemDirectionType.Undefined;
 
     public get ParentSourceStaffEntry(): SourceStaffEntry {
@@ -68,6 +76,12 @@ export class VoiceEntry {
     public set IsGrace(value: boolean) {
         this.isGrace = value;
     }
+    public get GraceAfterMainNote(): boolean {
+        return this.graceAfterMainNote;
+    }
+    public set GraceAfterMainNote(value: boolean) {
+        this.graceAfterMainNote = value;
+    }
     public get GraceNoteSlash(): boolean {
         return this.graceNoteSlash;
     }
@@ -89,11 +103,11 @@ export class VoiceEntry {
     public get LyricsEntries(): Dictionary<number, LyricsEntry> {
         return this.lyricsEntries;
     }
-    public get ArpeggiosNotesIndices(): number[] {
-        return this.arpeggiosNotesIndices;
+    public get Arpeggio(): Arpeggio {
+        return this.arpeggio;
     }
-    public set ArpeggiosNotesIndices(value: number[]) {
-        this.arpeggiosNotesIndices = value;
+    public set Arpeggio(value: Arpeggio) {
+        this.arpeggio = value;
     }
     public get OrnamentContainer(): OrnamentContainer {
         return this.ornamentContainer;
@@ -102,12 +116,27 @@ export class VoiceEntry {
         this.ornamentContainer = value;
     }
 
-    public get StemDirection(): StemDirectionType {
-        return this.stemDirection;
+    // WantedStemDirection provides the stem direction to VexFlow in case of more than 1 voice
+    // for optimal graphical appearance
+    public set WantedStemDirection(value: StemDirectionType) {
+        this.wantedStemDirection = value;
+    }
+    public get WantedStemDirection(): StemDirectionType {
+        return this.wantedStemDirection;
     }
+    public set WantedStemDirectionXml(value: StemDirectionType) {
+        this.wantedStemDirectionXml = value;
+    }
+    public get WantedStemDirectionXml(): StemDirectionType {
+        return this.wantedStemDirectionXml;
+    }
+    // StemDirection holds the actual value of the stem
     public set StemDirection(value: StemDirectionType) {
         this.stemDirection = value;
     }
+    public get StemDirection(): StemDirectionType {
+        return this.stemDirection;
+    }
 
     public static isSupportedArticulation(articulation: ArticulationEnum): boolean {
         switch (articulation) {
@@ -362,5 +391,6 @@ export enum StemDirectionType {
     Undefined = -1,
     Up = 0,
     Down = 1,
-    None = 2
+    None = 2,
+    Double = 3
 }

+ 35 - 9
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -2,8 +2,10 @@ import { DrawingParametersEnum } from "../MusicalScore/Graphical/DrawingParamete
 
 /** Possible options for the OpenSheetMusicDisplay constructor, none are mandatory. */
 export interface IOSMDOptions {
-    /** Not yet supported. Will always beam automatically. */ // TODO
+    /** Whether to automatically create beams for notes that don't have beams set in XML. */
     autoBeam?: boolean;
+    /** Options for autoBeaming like whether to beam over rests. See AutoBeamOptions interface. */
+    autoBeamOptions?: AutoBeamOptions;
     /** Automatically resize score with canvas size. Default is true. */
     autoResize?: boolean;
     /** Not yet supported. Will always place stems automatically. */ // TODO
@@ -14,22 +16,26 @@ export interface IOSMDOptions {
     disableCursor?: boolean;
     /** Broad Parameters like compact or preview mode. */
     drawingParameters?: string | DrawingParametersEnum;
-    /** Whether to draw hidden/invisible notes (print-object="no" in XML). Default false. Not yet supported. */ // TODO
-    drawHiddenNotes?: boolean;
-    /** Default color for a note head (without stem). Default black. Not yet supported. */ // TODO
-    defaultColorNoteHead?: string;
-    /** Default color for a note stem. Default black. Not yet supported. */ // TODO
-    defaultColorStem?: string;
     /** Whether to draw the title of the piece. If false, disables drawing Subtitle as well. */
     drawTitle?: boolean;
     /** Whether to draw the subtitle of the piece. If true, enables drawing Title as well. */
     drawSubtitle?: boolean;
     /** Whether to draw credits (title, composer, arranger, copyright etc., see <credit>. Not yet supported. */ // TODO
     drawCredits?: boolean;
-    /** Whether to draw part (instrument) names. */
-    drawPartNames?: boolean;
     /** Whether to draw the lyricist's name, if given. */
     drawLyricist?: boolean;
+    /** Whether to draw part (instrument) names. */
+    drawPartNames?: boolean;
+    /** Whether to draw fingerings (only left to the note for now). Default true. */
+    drawFingerings?: boolean;
+    /** Where to draw fingerings (left, right, above, below, auto).
+     * Default left. Auto, above, below experimental (potential collisions because bounding box not correct)
+     */
+    fingeringPosition?: string;
+    /** For above/below fingerings, whether to draw them directly above/below notes (default), or above/below staffline. */
+    fingeringInsideStafflines?: boolean;
+    /** Whether to set the wanted stem direction by xml (default) or automatically. */
+    setWantedStemDirectionByXml?: boolean;
     /** Whether tuplets are labeled with ratio (e.g. 5:2 instead of 5 for quintuplets). Default false. */
     tupletsRatioed?: boolean;
     /** Whether all tuplets should be bracketed (e.g. |--5--| instead of 5). Default false.
@@ -41,6 +47,12 @@ export interface IOSMDOptions {
      * (Bracketing all triplets can be cluttering)
      */
     tripletsBracketed?: boolean;
+    /** Whether to draw hidden/invisible notes (print-object="no" in XML). Default false. Not yet supported. */ // TODO
+    drawHiddenNotes?: boolean;
+    /** Default color for a note head (without stem). Default black. Not yet supported. */ // TODO
+    defaultColorNoteHead?: string;
+    /** Default color for a note stem. Default black. Not yet supported. */ // TODO
+    defaultColorStem?: string;
 }
 
 /** Handles [[IOSMDOptions]], e.g. returning default options with OSMDOptionsStandard() */
@@ -56,3 +68,17 @@ export class OSMDOptions {
         };
     }
 }
+
+export interface AutoBeamOptions {
+    /** Whether to extend beams over rests. Default false. */
+    beam_rests?: boolean;
+    /** Whether to extend beams only over rests that are in the middle of a potential beam. Default false. */
+    beam_middle_rests_only?: boolean;
+    /** Whether to maintain stem direction of autoBeamed notes. Discouraged, reduces beams. Default false. */
+    maintain_stem_directions?: boolean;
+    /** Groups of notes (fractions) to beam within a measure.
+     * List of fractions, each fraction being [nominator, denominator].
+     * E.g. [[3,4],[1,4]] will beam the first 3 quarters of a measure, then the last quarter.
+     */
+    groups?: [number[]];
+}

+ 165 - 92
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -14,8 +14,9 @@ import {Promise} from "es6-promise";
 import {AJAX} from "./AJAX";
 import * as log from "loglevel";
 import {DrawingParametersEnum, DrawingParameters} from "../MusicalScore/Graphical/DrawingParameters";
-import {IOSMDOptions, OSMDOptions} from "./OSMDOptions";
+import {IOSMDOptions, OSMDOptions, AutoBeamOptions} from "./OSMDOptions";
 import {EngravingRules} from "../MusicalScore/Graphical/EngravingRules";
+import {AbstractExpression} from "../MusicalScore/VoiceData/Expressions/AbstractExpression";
 
 /**
  * The main class and control point of OpenSheetMusicDisplay.<br>
@@ -46,25 +47,10 @@ export class OpenSheetMusicDisplay {
             throw new Error("Please pass a valid div container to OpenSheetMusicDisplay");
         }
 
-        if (options.backend === undefined || options.backend.toLowerCase() === "svg") {
-            this.backend = new SvgVexFlowBackend();
-        } else {
-            this.backend = new CanvasVexFlowBackend();
-        }
-
-        this.setDrawingParameters(options);
-
-        this.backend.initialize(this.container);
-        this.canvas = this.backend.getCanvas();
-        this.innerElement = this.backend.getInnerElement();
-        this.enableOrDisableCursor(this.drawingParameters.drawCursors);
-
-        // Create the drawer
-        this.drawer = new VexFlowMusicSheetDrawer(this.canvas, this.backend, this.drawingParameters);
-
-        if (options.autoResize) {
-            this.autoResize();
+        if (options.autoResize === undefined) {
+            options.autoResize = true;
         }
+        this.setOptions(options);
     }
 
     public cursor: Cursor;
@@ -78,6 +64,8 @@ export class OpenSheetMusicDisplay {
     private drawer: VexFlowMusicSheetDrawer;
     private graphic: GraphicalMusicSheet;
     private drawingParameters: DrawingParameters;
+    private autoResizeEnabled: boolean;
+    private resizeHandlerAttached: boolean;
 
     /**
      * Load a MusicXML file
@@ -144,7 +132,7 @@ export class OpenSheetMusicDisplay {
             return Promise.reject(new Error("given music sheet was incomplete or could not be loaded."));
         }
         this.graphic = new GraphicalMusicSheet(this.sheet, calc);
-        if (this.drawingParameters.drawCursors) {
+        if (this.drawingParameters.drawCursors && this.cursor) {
             this.cursor.init(this.sheet.MusicPartManager, this.graphic);
         }
         log.info(`Loaded sheet ${this.sheet.TitleString} successfully.`);
@@ -158,33 +146,162 @@ export class OpenSheetMusicDisplay {
         if (!this.graphic) {
             throw new Error("OpenSheetMusicDisplay: Before rendering a music sheet, please load a MusicXML file");
         }
+        this.drawer.clear(); // clear canvas before setting width
+
+        // Set page width
         const width: number = this.container.offsetWidth;
+        this.sheet.pageWidth = width / this.zoom / 10.0;
         // Before introducing the following optimization (maybe irrelevant), tests
         // have to be modified to ensure that width is > 0 when executed
         //if (isNaN(width) || width === 0) {
         //    return;
         //}
 
-        // Set page width
-        this.sheet.pageWidth = width / this.zoom / 10.0;
         // Calculate again
         this.graphic.reCalculate();
+        const height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * 10.0 * this.zoom;
         if (this.drawingParameters.drawCursors) {
             this.graphic.Cursors.length = 0;
         }
         // Update Sheet Page
-        const height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * 10.0 * this.zoom;
-        this.drawer.clear();
         this.drawer.resize(width, height);
         this.drawer.scale(this.zoom);
         // Finally, draw
         this.drawer.drawSheet(this.graphic);
-        if (this.drawingParameters.drawCursors) {
+        if (this.drawingParameters.drawCursors && this.cursor) {
             // Update the cursor position
             this.cursor.update();
         }
     }
 
+    /** States whether the render() function can be safely called. */
+    public IsReadyToRender(): boolean {
+        return this.graphic !== undefined;
+    }
+
+    /** Clears what OSMD has drawn on its canvas. */
+    public clear(): void {
+        this.drawer.clear();
+        this.reset(); // without this, resize will draw loaded sheet again
+    }
+
+    /** Set OSMD rendering options using an IOSMDOptions object.
+     *  Can be called during runtime. Also called by constructor.
+     *  For example, setOptions({autoResize: false}) will disable autoResize even during runtime.
+     */
+    public setOptions(options: IOSMDOptions): void {
+        this.drawingParameters = new DrawingParameters();
+        if (options.drawingParameters) {
+            this.drawingParameters.DrawingParametersEnum =
+                (<any>DrawingParametersEnum)[options.drawingParameters.toLowerCase()];
+        }
+
+        const updateExistingBackend: boolean = this.backend !== undefined;
+        if (options.backend !== undefined || this.backend === undefined) {
+            if (updateExistingBackend) {
+                // TODO doesn't work yet, still need to create a new OSMD object
+
+                this.drawer.clear();
+
+                // musicSheetCalculator.clearSystemsAndMeasures() // maybe? don't have reference though
+                // musicSheetCalculator.clearRecreatedObjects();
+            }
+            if (options.backend === undefined || options.backend.toLowerCase() === "svg") {
+                this.backend = new SvgVexFlowBackend();
+            } else {
+                this.backend = new CanvasVexFlowBackend();
+            }
+            this.backend.initialize(this.container);
+            this.canvas = this.backend.getCanvas();
+            this.innerElement = this.backend.getInnerElement();
+            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
+            // Create the drawer
+            this.drawer = new VexFlowMusicSheetDrawer(this.canvas, this.backend, this.drawingParameters);
+        }
+
+        // individual drawing parameters options
+        if (options.autoBeam !== undefined) {
+            EngravingRules.Rules.AutoBeamNotes = options.autoBeam;
+        }
+        const autoBeamOptions: AutoBeamOptions = options.autoBeamOptions;
+        if (autoBeamOptions) {
+            if (autoBeamOptions.maintain_stem_directions === undefined) {
+                autoBeamOptions.maintain_stem_directions = false;
+            }
+            EngravingRules.Rules.AutoBeamOptions = autoBeamOptions;
+            if (autoBeamOptions.groups && autoBeamOptions.groups.length) {
+                for (const fraction of autoBeamOptions.groups) {
+                    if (fraction.length !== 2) {
+                        throw new Error("Each fraction in autoBeamOptions.groups must be of length 2, e.g. [3,4] for beaming three fourths");
+                    }
+                }
+            }
+        }
+        if (options.disableCursor) {
+            this.drawingParameters.drawCursors = false;
+            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
+        }
+        // alternative to if block: this.drawingsParameters.drawCursors = options.drawCursors !== false. No if, but always sets drawingParameters.
+        // note that every option can be undefined, which doesn't mean the option should be set to false.
+        if (options.drawHiddenNotes) {
+            this.drawingParameters.drawHiddenNotes = true;
+        }
+        if (options.drawTitle !== undefined) {
+            this.drawingParameters.DrawTitle = options.drawTitle;
+            // TODO these settings are duplicate in drawingParameters and EngravingRules. Maybe we only need them in EngravingRules.
+            // this sets the parameter in DrawingParameters, which in turn sets the parameter in EngravingRules.
+            // see settings below that don't call drawingParameters for the immediate approach
+        }
+        if (options.drawSubtitle !== undefined) {
+            this.drawingParameters.DrawSubtitle = options.drawSubtitle;
+        }
+        if (options.drawLyricist !== undefined) {
+            this.drawingParameters.DrawLyricist = options.drawLyricist;
+        }
+        if (options.drawCredits !== undefined) {
+            this.drawingParameters.drawCredits = options.drawCredits;
+        }
+        if (options.drawPartNames !== undefined) {
+            this.drawingParameters.DrawPartNames = options.drawPartNames;
+        }
+        if (options.drawFingerings === false) {
+            EngravingRules.Rules.RenderFingerings = false;
+        }
+        if (options.fingeringPosition !== undefined) {
+            EngravingRules.Rules.FingeringPosition = AbstractExpression.PlacementEnumFromString(options.fingeringPosition);
+        }
+        if (options.fingeringInsideStafflines !== undefined) {
+            EngravingRules.Rules.FingeringInsideStafflines = options.fingeringInsideStafflines;
+        }
+        if (options.setWantedStemDirectionByXml !== undefined) {
+            EngravingRules.Rules.SetWantedStemDirectionByXml = options.setWantedStemDirectionByXml;
+        }
+        if (options.defaultColorNoteHead) {
+            this.drawingParameters.defaultColorNoteHead = options.defaultColorNoteHead;
+        }
+        if (options.defaultColorStem) {
+            this.drawingParameters.defaultColorStem = options.defaultColorStem;
+        }
+        if (options.tupletsRatioed) {
+            EngravingRules.Rules.TupletsRatioed = true;
+        }
+        if (options.tupletsBracketed) {
+            EngravingRules.Rules.TupletsBracketed = true;
+        }
+        if (options.tripletsBracketed) {
+            EngravingRules.Rules.TripletsBracketed = true;
+        }
+        if (options.autoResize) {
+            if (!this.resizeHandlerAttached) {
+                this.autoResize();
+            }
+            this.autoResizeEnabled = true;
+        } else if (options.autoResize === false) { // not undefined
+            this.autoResizeEnabled = false;
+            // we could remove the window EventListener here, but not necessary.
+        }
+    }
+
     /**
      * Sets the logging level for this OSMD instance. By default, this is set to `warn`.
      *
@@ -219,14 +336,12 @@ export class OpenSheetMusicDisplay {
      * FIXME: Probably unnecessary
      */
     private reset(): void {
-        if (this.drawingParameters.drawCursors) {
+        if (this.drawingParameters.drawCursors && this.cursor) {
             this.cursor.hide();
         }
         this.sheet = undefined;
         this.graphic = undefined;
         this.zoom = 1.0;
-        // this.canvas.width = 0;
-        // this.canvas.height = 0;
     }
 
     /**
@@ -250,7 +365,7 @@ export class OpenSheetMusicDisplay {
                 //    document.documentElement.offsetWidth
                 //);
                 //self.container.style.width = width + "px";
-                if (this.graphic !== undefined) {
+                if (self.IsReadyToRender()) {
                     self.render();
                 }
             }
@@ -263,12 +378,22 @@ export class OpenSheetMusicDisplay {
      * @param endCallback is the function called when resizing (kind-of) ends
      */
     private handleResize(startCallback: () => void, endCallback: () => void): void {
-        if (this.graphic === undefined) {
-            return;
-        }
         let rtime: number;
         let timeout: number = undefined;
         const delta: number = 200;
+        const self: OpenSheetMusicDisplay = this;
+
+        function resizeStart(): void {
+            if (!self.AutoResizeEnabled) {
+                return;
+            }
+            rtime = (new Date()).getTime();
+            if (!timeout) {
+                startCallback();
+                rtime = (new Date()).getTime();
+                timeout = window.setTimeout(resizeEnd, delta);
+            }
+        }
 
         function resizeEnd(): void {
             timeout = undefined;
@@ -280,21 +405,13 @@ export class OpenSheetMusicDisplay {
             }
         }
 
-        function resizeStart(): void {
-            rtime = (new Date()).getTime();
-            if (!timeout) {
-                startCallback();
-                rtime = (new Date()).getTime();
-                timeout = window.setTimeout(resizeEnd, delta);
-            }
-        }
-
         if ((<any>window).attachEvent) {
             // Support IE<9
             (<any>window).attachEvent("onresize", resizeStart);
         } else {
             window.addEventListener("resize", resizeStart);
         }
+        this.resizeHandlerAttached = true;
 
         window.setTimeout(startCallback, 0);
         window.setTimeout(endCallback, 1);
@@ -324,61 +441,12 @@ export class OpenSheetMusicDisplay {
     }
 
     //#region GETTER / SETTER
-    private setDrawingParameters(options: IOSMDOptions): void {
-        this.drawingParameters = new DrawingParameters();
-        if (options.drawingParameters) {
-            this.drawingParameters.DrawingParametersEnum =
-                (<any>DrawingParametersEnum)[options.drawingParameters.toLowerCase()];
-        }
-        // individual drawing parameters options
-        if (options.disableCursor) {
-            this.drawingParameters.drawCursors = false;
-        }
-        if (options.drawHiddenNotes) {
-            this.drawingParameters.drawHiddenNotes = true;
-        }
-        if (options.drawTitle !== undefined) {
-            this.drawingParameters.DrawTitle = options.drawTitle;
-            // TODO these settings are duplicate in drawingParameters and EngravingRules. Maybe we only need them in EngravingRules.
-            // this sets the parameter in DrawingParameters, which in turn sets the parameter in EngravingRules.
-            // see tuplets settings below for the immediate approach
-        }
-        if (options.drawSubtitle !== undefined) {
-            this.drawingParameters.DrawSubtitle = options.drawSubtitle;
-        }
-        if (options.drawPartNames !== undefined) {
-            this.drawingParameters.DrawPartNames = options.drawPartNames;
-        }
-        if (options.drawLyricist !== undefined) {
-            this.drawingParameters.DrawLyricist = options.drawLyricist;
-        }
-        if (options.drawCredits !== undefined) {
-            this.drawingParameters.drawCredits = options.drawCredits;
-        }
-        if (options.defaultColorNoteHead) {
-            this.drawingParameters.defaultColorNoteHead = options.defaultColorNoteHead;
-        }
-        if (options.defaultColorStem) {
-            this.drawingParameters.defaultColorStem = options.defaultColorStem;
-        }
-        if (options.tupletsRatioed) {
-            EngravingRules.Rules.TupletsRatioed = true;
-        }
-        if (options.tupletsBracketed) {
-            EngravingRules.Rules.TupletsBracketed = true;
-        }
-        if (options.tripletsBracketed) {
-            EngravingRules.Rules.TripletsBracketed = true;
-        }
-    }
-
     public set DrawSkyLine(value: boolean) {
         if (this.drawer) {
             this.drawer.skyLineVisible = value;
             this.render();
         }
     }
-
     public get DrawSkyLine(): boolean {
         return this.drawer.skyLineVisible;
     }
@@ -389,7 +457,6 @@ export class OpenSheetMusicDisplay {
             this.render();
         }
     }
-
     public get DrawBottomLine(): boolean {
         return this.drawer.bottomLineVisible;
     }
@@ -398,9 +465,15 @@ export class OpenSheetMusicDisplay {
         this.drawer.drawableBoundingBoxElement = value;
         this.render();
     }
-
     public get DrawBoundingBox(): string {
         return this.drawer.drawableBoundingBoxElement;
     }
+
+    public get AutoResizeEnabled(): boolean {
+        return this.autoResizeEnabled;
+    }
+    public set AutoResizeEnabled(value: boolean) {
+        this.autoResizeEnabled = value;
+    }
     //#endregion
 }

+ 10 - 0
src/Util/CollectionUtil.ts

@@ -2,7 +2,11 @@ import Dictionary from "typescript-collections/dist/lib/Dictionary";
 
 declare global {
     interface Array<T> {
+        /** Returns the last element from an array */
         last(): T;
+        /** Deletes all elements from an array */
+        clear(): void;
+        /** Returns true if the element is found in the array */
         contains(elem: T): boolean;
     }
 }
@@ -13,6 +17,12 @@ if (!Array.prototype.last) {
     };
 }
 
+if (!Array.prototype.clear) {
+    Array.prototype.clear = function<T>(): void {
+        this.length = 0;
+    };
+}
+
 if (!Array.prototype.contains) {
     Array.prototype.contains = function<T>(elem: T): boolean {
         return this.indexOf(elem) !== -1;

+ 1 - 1
test/Util/TestUtils.ts

@@ -40,6 +40,6 @@ export class TestUtils {
     }
 
     public static createOpenSheetMusicDisplay(div: HTMLElement): OpenSheetMusicDisplay {
-        return new OpenSheetMusicDisplay(div);
+        return new OpenSheetMusicDisplay(div, {autoResize: false});
     }
 }

+ 66 - 66
test/data/JohannSebastianBach_PraeludiumInCDur_BWV846_1.xml

@@ -283,7 +283,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -321,7 +321,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -559,7 +559,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -597,7 +597,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -847,7 +847,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -885,7 +885,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -1123,7 +1123,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -1161,7 +1161,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -1411,7 +1411,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -1449,7 +1449,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -1692,7 +1692,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -1730,7 +1730,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -1980,7 +1980,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -2018,7 +2018,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -2256,7 +2256,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -2294,7 +2294,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -2544,7 +2544,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -2582,7 +2582,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -2825,7 +2825,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -2863,7 +2863,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -3113,7 +3113,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -3151,7 +3151,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -3394,7 +3394,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -3435,7 +3435,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -3687,7 +3687,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -3725,7 +3725,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -3963,7 +3963,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -4004,7 +4004,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -4256,7 +4256,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest>
           <display-step>C</display-step>
           <display-octave>4</display-octave>
@@ -4297,7 +4297,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -4535,7 +4535,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -4573,7 +4573,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -4823,7 +4823,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -4861,7 +4861,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5099,7 +5099,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5137,7 +5137,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5387,7 +5387,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5425,7 +5425,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5668,7 +5668,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5706,7 +5706,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5956,7 +5956,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -5994,7 +5994,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -6237,7 +6237,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -6275,7 +6275,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -6528,7 +6528,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -6566,7 +6566,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -6807,7 +6807,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -6845,7 +6845,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7095,7 +7095,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7133,7 +7133,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7371,7 +7371,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7409,7 +7409,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7659,7 +7659,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7697,7 +7697,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7940,7 +7940,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -7981,7 +7981,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -8233,7 +8233,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -8272,7 +8272,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -8510,7 +8510,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -8548,7 +8548,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -8798,7 +8798,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -8836,7 +8836,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -9079,7 +9079,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -9117,7 +9117,7 @@
           <tied type="stop"/>
           </notations>
         </note>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -9386,7 +9386,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>
@@ -9648,7 +9648,7 @@
       <backup>
         <duration>16</duration>
         </backup>
-      <note>
+      <note print-object="no">
         <rest/>
         <duration>1</duration>
         <voice>5</voice>

+ 366 - 14
test/data/OSMD_function_test_all.xml

@@ -761,7 +761,7 @@
         </note>
       </measure>
     <measure number="15" width="318.48">
-      <note default-x="47.11" default-y="-30.00">
+      <note default-x="110.28" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -772,7 +772,7 @@
         <stem>up</stem>
         <notations>
           <technical>
-            <fingering>0</fingering>
+            <fingering>2</fingering>
             </technical>
           </notations>
         <lyric number="1" default-x="6.22" default-y="-80.00">
@@ -780,7 +780,39 @@
           <text>Fingering</text>
           </lyric>
         </note>
-      <note default-x="122.90" default-y="-35.00">
+      <note default-x="110.28" default-y="-20.00">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <technical>
+            <fingering>3</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="110.28" default-y="-10.00">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <technical>
+            <fingering>5</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="387.67" default-y="-35.00">
         <pitch>
           <step>F</step>
           <octave>4</octave>
@@ -796,7 +828,7 @@
             </technical>
           </notations>
         </note>
-      <note default-x="166.01" default-y="-40.00">
+      <note default-x="545.97" default-y="-40.00">
         <pitch>
           <step>E</step>
           <octave>4</octave>
@@ -808,28 +840,30 @@
         <beam number="1">end</beam>
         <notations>
           <technical>
-            <fingering>2</fingering>
+            <fingering>3</fingering>
             </technical>
           </notations>
         </note>
-      <note default-x="209.11" default-y="-45.00">
+      <note default-x="704.26" default-y="-45.00">
         <pitch>
           <step>D</step>
+          <alter>1</alter>
           <octave>4</octave>
           </pitch>
         <duration>1</duration>
         <voice>1</voice>
         <type>16th</type>
+        <accidental>sharp</accidental>
         <stem>up</stem>
         <beam number="1">begin</beam>
         <beam number="2">begin</beam>
         <notations>
           <technical>
-            <fingering>3</fingering>
+            <fingering>2</fingering>
             </technical>
           </notations>
         </note>
-      <note default-x="236.05" default-y="-40.00">
+      <note default-x="803.20" default-y="-40.00">
         <pitch>
           <step>E</step>
           <octave>4</octave>
@@ -842,11 +876,11 @@
         <beam number="2">continue</beam>
         <notations>
           <technical>
-            <fingering>4</fingering>
+            <fingering>3</fingering>
             </technical>
           </notations>
         </note>
-      <note default-x="262.99" default-y="-35.00">
+      <note default-x="902.14" default-y="-35.00">
         <pitch>
           <step>F</step>
           <octave>4</octave>
@@ -859,11 +893,11 @@
         <beam number="2">continue</beam>
         <notations>
           <technical>
-            <fingering>5</fingering>
+            <fingering>4</fingering>
             </technical>
           </notations>
         </note>
-      <note default-x="289.93" default-y="-45.00">
+      <note default-x="1001.08" default-y="-45.00">
         <pitch>
           <step>D</step>
           <octave>4</octave>
@@ -871,9 +905,15 @@
         <duration>1</duration>
         <voice>1</voice>
         <type>16th</type>
+        <accidental>natural</accidental>
         <stem>up</stem>
         <beam number="1">end</beam>
         <beam number="2">end</beam>
+        <notations>
+          <technical>
+            <fingering>0</fingering>
+            </technical>
+          </notations>
         </note>
       </measure>
     <measure number="16" width="126.44">
@@ -3045,11 +3085,323 @@
         <bar-style>light-heavy</bar-style>
         </barline>
       </measure>
-    <measure number="38" width="112.42">
+      <measure number="38" width="310.04">
+      <attributes>
+        <divisions>1</divisions>
+	</attributes>
+      <note default-x="110.18" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>whole</type>
+        <lyric number="1" default-x="9.90" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>Arpeggios</text>
+          </lyric>
+        </note>
+      </measure>
+    <measure number="39" width="263.63">
+      <note default-x="37.83" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-13.81" default-y="-14.55"/>
+          </notations>
+        <lyric number="1" default-x="6.22" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>Neutral</text>
+          </lyric>
+        </note>
+      <note default-x="37.83" default-y="-30.00">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-13.81" default-y="-14.55"/>
+          </notations>
+        </note>
+      <note default-x="37.83" default-y="-20.00">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-13.81" default-y="-14.55"/>
+          </notations>
+        </note>
+      <note default-x="149.75" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate direction="up" default-x="-18.53" default-y="-14.55"/>
+          </notations>
+        <lyric number="1" default-x="6.22" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>Up</text>
+          </lyric>
+        </note>
+      <note default-x="149.75" default-y="-30.00">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate direction="up" default-x="-18.53" default-y="-14.55"/>
+          </notations>
+        </note>
+      <note default-x="149.75" default-y="-20.00">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate direction="up" default-x="-18.53" default-y="-14.55"/>
+          </notations>
+        </note>
+      </measure>
+    <measure number="40" width="226.96">
+      <note default-x="31.27" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate direction="down" default-x="-18.53" default-y="-14.55"/>
+          </notations>
+        <lyric number="1" default-x="6.22" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>Down</text>
+          </lyric>
+        </note>
+      <note default-x="31.27" default-y="-30.00">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate direction="down" default-x="-18.53" default-y="-14.55"/>
+          </notations>
+        </note>
+      <note default-x="31.27" default-y="-20.00">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate direction="down" default-x="-18.53" default-y="-14.55"/>
+          </notations>
+        </note>
       <note>
         <rest/>
-        <duration>16</duration>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      </measure>
+    <measure number="41" width="309.98">
+      <note default-x="8.11" default-y="-5.00">
+        <grace/>
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <beam number="1">begin</beam>
+        </note>
+      <note default-x="30.53" default-y="0.00">
+        <grace/>
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <stem>up</stem>
+        <beam number="1">continue</beam>
+        </note>
+      <note default-x="41.49" default-y="5.00">
+        <grace/>
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <beam number="1">end</beam>
+        </note>
+      <note default-x="66.66" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-13.81" default-y="0.45"/>
+          </notations>
+        </note>
+      <note default-x="66.66" default-y="-30.00">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-13.81" default-y="0.45"/>
+          </notations>
+        </note>
+      <note default-x="66.66" default-y="-20.00">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-13.81" default-y="0.45"/>
+          </notations>
+        </note>
+      <note default-x="66.66" default-y="-5.00">
+        <chord/>
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-13.81" default-y="0.45"/>
+          </notations>
+        </note>
+      <attributes>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+          <clef-octave-change>1</clef-octave-change>
+          </clef>
+        </attributes>
+      <note default-x="182.84" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-29.22" default-y="0.45"/>
+          </notations>
+        </note>
+      <note default-x="182.84" default-y="-30.00">
+        <chord/>
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
         <voice>1</voice>
+        <type>half</type>
+        <accidental>sharp</accidental>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-29.22" default-y="0.45"/>
+          </notations>
+        </note>
+      <note default-x="182.84" default-y="-20.00">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-29.22" default-y="0.45"/>
+          </notations>
+        </note>
+      <note default-x="182.84" default-y="-5.00">
+        <chord/>
+        <pitch>
+          <step>E</step>
+          <octave>6</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <arpeggiate default-x="-29.22" default-y="0.45"/>
+          </notations>
         </note>
       <barline location="right">
         <bar-style>light-heavy</bar-style>

+ 1669 - 0
test/data/OSMD_function_test_autobeam.musicxml

@@ -0,0 +1,1669 @@
+<?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>OSMD Function Test - AutoBeam</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 2.3.2</software>
+      <encoding-date>2018-10-18</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.05556</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1584</page-height>
+      <page-width>1224</page-width>
+      <page-margins type="even">
+        <left-margin>56.6929</left-margin>
+        <right-margin>56.6929</right-margin>
+        <top-margin>56.6929</top-margin>
+        <bottom-margin>113.386</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>56.6929</left-margin>
+        <right-margin>56.6929</right-margin>
+        <top-margin>56.6929</top-margin>
+        <bottom-margin>113.386</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="FreeSerif" font-size="10"/>
+    <lyric-font font-family="FreeSerif" font-size="11"/>
+    </defaults>
+  <credit page="1">
+    <credit-words default-x="612" default-y="1527.31" justify="center" valign="top" font-size="24">OSMD Function Test - AutoBeam</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="312.93">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>-0.00</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        </print>
+      <attributes>
+        <divisions>480</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        </attributes>
+      <note default-x="75.17" default-y="-50.00">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="104.69" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="134.21" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="163.73" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="193.25" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="222.77" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="252.29" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="281.81" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      </measure>
+    <measure number="2" width="260.88">
+      <note default-x="12.00" default-y="20.00">
+        <pitch>
+          <step>C</step>
+          <octave>6</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="42.91" default-y="25.00">
+        <pitch>
+          <step>D</step>
+          <octave>6</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="73.82" default-y="10.00">
+        <pitch>
+          <step>A</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="104.73" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="135.64" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="166.55" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="197.46" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="228.37" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      </measure>
+    <measure number="3" width="265.38">
+      <note default-x="16.27" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="47.21" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="78.15" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="109.08" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="140.02" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="170.96" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="201.90" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="232.84" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      </measure>
+    <measure number="4" width="271.42">
+      <note default-x="12.00" default-y="-50.00">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="44.23" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="76.46" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="108.68" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <alter>-1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="140.91" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="173.14" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="205.37" default-y="10.00">
+        <pitch>
+          <step>A</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="237.59" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      </measure>
+    <measure number="5" width="282.87">
+      <print new-system="yes">
+        <system-layout>
+          <system-margins>
+            <left-margin>-0.00</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <system-distance>150.00</system-distance>
+          </system-layout>
+        </print>
+      <note default-x="62.34" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <alter>-1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="91.28" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <alter>-1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="120.21" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="149.15" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="175.42" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="202.44" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>natural</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="228.72" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="254.99" default-y="-45.00">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      </measure>
+    <measure number="6" width="140.37">
+      <note default-x="12.00" default-y="-50.00">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="43.69" default-y="-45.00">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="75.38" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+        </note>
+      <note default-x="107.08" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="7" width="135.43">
+      <note default-x="12.00" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="45.84" default-y="15.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+        </note>
+      <note default-x="79.32" default-y="20.00">
+        <pitch>
+          <step>C</step>
+          <octave>6</octave>
+          </pitch>
+        <duration>960</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="8" width="165.36">
+      <note default-x="16.27" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>360</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="43.13" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>120</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>down</stem>
+
+
+        </note>
+      <note>
+        <rest/>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        </note>
+      <note default-x="90.02" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>360</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <dot/>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="116.87" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>120</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>down</stem>
+
+
+        </note>
+      <note>
+        <rest/>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        </note>
+      </measure>
+    <measure number="9" width="183.56">
+      <note>
+        <rest/>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        </note>
+      <note default-x="40.36" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+        </note>
+      <note default-x="68.53" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>120</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <stem>down</stem>
+
+
+        </note>
+      <note default-x="86.13" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="115.07" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <alter>-1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>120</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <accidental>flat</accidental>
+        <stem>down</stem>
+
+
+        </note>
+      <note>
+        <rest/>
+        <duration>960</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      </measure>
+    <measure number="10" width="203.03">
+      <note default-x="12.00" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="34.89" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="57.78" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="80.67" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="109.60" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="132.49" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="155.38" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="178.27" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      </measure>
+    <measure number="11" width="277.81">
+      <print new-system="yes">
+        <system-layout>
+          <system-margins>
+            <left-margin>-0.00</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <system-distance>150.00</system-distance>
+          </system-layout>
+        </print>
+      <note default-x="62.34" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="88.76" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="115.18" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="141.60" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="170.53" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="196.95" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="223.37" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="249.79" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      </measure>
+    <measure number="12" width="173.61">
+      <note default-x="18.42" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <accidental>sharp</accidental>
+        <stem>down</stem>
+        </note>
+      <note default-x="54.40" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="76.89" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="112.87" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="148.85" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <notations>
+          <tied type="start"/>
+          </notations>
+        </note>
+      </measure>
+    <measure number="13" width="172.55">
+      <note default-x="12.00" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>480</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <notations>
+          <tied type="stop"/>
+          </notations>
+        </note>
+      <note default-x="49.40" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="72.78" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="110.18" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note>
+        <rest/>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        </note>
+      </measure>
+    <measure number="14" width="285.26">
+      <note default-x="12.00" default-y="20.00">
+        <pitch>
+          <step>C</step>
+          <octave>6</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="51.52" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="75.34" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="99.16" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="122.98" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="146.80" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="170.61" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="194.43" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>96</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="210.95" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>96</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+
+        </note>
+      <note default-x="227.46" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>96</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+
+        </note>
+      <note default-x="243.98" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>96</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+
+        </note>
+      <note default-x="260.49" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>96</duration>
+        <voice>1</voice>
+        <type>16th</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      </measure>
+    <measure number="15" width="201.38">
+      <note default-x="12.00" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="50.15" default-y="-50.00">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="69.70" default-y="-45.00">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="89.25" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>160</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>3</actual-notes>
+          <normal-notes>2</normal-notes>
+          </time-modification>
+        <stem>up</stem>
+
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="108.81" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        </note>
+      <note>
+        <rest/>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        </note>
+      <note>
+        <rest/>
+        <duration>480</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        </note>
+      </measure>
+    <measure number="16" width="597.55">
+      <print new-system="yes">
+        <system-layout>
+          <system-margins>
+            <left-margin>-0.00</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <system-distance>150.00</system-distance>
+          </system-layout>
+        </print>
+      <note default-x="49.08" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>192</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="101.55" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>192</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="154.02" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>192</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="206.49" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>192</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="258.96" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>192</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>5</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <note default-x="311.44" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>137</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>7</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        <notations>
+          <tuplet type="start" bracket="no"/>
+          </notations>
+        </note>
+      <note default-x="352.04" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>137</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>7</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="392.65" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>137</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>7</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="433.26" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>137</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>7</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="473.87" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>137</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>7</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="514.48" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>137</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>sharp</accidental>
+        <time-modification>
+          <actual-notes>7</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="555.09" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>137</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <time-modification>
+          <actual-notes>7</actual-notes>
+          <normal-notes>4</normal-notes>
+          </time-modification>
+        <stem>down</stem>
+
+        <notations>
+          <tuplet type="stop"/>
+          </notations>
+        </note>
+      <forward>
+        <duration>1</duration>
+        </forward>
+      </measure>
+    <measure number="17" width="513.06">
+      <note default-x="12.00" default-y="-50.00">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="73.31" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="134.62" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <alter>-1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <accidental>flat</accidental>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="195.92" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+
+        </note>
+      <note default-x="257.23" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="318.54" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="379.85" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <note default-x="441.16" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>240</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 40 - 12
test/data/OSMD_function_test_expressions.musicxml

@@ -7,7 +7,7 @@
   <identification>
     <encoding>
       <software>MuseScore 2.3.2</software>
-      <encoding-date>2018-08-22</encoding-date>
+      <encoding-date>2018-09-27</encoding-date>
       <supports element="accidental" type="yes"/>
       <supports element="beam" type="yes"/>
       <supports element="print" attribute="new-page" type="yes" value="yes"/>
@@ -63,7 +63,7 @@
       <print>
         <system-layout>
           <system-margins>
-            <left-margin>-0.00</left-margin>
+            <left-margin>0.00</left-margin>
             <right-margin>0.00</right-margin>
             </system-margins>
           <top-system-distance>170.00</top-system-distance>
@@ -93,7 +93,12 @@
         </direction>
       <direction placement="above">
         <direction-type>
-          <words default-y="40.00" relative-x="-12.75" relative-y="6.30" font-weight="bold">Adagio</words>
+          <words default-y="40.00" relative-x="-59.61" relative-y="12.55" font-weight="bold">Adagio</words>
+          </direction-type>
+        </direction>
+      <direction placement="above">
+        <direction-type>
+          <words default-y="40.00">Notenzeilentext</words>
           </direction-type>
         </direction>
       <direction placement="above">
@@ -216,7 +221,7 @@
     <measure number="3" width="199.74">
       <direction placement="above">
         <direction-type>
-          <dynamics default-x="6.58" default-y="-80.00" relative-x="-6.11" relative-y="105.93">
+          <dynamics default-x="6.58" default-y="-80.00" relative-y="96.00">
             <mf/>
             </dynamics>
           </direction-type>
@@ -276,7 +281,7 @@
     <measure number="4" width="199.74">
       <direction placement="above">
         <direction-type>
-          <dynamics default-x="3.29" default-y="-80.00" relative-x="1.02" relative-y="102.87">
+          <dynamics default-x="3.29" default-y="-80.00" relative-y="96.00">
             <fz/>
             </dynamics>
           </direction-type>
@@ -340,7 +345,7 @@
         </direction>
       <direction placement="below">
         <direction-type>
-          <wedge type="crescendo" number="1" default-y="-65.00"/>
+          <wedge type="crescendo" number="1" default-y="-93.24"/>
           </direction-type>
         </direction>
       <note default-x="12.00" default-y="-50.00">
@@ -393,13 +398,13 @@
       <print new-system="yes">
         <system-layout>
           <system-margins>
-            <left-margin>-0.00</left-margin>
-            <right-margin>0.00</right-margin>
+            <left-margin>0.00</left-margin>
+            <right-margin>-0.00</right-margin>
             </system-margins>
           <system-distance>150.00</system-distance>
           </system-layout>
         </print>
-      <note default-x="49.08" default-y="-50.00">
+      <note default-x="49.07" default-y="-50.00">
         <pitch>
           <step>C</step>
           <octave>6</octave>
@@ -429,6 +434,19 @@
         <type>quarter</type>
         <stem>up</stem>
         </note>
+      <direction placement="below">
+        <direction-type>
+          <dynamics default-x="3.29" default-y="-80.00" relative-x="1.57" relative-y="-18.82">
+            <p/>
+            </dynamics>
+          </direction-type>
+        <sound dynamics="54.44"/>
+        </direction>
+      <direction placement="below">
+        <direction-type>
+          <wedge type="crescendo" number="1" default-y="-93.82" relative-x="25.10"/>
+          </direction-type>
+        </direction>
       <note default-x="205.86" default-y="-45.00">
         <pitch>
           <step>D</step>
@@ -514,7 +532,12 @@
         </note>
       <direction placement="below">
         <direction-type>
-          <dynamics default-x="6.58" default-y="-80.00" relative-y="6.00">
+          <wedge type="stop" number="1" relative-x="-9.70"/>
+          </direction-type>
+        </direction>
+      <direction placement="below">
+        <direction-type>
+          <dynamics default-x="6.58" default-y="-80.00" relative-x="3.14" relative-y="-20.67">
             <ff/>
             </dynamics>
           </direction-type>
@@ -556,7 +579,7 @@
     <measure number="8" width="171.37">
       <direction placement="above">
         <direction-type>
-          <dynamics default-x="6.58" default-y="-80.00" relative-x="-2.04" relative-y="105.93">
+          <dynamics default-x="6.58" default-y="-80.00" relative-y="96.00">
             <sfz/>
             </dynamics>
           </direction-type>
@@ -574,7 +597,7 @@
     <measure number="9" width="171.37">
       <direction placement="above">
         <direction-type>
-          <words default-y="40.00" relative-x="2.46" relative-y="-16.02" font-weight="bold">Presto</words>
+          <words default-y="40.00" font-weight="bold">Presto</words>
           </direction-type>
         </direction>
       <note default-x="12.00" default-y="-15.00">
@@ -591,6 +614,11 @@
       <barline location="left">
         <ending number="1" type="start" default-y="30.00"/>
         </barline>
+      <direction placement="below">
+        <direction-type>
+          <words default-y="40.00" relative-x="1.57" relative-y="-111.38">crescendo</words>
+          </direction-type>
+        </direction>
       <note default-x="12.00" default-y="-30.00">
         <pitch>
           <step>G</step>

+ 365 - 0
test/data/OSMD_function_test_expressions_overlap.musicxml

@@ -0,0 +1,365 @@
+<?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>Dynamic Expressions Overlap</work-title>
+    </work>
+    <identification>
+        <encoding>
+            <software>MuseScore 2.3.2</software>
+            <encoding-date>2018-10-13</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.05556</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1683.36</page-height>
+            <page-width>1190.88</page-width>
+            <page-margins type="even">
+                <left-margin>56.6929</left-margin>
+                <right-margin>56.6929</right-margin>
+                <top-margin>56.6929</top-margin>
+                <bottom-margin>113.386</bottom-margin>
+            </page-margins>
+            <page-margins type="odd">
+                <left-margin>56.6929</left-margin>
+                <right-margin>56.6929</right-margin>
+                <top-margin>56.6929</top-margin>
+                <bottom-margin>113.386</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <word-font font-family="FreeSerif" font-size="10"/>
+        <lyric-font font-family="FreeSerif" font-size="11"/>
+    </defaults>
+    <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="457.55">
+            <print>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>0.00</left-margin>
+                        <right-margin>-0.00</right-margin>
+                    </system-margins>
+                    <top-system-distance>70.00</top-system-distance>
+                </system-layout>
+            </print>
+            <attributes>
+                <divisions>2</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>
+                    <dynamics default-x="6.58" default-y="-80.00">
+                        <ppp/>
+                    </dynamics>
+                </direction-type>
+                <sound dynamics="17.78"/>
+            </direction>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="crescendo" number="1" default-y="-75.00"/>
+                </direction-type>
+            </direction>
+            <note default-x="75.17" default-y="-40.00">
+                <pitch>
+                    <step>E</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>up</stem>
+                <beam number="1">begin</beam>
+            </note>
+            <note default-x="75.17" default-y="-25.00">
+                <chord/>
+                <pitch>
+                    <step>A</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>up</stem>
+            </note>
+            <note default-x="122.77" default-y="-20.00">
+                <pitch>
+                    <step>B</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>up</stem>
+                <beam number="1">continue</beam>
+            </note>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="stop" number="1"/>
+                </direction-type>
+            </direction>
+            <direction placement="below">
+                <direction-type>
+                    <dynamics default-x="6.58" default-y="-80.00" relative-x="-1.37" relative-y="-0.38">
+                        <ff/>
+                    </dynamics>
+                </direction-type>
+                <sound dynamics="124.44"/>
+            </direction>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="crescendo" number="1" default-y="-75.00"/>
+                </direction-type>
+            </direction>
+            <note default-x="170.37" default-y="-20.00">
+                <pitch>
+                    <step>B</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>up</stem>
+                <beam number="1">continue</beam>
+            </note>
+            <note default-x="217.97" default-y="-15.00">
+                <pitch>
+                    <step>C</step>
+                    <octave>5</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>up</stem>
+                <beam number="1">end</beam>
+            </note>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="stop" number="1"/>
+                </direction-type>
+            </direction>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="diminuendo" number="1" default-y="-75.00"/>
+                </direction-type>
+            </direction>
+            <note default-x="265.56" default-y="-15.00">
+                <pitch>
+                    <step>C</step>
+                    <octave>5</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>down</stem>
+                <beam number="1">begin</beam>
+            </note>
+            <note default-x="313.16" default-y="-25.00">
+                <pitch>
+                    <step>A</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>down</stem>
+                <beam number="1">continue</beam>
+            </note>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="stop" number="1"/>
+                </direction-type>
+            </direction>
+            <note default-x="360.76" default-y="-40.00">
+                <pitch>
+                    <step>E</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>down</stem>
+                <beam number="1">continue</beam>
+            </note>
+            <note default-x="360.76" default-y="5.00">
+                <chord/>
+                <pitch>
+                    <step>G</step>
+                    <octave>5</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>down</stem>
+            </note>
+            <note default-x="408.35" default-y="-20.00">
+                <pitch>
+                    <step>B</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>down</stem>
+                <beam number="1">end</beam>
+            </note>
+            <note default-x="396.49" default-y="0.00">
+                <chord/>
+                <pitch>
+                    <step>F</step>
+                    <octave>5</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>down</stem>
+            </note>
+            <note default-x="408.35" default-y="5.00">
+                <chord/>
+                <pitch>
+                    <step>G</step>
+                    <octave>5</octave>
+                </pitch>
+                <duration>1</duration>
+                <voice>1</voice>
+                <type>eighth</type>
+                <stem>down</stem>
+            </note>
+        </measure>
+        <measure number="2" width="298.00">
+            <direction placement="below">
+                <direction-type>
+                    <dynamics default-x="6.58" default-y="-80.00" relative-x="-3.14" relative-y="-1.57">
+                        <pp/>
+                    </dynamics>
+                </direction-type>
+                <sound dynamics="36.67"/>
+            </direction>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="crescendo" number="1" default-y="-78.14" relative-x="5.81"/>
+                </direction-type>
+            </direction>
+            <note default-x="12.00" default-y="-30.00">
+                <pitch>
+                    <step>G</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>4</duration>
+                <voice>1</voice>
+                <type>half</type>
+                <stem>up</stem>
+            </note>
+            <direction placement="below">
+                <direction-type>
+                    <wedge type="stop" number="1" relative-x="1.57"/>
+                </direction-type>
+            </direction>
+            <direction placement="below">
+                <direction-type>
+                    <dynamics default-x="3.29" default-y="-80.00" relative-x="-3.14" relative-y="-1.57">
+                        <other-dynamics>sff</other-dynamics>
+                    </dynamics>
+                </direction-type>
+            </direction>
+            <note default-x="154.02" default-y="-5.00">
+                <pitch>
+                    <step>E</step>
+                    <octave>5</octave>
+                </pitch>
+                <duration>4</duration>
+                <voice>1</voice>
+                <type>half</type>
+                <stem>down</stem>
+            </note>
+        </measure>
+        <measure number="3" width="321.94">
+            <direction placement="below">
+                <direction-type>
+                    <words default-y="-80.00" relative-x="64.32" font-style="italic" font-size="12">crescendo</words>
+                </direction-type>
+            </direction>
+            <direction placement="below">
+                <direction-type>
+                    <dynamics default-x="6.58" default-y="-80.00">
+                        <pp/>
+                    </dynamics>
+                </direction-type>
+                <sound dynamics="36.67"/>
+            </direction>
+            <note default-x="12.00" default-y="-35.00">
+                <pitch>
+                    <step>F</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>4</duration>
+                <voice>1</voice>
+                <type>half</type>
+                <stem>up</stem>
+            </note>
+            <direction placement="below">
+                <direction-type>
+                    <dynamics default-x="6.58" default-y="-80.00" relative-x="-10.28" relative-y="-0.38">
+                        <sffz/>
+                    </dynamics>
+                </direction-type>
+            </direction>
+            <note default-x="145.24" default-y="-35.00">
+                <pitch>
+                    <step>F</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>up</stem>
+            </note>
+            <note default-x="228.29" default-y="-15.00">
+                <pitch>
+                    <step>C</step>
+                    <octave>5</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>down</stem>
+            </note>
+            <barline location="right">
+                <bar-style>light-heavy</bar-style>
+            </barline>
+        </measure>
+    </part>
+</score-partwise>

+ 115 - 59
test/data/OSMD_function_test_noteHeadShapes.musicxml

@@ -7,7 +7,7 @@
   <identification>
     <encoding>
       <software>MuseScore 2.3.2</software>
-      <encoding-date>2018-08-28</encoding-date>
+      <encoding-date>2018-10-11</encoding-date>
       <supports element="accidental" type="yes"/>
       <supports element="beam" type="yes"/>
       <supports element="print" attribute="new-page" type="yes" value="yes"/>
@@ -63,7 +63,7 @@
       <print>
         <system-layout>
           <system-margins>
-            <left-margin>-0.00</left-margin>
+            <left-margin>0.00</left-margin>
             <right-margin>0.00</right-margin>
             </system-margins>
           <top-system-distance>170.00</top-system-distance>
@@ -331,13 +331,13 @@
       <print new-system="yes">
         <system-layout>
           <system-margins>
-            <left-margin>-0.00</left-margin>
+            <left-margin>0.00</left-margin>
             <right-margin>0.00</right-margin>
             </system-margins>
           <system-distance>150.00</system-distance>
           </system-layout>
         </print>
-      <note default-x="49.08" default-y="-30.00">
+      <note default-x="49.07" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -424,55 +424,17 @@
         <type>quarter</type>
         </note>
       </measure>
-    <measure number="12" width="1110.61">
+    <measure number="12" width="372.16">
       <print new-system="yes">
         <system-layout>
           <system-margins>
-            <left-margin>-0.00</left-margin>
+            <left-margin>0.00</left-margin>
             <right-margin>0.00</right-margin>
             </system-margins>
           <system-distance>150.00</system-distance>
           </system-layout>
         </print>
-      <note>
-        <rest/>
-        <duration>16</duration>
-        <voice>1</voice>
-        </note>
-      </measure>
-    <measure number="13" width="229.88">
-      <print new-system="yes">
-        <system-layout>
-          <system-margins>
-            <left-margin>-0.00</left-margin>
-            <right-margin>0.00</right-margin>
-            </system-margins>
-          <system-distance>150.00</system-distance>
-          </system-layout>
-        </print>
-      <note default-x="105.50" default-y="-15.00">
-        <pitch>
-          <step>C</step>
-          <octave>5</octave>
-          </pitch>
-        <duration>16</duration>
-        <voice>1</voice>
-        <type>whole</type>
-        <lyric number="1" default-x="11.65" default-y="-80.00">
-          <syllabic>single</syllabic>
-          <text>NotInVexFlow:</text>
-          </lyric>
-        </note>
-      </measure>
-    <measure number="14" width="80.05">
-      <note>
-        <rest/>
-        <duration>16</duration>
-        <voice>1</voice>
-        </note>
-      </measure>
-    <measure number="15" width="146.47">
-      <note default-x="49.95" default-y="-30.00">
+      <note default-x="87.03" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -487,7 +449,7 @@
           </lyric>
         </note>
       </measure>
-    <measure number="16" width="183.27">
+    <measure number="13" width="377.89">
       <note default-x="33.79" default-y="-30.00">
         <pitch>
           <step>G</step>
@@ -497,13 +459,13 @@
         <voice>1</voice>
         <type>quarter</type>
         <stem>up</stem>
-        <notehead>la</notehead>
+        <notehead>square</notehead>
         <lyric number="1" default-x="6.58" default-y="-80.00">
           <syllabic>single</syllabic>
           <text>square</text>
           </lyric>
         </note>
-      <note default-x="71.24" default-y="-30.00">
+      <note default-x="121.40" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -513,6 +475,10 @@
         <type>16th</type>
         <stem>up</stem>
         <notehead>la</notehead>
+        <lyric number="1" default-x="6.58" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>La</text>
+          </lyric>
         </note>
       <note>
         <rest/>
@@ -526,7 +492,15 @@
         <voice>1</voice>
         <type>eighth</type>
         </note>
-      <note default-x="137.67" default-y="-30.00">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      </measure>
+    <measure number="14" width="360.57">
+      <note default-x="48.28" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -535,11 +509,56 @@
         <voice>1</voice>
         <type>half</type>
         <stem>up</stem>
-        <notehead>la</notehead>
+        <notehead>rectangle</notehead>
+        <lyric number="1" default-x="6.21" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>Rectangle</text>
+          </lyric>
+        </note>
+      <note default-x="186.57" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <notehead>rectangle</notehead>
+        </note>
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>quarter</type>
         </note>
       </measure>
-    <measure number="17" width="129.19">
-      <note default-x="12.00" default-y="-30.00">
+    <measure number="15" width="247.84">
+      <print new-system="yes">
+        <system-layout>
+          <system-margins>
+            <left-margin>0.00</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <system-distance>150.00</system-distance>
+          </system-layout>
+        </print>
+      <note default-x="105.50" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>16</duration>
+        <voice>1</voice>
+        <type>whole</type>
+        <lyric number="1" default-x="11.65" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>NotInVexFlow:</text>
+          </lyric>
+        </note>
+      </measure>
+    <measure number="16" width="248.46">
+      <note default-x="36.61" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -550,10 +569,10 @@
         <stem>up</stem>
         <lyric number="1" default-x="6.22" default-y="-80.00">
           <syllabic>single</syllabic>
-          <text>/</text>
+          <text>slashed</text>
           </lyric>
         </note>
-      <note default-x="69.62" default-y="-30.00">
+      <note default-x="141.55" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -562,13 +581,13 @@
         <voice>1</voice>
         <type>half</type>
         <stem>up</stem>
-        <lyric number="1" default-x="8.48" default-y="-80.00">
+        <lyric number="1" default-x="6.22" default-y="-80.00">
           <syllabic>single</syllabic>
-          <text>back/</text>
+          <text>backSlashed</text>
           </lyric>
         </note>
       </measure>
-    <measure number="18" width="115.13">
+    <measure number="17" width="133.09">
       <note default-x="26.11" default-y="-30.00">
         <pitch>
           <step>G</step>
@@ -583,7 +602,7 @@
           <text>none</text>
           </lyric>
         </note>
-      <note default-x="69.64" default-y="-30.00">
+      <note default-x="78.62" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -594,7 +613,7 @@
         <stem>up</stem>
         </note>
       </measure>
-    <measure number="19" width="226.61">
+    <measure number="18" width="235.57">
       <note default-x="77.35" default-y="-30.00">
         <pitch>
           <step>G</step>
@@ -619,6 +638,43 @@
         <type>half</type>
         <stem>up</stem>
         </note>
+      </measure>
+    <measure number="19" width="134.20">
+      <note default-x="26.11" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <lyric number="1" default-x="6.22" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>none</text>
+          </lyric>
+        </note>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      </measure>
+    <measure number="20" width="111.45">
+      <note default-x="14.97" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>16</duration>
+        <voice>1</voice>
+        <type>whole</type>
+        <lyric number="1" default-x="3.26" default-y="-80.00">
+          <syllabic>single</syllabic>
+          <text>etc</text>
+          </lyric>
+        </note>
       <barline location="right">
         <bar-style>light-heavy</bar-style>
         </barline>

+ 3 - 4
webpack.sourcemap.js

@@ -1,8 +1,7 @@
 var merge = require('webpack-merge')
-var common = require('./webpack.common.js')
+var production = require('./webpack.prod.js')
 
 // will create a build plus separate .min.js.map source map for debugging
-module.exports = merge(common, {
-    devtool: 'source-map',
-    mode: 'development'
+module.exports = merge(production, {
+    devtool: 'source-map'
 })