Browse Source

Merge branch 'develop'

sschmid 4 years ago
parent
commit
e078712d47
68 changed files with 4695 additions and 1933 deletions
  1. 1 0
      .eslintignore
  2. 5 0
      demo/index.js
  3. 3 3
      src/Common/DataObjects/Pitch.ts
  4. 1 1
      src/MusicalScore/Graphical/AbstractGraphicalExpression.ts
  5. 2 1
      src/MusicalScore/Graphical/AccidentalCalculator.ts
  6. 4 3
      src/MusicalScore/Graphical/DrawingEnums.ts
  7. 439 1727
      src/MusicalScore/Graphical/EngravingRules.ts
  8. 1 1
      src/MusicalScore/Graphical/GraphicalChordSymbolContainer.ts
  9. 1 1
      src/MusicalScore/Graphical/GraphicalContinuousDynamicExpression.ts
  10. 4 1
      src/MusicalScore/Graphical/GraphicalLabel.ts
  11. 5 0
      src/MusicalScore/Graphical/GraphicalMeasure.ts
  12. 13 2
      src/MusicalScore/Graphical/GraphicalMusicSheet.ts
  13. 1 1
      src/MusicalScore/Graphical/GraphicalStaffEntry.ts
  14. 112 24
      src/MusicalScore/Graphical/MusicSheetCalculator.ts
  15. 3 4
      src/MusicalScore/Graphical/MusicSystem.ts
  16. 7 4
      src/MusicalScore/Graphical/MusicSystemBuilder.ts
  17. 1 1
      src/MusicalScore/Graphical/VexFlow/AlignmentManager.ts
  18. 9 1
      src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts
  19. 7 2
      src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts
  20. 11 4
      src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts
  21. 80 13
      src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
  22. 4 4
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts
  23. 10 3
      src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts
  24. 42 12
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts
  25. 4 1
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts
  26. 21 14
      src/MusicalScore/Graphical/VexFlow/VexFlowOctaveShift.ts
  27. 2 3
      src/MusicalScore/Graphical/VexFlow/VexFlowVoiceEntry.ts
  28. 3 2
      src/MusicalScore/Graphical/VexFlow/VexflowStafflineNoteCalculator.ts
  29. 1 1
      src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts
  30. 6 4
      src/MusicalScore/ScoreIO/InstrumentReader.ts
  31. 3 2
      src/MusicalScore/ScoreIO/MusicSheetReader.ts
  32. 7 0
      src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts
  33. 18 5
      src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts
  34. 27 15
      src/MusicalScore/ScoreIO/VoiceGenerator.ts
  35. 1 1
      src/MusicalScore/VoiceData/ChordSymbolContainer.ts
  36. 2 0
      src/MusicalScore/VoiceData/Expressions/MoodExpression.ts
  37. 2 1
      src/MusicalScore/VoiceData/Expressions/MultiExpression.ts
  38. 4 2
      src/MusicalScore/VoiceData/Expressions/UnknownExpression.ts
  39. 15 5
      src/MusicalScore/VoiceData/Note.ts
  40. 3 1
      src/MusicalScore/VoiceData/OrnamentContainer.ts
  41. 36 2
      src/MusicalScore/VoiceData/SourceMeasure.ts
  42. 3 2
      src/MusicalScore/VoiceData/TabNote.ts
  43. 4 2
      src/MusicalScore/VoiceData/Tuplet.ts
  44. 16 15
      src/MusicalScore/VoiceData/VoiceEntry.ts
  45. 61 22
      src/OpenSheetMusicDisplay/Cursor.ts
  46. 15 2
      src/OpenSheetMusicDisplay/OSMDOptions.ts
  47. 25 7
      src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts
  48. 1 1
      src/Util/CollectionUtil.ts
  49. 9 0
      src/VexFlowPatch/readme.txt
  50. 69 0
      src/VexFlowPatch/stavevolta.js
  51. 491 0
      src/VexFlowPatch/tabnote.js
  52. 4 4
      test/Common/DataObjects/Pitch_Test.ts
  53. 2 1
      test/Common/OSMD/OSMD_Test.ts
  54. 1 1
      test/MusicalScore/VoiceData/NoteType_Test.ts
  55. 0 1
      test/Util/visual_regression.sh
  56. 377 0
      test/data/OSMD_Function_Test_Tablature_Alleffects.musicxml
  57. 233 0
      test/data/OSMD_Function_Test_Tablature_Bends.musicxml
  58. 224 0
      test/data/OSMD_Function_Test_Tablature_Hammeron_Pulloff.musicxml
  59. 228 0
      test/data/OSMD_Function_Test_Tablature_Multibends.musicxml
  60. 189 0
      test/data/OSMD_Function_Test_Tablature_Slides.musicxml
  61. 193 0
      test/data/OSMD_Function_Test_Tablature_Vibrato.musicxml
  62. 18 0
      test/data/OSMD_function_test_all.xml
  63. BIN
      test/data/OSMD_function_test_metronome_marks.mxl
  64. 2 2
      test/data/Slurtest_highNotes.musicxml
  65. 154 0
      test/data/Test_Auto_Multirest_1.musicxml
  66. 999 0
      test/data/Test_Auto_Multirest_2.musicxml
  67. 455 0
      test/data/Tuplet_placement_test.xml
  68. 1 1
      test/data/tabs_bend_and_release.musicxml

+ 1 - 0
.eslintignore

@@ -3,3 +3,4 @@ dist
 build
 build
 bin
 bin
 demo
 demo
+src/VexFlowPatch

+ 5 - 0
demo/index.js

@@ -36,6 +36,7 @@ import * as svg2pdf from '../node_modules/svg2pdf.js/dist/svg2pdf.min';
             "OSMD Function Test - Expressions": "OSMD_function_test_expressions.musicxml",
             "OSMD Function Test - Expressions": "OSMD_function_test_expressions.musicxml",
             "OSMD Function Test - Expressions Overlap": "OSMD_function_test_expressions_overlap.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 - Grace Notes": "OSMD_function_test_GraceNotes.xml",
+            "OSMD Function Test - Metronome Marks": "OSMD_function_test_metronome_marks.mxl",
             "OSMD Function Test - Multiple Rest Measures": "OSMD_function_test_multiple_rest_measures.musicxml",
             "OSMD Function Test - Multiple Rest Measures": "OSMD_function_test_multiple_rest_measures.musicxml",
             "OSMD Function Test - Invisible Notes": "OSMD_function_test_invisible_notes.musicxml",
             "OSMD Function Test - Invisible Notes": "OSMD_function_test_invisible_notes.musicxml",
             "OSMD Function Test - Notehead Shapes": "OSMD_function_test_noteheadShapes.musicxml",
             "OSMD Function Test - Notehead Shapes": "OSMD_function_test_noteheadShapes.musicxml",
@@ -43,9 +44,13 @@ import * as svg2pdf from '../node_modules/svg2pdf.js/dist/svg2pdf.min';
             "OSMD Function Test - Selecting Measures To Draw": "OSMD_function_test_measuresToDraw_Beethoven_AnDieFerneGeliebte.xml",
             "OSMD Function Test - Selecting Measures To Draw": "OSMD_function_test_measuresToDraw_Beethoven_AnDieFerneGeliebte.xml",
             "OSMD Function Test - System and Page Breaks": "OSMD_Function_Test_System_and_Page_Breaks_4_pages.mxl",
             "OSMD Function Test - System and Page Breaks": "OSMD_Function_Test_System_and_Page_Breaks_4_pages.mxl",
             "OSMD Function Test - Tabulature": "OSMD_Function_Test_Tabulature_hayden_study_1.mxl",
             "OSMD Function Test - Tabulature": "OSMD_Function_Test_Tabulature_hayden_study_1.mxl",
+            "OSMD Function Test - Tabulature MultiBends": "OSMD_Function_Test_Tablature_Multibends.musicxml",
+            "OSMD Function Test - Tabulature All Effects": "OSMD_Function_Test_Tablature_Alleffects.musicxml",
             "OSMD Function Test - Tremolo": "OSMD_Function_Test_Tremolo_2bars.musicxml",
             "OSMD Function Test - Tremolo": "OSMD_Function_Test_Tremolo_2bars.musicxml",
             "OSMD Function Test - Labels": "OSMD_Function_Test_Labels.musicxml",
             "OSMD Function Test - Labels": "OSMD_Function_Test_Labels.musicxml",
             "OSMD Function Test - High Slur Test": "Slurtest_highNotes.musicxml",
             "OSMD Function Test - High Slur Test": "Slurtest_highNotes.musicxml",
+            "OSMD Function Test - Auto Multirest Measures Single Staff": "Test_Auto_Multirest_1.musicxml",
+            "OSMD Function Test - Auto Multirest Measures Multiple Staves": "Test_Auto_Multirest_2.musicxml",
             "Schubert, F. - An Die Musik": "Schubert_An_die_Musik.xml",
             "Schubert, F. - An Die Musik": "Schubert_An_die_Musik.xml",
             "Actor, L. - Prelude (Large Sample, loading time)": "ActorPreludeSample.xml",
             "Actor, L. - Prelude (Large Sample, loading time)": "ActorPreludeSample.xml",
             "Actor, L. - Prelude (Large, No Print Part Names)": "ActorPreludeSample_PartName.xml",
             "Actor, L. - Prelude (Large, No Print Part Names)": "ActorPreludeSample_PartName.xml",

+ 3 - 3
src/Common/DataObjects/Pitch.ts

@@ -73,12 +73,12 @@ export class Pitch {
      *          ret[1] = the octave shift (not the new octave!)
      *          ret[1] = the octave shift (not the new octave!)
      * @constructor
      * @constructor
      */
      */
-    public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): { value: number; overflow: number; } {
+    public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): { halftone: number; overflow: number; } {
         const newHalfTone: number = <number>pitch.fundamentalNote + pitch.AccidentalHalfTones + transpose;
         const newHalfTone: number = <number>pitch.fundamentalNote + pitch.AccidentalHalfTones + transpose;
         return Pitch.WrapAroundCheck(newHalfTone, 12);
         return Pitch.WrapAroundCheck(newHalfTone, 12);
     }
     }
 
 
-    public static WrapAroundCheck(value: number, limit: number): { value: number; overflow: number; } {
+    public static WrapAroundCheck(value: number, limit: number): { halftone: number; overflow: number; } {
         let overflow: number = 0;
         let overflow: number = 0;
 
 
         while (value < 0) {
         while (value < 0) {
@@ -89,7 +89,7 @@ export class Pitch {
             value -= limit;
             value -= limit;
             overflow++; // the octave change
             overflow++; // the octave change
         }
         }
-        return {overflow: overflow, value: value};
+        return {overflow: overflow, halftone: value};
     }
     }
 
 
     //public static calcFrequency(pitch: Pitch): number;
     //public static calcFrequency(pitch: Pitch): number;

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

@@ -4,7 +4,7 @@ import { StaffLine } from "./StaffLine";
 import { BoundingBox } from "./BoundingBox";
 import { BoundingBox } from "./BoundingBox";
 import { AbstractExpression, PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 import { AbstractExpression, PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 import { EngravingRules } from "./EngravingRules";
 import { EngravingRules } from "./EngravingRules";
-import { SourceMeasure } from "../VoiceData";
+import { SourceMeasure } from "../VoiceData/SourceMeasure";
 
 
 export abstract class AbstractGraphicalExpression extends GraphicalObject {
 export abstract class AbstractGraphicalExpression extends GraphicalObject {
     protected label: GraphicalLabel;
     protected label: GraphicalLabel;

+ 2 - 1
src/MusicalScore/Graphical/AccidentalCalculator.ts

@@ -3,7 +3,8 @@ import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
 import {GraphicalNote} from "./GraphicalNote";
 import {GraphicalNote} from "./GraphicalNote";
 import {Pitch} from "../../Common/DataObjects/Pitch";
 import {Pitch} from "../../Common/DataObjects/Pitch";
 import {NoteEnum} from "../../Common/DataObjects/Pitch";
 import {NoteEnum} from "../../Common/DataObjects/Pitch";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import { Dictionary } from "typescript-collections";
+// import { Dictionary } from "typescript-collections/dist/lib";
 import { MusicSheetCalculator } from "./MusicSheetCalculator";
 import { MusicSheetCalculator } from "./MusicSheetCalculator";
 
 
 /**
 /**

+ 4 - 3
src/MusicalScore/Graphical/DrawingEnums.ts

@@ -1,5 +1,6 @@
 // import * as Collections from "typescript-collections";
 // import * as Collections from "typescript-collections";
-import Collections = require("typescript-collections");
+// import Collections = require("typescript-collections");
+import { Dictionary } from "typescript-collections";
 
 
 /**
 /**
  * The supported styles to draw a rectangle on the music sheet
  * The supported styles to draw a rectangle on the music sheet
@@ -41,8 +42,8 @@ export enum OutlineAndFillStyleEnum {
 }
 }
 
 
 // tslint:disable-next-line:max-line-length A linebreak would be more confusing here
 // tslint:disable-next-line:max-line-length A linebreak would be more confusing here
-export const OUTLINE_AND_FILL_STYLE_DICT: Collections.Dictionary<OutlineAndFillStyleEnum, string> =
-    new Collections.Dictionary<OutlineAndFillStyleEnum, string>();
+export const OUTLINE_AND_FILL_STYLE_DICT: Dictionary<OutlineAndFillStyleEnum, string> =
+    new Dictionary<OutlineAndFillStyleEnum, string>();
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.BaseWritingColor, "Thistle");
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.BaseWritingColor, "Thistle");
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.FollowingCursor, "Aqua");
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.FollowingCursor, "Aqua");
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.AlternativeFollowingCursor, "Azure");
 OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.AlternativeFollowingCursor, "Azure");

File diff suppressed because it is too large
+ 439 - 1727
src/MusicalScore/Graphical/EngravingRules.ts


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

@@ -18,8 +18,8 @@ export class GraphicalChordSymbolContainer extends GraphicalObject {
         super();
         super();
         this.chordSymbolContainer = chordSymbolContainer;
         this.chordSymbolContainer = chordSymbolContainer;
         this.boundingBox = new BoundingBox(this, parent);
         this.boundingBox = new BoundingBox(this, parent);
-        this.calculateLabel(textHeight, transposeHalftones, keyInstruction);
         this.rules = rules;
         this.rules = rules;
+        this.calculateLabel(textHeight, transposeHalftones, keyInstruction);
     }
     }
     public get GetChordSymbolContainer(): ChordSymbolContainer {
     public get GetChordSymbolContainer(): ChordSymbolContainer {
         return this.chordSymbolContainer;
         return this.chordSymbolContainer;

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

@@ -8,7 +8,7 @@ import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
 import { ISqueezable } from "./ISqueezable";
 import { ISqueezable } from "./ISqueezable";
 import log from "loglevel";
 import log from "loglevel";
-import { SourceMeasure } from "../VoiceData";
+import { SourceMeasure } from "../VoiceData/SourceMeasure";
 
 
 /**
 /**
  * This class prepares the graphical elements for a continuous expression. It calculates the wedges and
  * This class prepares the graphical elements for a continuous expression. It calculates the wedges and

+ 4 - 1
src/MusicalScore/Graphical/GraphicalLabel.ts

@@ -83,7 +83,10 @@ export class GraphicalLabel extends Clickable {
             line.xOffset = xOffset;
             line.xOffset = xOffset;
         }
         }
 
 
-        const height: number = this.Label.fontHeight * numOfLines;
+        let height: number = this.Label.fontHeight * numOfLines;
+        if (this.rules.SpacingBetweenTextLines > 0 && this.TextLines.length > 1) {
+            height += (this.rules.SpacingBetweenTextLines * numOfLines) / 10;
+        }
         const bbox: BoundingBox = this.PositionAndShape;
         const bbox: BoundingBox = this.PositionAndShape;
 
 
         switch (this.Label.textAlignment) {
         switch (this.Label.textAlignment) {

+ 5 - 0
src/MusicalScore/Graphical/GraphicalMeasure.ts

@@ -59,6 +59,11 @@ export abstract class GraphicalMeasure extends GraphicalObject {
      */
      */
     public endInstructionsWidth: number;
     public endInstructionsWidth: number;
     public hasError: boolean;
     public hasError: boolean;
+    /**
+     * Whether or not this measure is nothing but rest(s).
+     * Also see SourceMeasure.allRests, which is not the same, because a source measure can have multiple staffs/graphicalMeasures.
+     */
+    public hasOnlyRests: boolean = false;
 
 
     private parentStaff: Staff;
     private parentStaff: Staff;
     private parentMusicSystem: MusicSystem;
     private parentMusicSystem: MusicSystem;

+ 13 - 2
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -18,7 +18,7 @@ import {Instrument} from "../Instrument";
 import {BoundingBox} from "./BoundingBox";
 import {BoundingBox} from "./BoundingBox";
 import {MusicSheetCalculator} from "./MusicSheetCalculator";
 import {MusicSheetCalculator} from "./MusicSheetCalculator";
 import log from "loglevel";
 import log from "loglevel";
-//import Dictionary from "typescript-collections/dist/lib/Dictionary"; // unused for now
+//import { Dictionary } from "typescript-collections"; // unused for now
 import {CollectionUtil} from "../../Util/CollectionUtil";
 import {CollectionUtil} from "../../Util/CollectionUtil";
 import {SelectionStartSymbol} from "./SelectionStartSymbol";
 import {SelectionStartSymbol} from "./SelectionStartSymbol";
 import {SelectionEndSymbol} from "./SelectionEndSymbol";
 import {SelectionEndSymbol} from "./SelectionEndSymbol";
@@ -202,6 +202,17 @@ export class GraphicalMusicSheet {
         return undefined;
         return undefined;
     }
     }
 
 
+    public findGraphicalMeasure(measureIndex: number, staffIndex: number): GraphicalMeasure {
+        for (let i: number = measureIndex; i >= 0; i--) {
+            const gMeasure: GraphicalMeasure = this.measureList[i][staffIndex];
+            if (gMeasure) {
+                return gMeasure;
+            }
+            // else look backwards (previous measures). this is only really valid for MultipleRestMeasures of course.
+        }
+        return undefined; // shouldn't happen
+    }
+
     /**
     /**
      * Search the MeasureList for a certain GraphicalStaffEntry with the given SourceStaffEntry,
      * Search the MeasureList for a certain GraphicalStaffEntry with the given SourceStaffEntry,
      * at a certain verticalIndex (eg a corresponding Staff), starting at a specific horizontalIndex (eg specific GraphicalMeasure).
      * at a certain verticalIndex (eg a corresponding Staff), starting at a specific horizontalIndex (eg specific GraphicalMeasure).
@@ -455,7 +466,7 @@ export class GraphicalMusicSheet {
      */
      */
     public getGraphicalMeasureFromSourceMeasureAndIndex(sourceMeasure: SourceMeasure, staffIndex: number): GraphicalMeasure {
     public getGraphicalMeasureFromSourceMeasureAndIndex(sourceMeasure: SourceMeasure, staffIndex: number): GraphicalMeasure {
         for (let i: number = 0; i < this.measureList.length; i++) {
         for (let i: number = 0; i < this.measureList.length; i++) {
-            if (this.measureList[i][0].parentSourceMeasure === sourceMeasure) {
+            if (this.measureList[i][0]?.parentSourceMeasure === sourceMeasure) {
                 return this.measureList[i][staffIndex];
                 return this.measureList[i][staffIndex];
             }
             }
         }
         }

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

@@ -88,7 +88,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      * @param tieNote
      * @param tieNote
      * @returns {any}
      * @returns {any}
      */
      */
-    public findEndTieGraphicalNoteFromNote(tieNote: Note): GraphicalNote {
+    public findTieGraphicalNoteFromNote(tieNote: Note): GraphicalNote {
         for (const gve of this.graphicalVoiceEntries) {
         for (const gve of this.graphicalVoiceEntries) {
             for (const graphicalNote of gve.notes) {
             for (const graphicalNote of gve.notes) {
                 const note: Note = graphicalNote.sourceNote;
                 const note: Note = graphicalNote.sourceNote;

+ 112 - 24
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -48,7 +48,7 @@ import { MidiInstrument } from "../VoiceData/Instructions/ClefInstruction";
 import { Staff } from "../VoiceData/Staff";
 import { Staff } from "../VoiceData/Staff";
 import { OctaveShift } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import { OctaveShift } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import log from "loglevel";
 import log from "loglevel";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import { Dictionary } from "typescript-collections";
 import { GraphicalLyricEntry } from "./GraphicalLyricEntry";
 import { GraphicalLyricEntry } from "./GraphicalLyricEntry";
 import { GraphicalLyricWord } from "./GraphicalLyricWord";
 import { GraphicalLyricWord } from "./GraphicalLyricWord";
 import { GraphicalLine } from "./GraphicalLine";
 import { GraphicalLine } from "./GraphicalLine";
@@ -167,11 +167,18 @@ export abstract class MusicSheetCalculator {
                 activeClefs
                 activeClefs
             );
             );
             measureList.push(graphicalMeasures);
             measureList.push(graphicalMeasures);
-            if (sourceMeasure.multipleRestMeasures && this.rules.RenderMultipleRestMeasures) {
+            if (sourceMeasure.multipleRestMeasures > 0 && this.rules.RenderMultipleRestMeasures) {
+                // multiRest given in XML, skip the next measures included
+                sourceMeasure.isReducedToMultiRest = true;
+                sourceMeasure.multipleRestMeasureNumber = 1;
                 const measuresToSkip: number = sourceMeasure.multipleRestMeasures - 1;
                 const measuresToSkip: number = sourceMeasure.multipleRestMeasures - 1;
                 // console.log(`skipping ${measuresToSkip} measures for measure #${sourceMeasure.MeasureNumber}.`);
                 // console.log(`skipping ${measuresToSkip} measures for measure #${sourceMeasure.MeasureNumber}.`);
                 idx += measuresToSkip;
                 idx += measuresToSkip;
-                for (let idx2: number = 0; idx2 < measuresToSkip; idx2++) {
+                for (let idx2: number = 1; idx2 <= measuresToSkip; idx2++) {
+                    const nextSourceMeasure: SourceMeasure = musicSheet.SourceMeasures[sourceMeasure.MeasureNumber - 1 + idx2];
+                    // TODO handle the case that a measure after the first multiple rest measure can't be reduced
+                    nextSourceMeasure.multipleRestMeasureNumber = idx2 + 1;
+                    nextSourceMeasure.isReducedToMultiRest = true;
                     measureList.push([undefined]);
                     measureList.push([undefined]);
                     // TODO we could push an object here or push nothing entirely,
                     // TODO we could push an object here or push nothing entirely,
                     //   but then the index doesn't correspond to measure numbers anymore.
                     //   but then the index doesn't correspond to measure numbers anymore.
@@ -179,6 +186,67 @@ export abstract class MusicSheetCalculator {
             }
             }
         }
         }
 
 
+        if (this.rules.AutoGenerateMutipleRestMeasuresFromRestMeasures && this.rules.RenderMultipleRestMeasures) {
+            //track number of multirests
+            let beginMultiRestMeasure: SourceMeasure = undefined;
+            let multiRestCount: number = 0;
+            //go through all source measures again. Need to calc auto-multi-rests
+            for (let idx: number = 0, len: number = musicSheet.SourceMeasures.length; idx < len; ++idx) {
+                const sourceMeasure: SourceMeasure = musicSheet.SourceMeasures[idx];
+                if (!sourceMeasure.isReducedToMultiRest && sourceMeasure.canBeReducedToMultiRest()) {
+                    //we've already been initialized, we are in the midst of a multirest sequence
+                    if (multiRestCount > 0) {
+                        beginMultiRestMeasure.isReducedToMultiRest = true;
+                        beginMultiRestMeasure.multipleRestMeasureNumber = 1;
+                        multiRestCount++;
+                        sourceMeasure.multipleRestMeasureNumber = multiRestCount;
+                        sourceMeasure.isReducedToMultiRest = true;
+                        //clear out these measures. We know now that we are in multirest mode
+                        for (let idx2: number = 0; idx2 < measureList[idx].length; idx2++) {
+                            measureList[idx][idx2] = undefined;
+                        }
+                    } else { //else this is the (potential) beginning
+                        beginMultiRestMeasure = sourceMeasure;
+                        multiRestCount = 1;
+                    }
+                } else { //not multirest measure
+                    if (multiRestCount > 1) { //Actual multirest sequence just happened. Process
+                        beginMultiRestMeasure.multipleRestMeasures = multiRestCount;
+                        //regen graphical measures for this source measure
+                        const graphicalMeasures: GraphicalMeasure[] = this.createGraphicalMeasuresForSourceMeasure(
+                            beginMultiRestMeasure,
+                            accidentalCalculators,
+                            lyricWords,
+                            openOctaveShifts,
+                            activeClefs
+                        );
+                        measureList[beginMultiRestMeasure.measureListIndex] = graphicalMeasures;
+                        multiRestCount = 0;
+                        beginMultiRestMeasure = undefined;
+                    } else { //had a potential multirest sequence, but didn't pan out. only one measure was rests
+                        multiRestCount = 0;
+                        beginMultiRestMeasure = undefined;
+                    }
+                }
+            }
+            //If we reached the end of the sheet and have pending multirest measure, process
+            if (multiRestCount > 1) {
+                beginMultiRestMeasure.multipleRestMeasures = multiRestCount;
+                beginMultiRestMeasure.isReducedToMultiRest = true;
+                //regen graphical measures for this source measure
+                const graphicalMeasures: GraphicalMeasure[] = this.createGraphicalMeasuresForSourceMeasure(
+                    beginMultiRestMeasure,
+                    accidentalCalculators,
+                    lyricWords,
+                    openOctaveShifts,
+                    activeClefs
+                );
+                measureList[beginMultiRestMeasure.measureListIndex] = graphicalMeasures;
+                multiRestCount = 0;
+                beginMultiRestMeasure = undefined;
+            }
+        }
+
         const staffIsPercussionArray: Array<boolean> =
         const staffIsPercussionArray: Array<boolean> =
                         activeClefs.map(clef => (clef.ClefType === ClefEnum.percussion));
                         activeClefs.map(clef => (clef.ClefType === ClefEnum.percussion));
 
 
@@ -343,6 +411,10 @@ export abstract class MusicSheetCalculator {
      */
      */
     protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
     protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
         const staffLine: StaffLine = musicSystem.StaffLines[0];
         const staffLine: StaffLine = musicSystem.StaffLines[0];
+        if (!staffLine || !staffLine.Measures[0]) {
+            log.warn("calculateMeasureNumberPlacement: measure undefined for system.Id " + musicSystem.Id);
+            return; // TODO apparently happens in script sometimes (mp #70)
+        }
         let previousLabelMeasureNumber: number = staffLine.Measures[0].MeasureNumber;
         let previousLabelMeasureNumber: number = staffLine.Measures[0].MeasureNumber;
         let labelOffsetX: number = 0;
         let labelOffsetX: number = 0;
         for (let i: number = 0; i < staffLine.Measures.length; i++) {
         for (let i: number = 0; i < staffLine.Measures.length; i++) {
@@ -499,7 +571,8 @@ export abstract class MusicSheetCalculator {
                 // eg verseNumbers: 2,3,4,6 => 1,2,3,4
                 // eg verseNumbers: 2,3,4,6 => 1,2,3,4
                 const verseNumber: number = lyricEntry.LyricsEntry.VerseNumber;
                 const verseNumber: number = lyricEntry.LyricsEntry.VerseNumber;
                 const sortedLyricVerseNumberIndex: number = lyricVersesNumber.indexOf(verseNumber);
                 const sortedLyricVerseNumberIndex: number = lyricVersesNumber.indexOf(verseNumber);
-                const firstPosition: number = lyricsStartYPosition + this.rules.LyricsHeight + this.rules.VerticalBetweenLyricsDistance;
+                const firstPosition: number = lyricsStartYPosition + this.rules.LyricsHeight + this.rules.VerticalBetweenLyricsDistance +
+                    this.rules.LyricsYOffsetToStaffHeight;
 
 
                 // Y-position calculated according to aforementioned mapping
                 // Y-position calculated according to aforementioned mapping
                 let position: number = firstPosition + (this.rules.VerticalBetweenLyricsDistance + this.rules.LyricsHeight) * sortedLyricVerseNumberIndex;
                 let position: number = firstPosition + (this.rules.VerticalBetweenLyricsDistance + this.rules.LyricsHeight) * sortedLyricVerseNumberIndex;
@@ -1380,6 +1453,9 @@ export abstract class MusicSheetCalculator {
             if (this.rules.MinMeasureToDrawIndex > 0) {
             if (this.rules.MinMeasureToDrawIndex > 0) {
                 return; // assuming that the tempo is always in measure 1 (idx 0), adding the expression causes issues when we don't draw measure 1
                 return; // assuming that the tempo is always in measure 1 (idx 0), adding the expression causes issues when we don't draw measure 1
             }
             }
+            if (!measures[0]) {
+                return;
+            }
             let staffLine: StaffLine = measures[0].ParentStaffLine;
             let staffLine: StaffLine = measures[0].ParentStaffLine;
             let firstVisibleMeasureX: number = measures[0].PositionAndShape.RelativePosition.x;
             let firstVisibleMeasureX: number = measures[0].PositionAndShape.RelativePosition.x;
             let verticalIndex: number = 0;
             let verticalIndex: number = 0;
@@ -1644,7 +1720,7 @@ export abstract class MusicSheetCalculator {
     protected maxInstrNameLabelLength(): number {
     protected maxInstrNameLabelLength(): number {
         let maxLabelLength: number = 0.0;
         let maxLabelLength: number = 0.0;
         for (const instrument of this.graphicalMusicSheet.ParentMusicSheet.Instruments) {
         for (const instrument of this.graphicalMusicSheet.ParentMusicSheet.Instruments) {
-            if (instrument.NameLabel.print && instrument.Voices.length > 0 && instrument.Voices[0].Visible) {
+            if (instrument.NameLabel?.print && instrument.Voices.length > 0 && instrument.Voices[0].Visible) {
                 let renderedLabel: Label = instrument.NameLabel;
                 let renderedLabel: Label = instrument.NameLabel;
                 if (!this.rules.RenderPartNames) {
                 if (!this.rules.RenderPartNames) {
                     renderedLabel = new Label("", renderedLabel.textAlignment, renderedLabel.font);
                     renderedLabel = new Label("", renderedLabel.textAlignment, renderedLabel.font);
@@ -1944,16 +2020,16 @@ export abstract class MusicSheetCalculator {
 
 
     private handleTie(tie: Tie, startGraphicalStaffEntry: GraphicalStaffEntry, staffIndex: number, measureIndex: number): void {
     private handleTie(tie: Tie, startGraphicalStaffEntry: GraphicalStaffEntry, staffIndex: number, measureIndex: number): void {
         let startGse: GraphicalStaffEntry = startGraphicalStaffEntry;
         let startGse: GraphicalStaffEntry = startGraphicalStaffEntry;
-        let startNote: GraphicalNote = startGse.findEndTieGraphicalNoteFromNote(tie.StartNote);
+        let startNote: GraphicalNote = undefined;
         let endGse: GraphicalStaffEntry = undefined;
         let endGse: GraphicalStaffEntry = undefined;
         let endNote: GraphicalNote = undefined;
         let endNote: GraphicalNote = undefined;
         for (let i: number = 1; i < tie.Notes.length; i++) {
         for (let i: number = 1; i < tie.Notes.length; i++) {
-            startNote = startGse.findEndTieGraphicalNoteFromNote(tie.Notes[i - 1]);
+            startNote = startGse.findTieGraphicalNoteFromNote(tie.Notes[i - 1]);
             endGse = this.graphicalMusicSheet.GetGraphicalFromSourceStaffEntry(tie.Notes[i].ParentStaffEntry);
             endGse = this.graphicalMusicSheet.GetGraphicalFromSourceStaffEntry(tie.Notes[i].ParentStaffEntry);
             if (!endGse) {
             if (!endGse) {
                 continue;
                 continue;
             }
             }
-            endNote = endGse.findEndTieGraphicalNoteFromNote(tie.Notes[i]);
+            endNote = endGse.findTieGraphicalNoteFromNote(tie.Notes[i]);
             if (startNote !== undefined && endNote !== undefined && endGse) {
             if (startNote !== undefined && endNote !== undefined && endGse) {
                 if (!startNote.sourceNote.PrintObject || !endNote.sourceNote.PrintObject) {
                 if (!startNote.sourceNote.PrintObject || !endNote.sourceNote.PrintObject) {
                     continue;
                     continue;
@@ -2024,13 +2100,16 @@ export abstract class MusicSheetCalculator {
         const openBeams: Beam[] = [];
         const openBeams: Beam[] = [];
         const openTuplets: Tuplet[] = [];
         const openTuplets: Tuplet[] = [];
         const staffEntryLinks: StaffEntryLink[] = [];
         const staffEntryLinks: StaffEntryLink[] = [];
+        let restInAllGraphicalMeasures: boolean = true;
         for (let staffIndex: number = 0; staffIndex < sourceMeasure.CompleteNumberOfStaves; staffIndex++) {
         for (let staffIndex: number = 0; staffIndex < sourceMeasure.CompleteNumberOfStaves; staffIndex++) {
             const measure: GraphicalMeasure = this.createGraphicalMeasure( // (VexFlowMeasure)
             const measure: GraphicalMeasure = this.createGraphicalMeasure( // (VexFlowMeasure)
                 sourceMeasure, openTuplets, openBeams,
                 sourceMeasure, openTuplets, openBeams,
                 accidentalCalculators[staffIndex], activeClefs, openOctaveShifts, openLyricWords, staffIndex, staffEntryLinks
                 accidentalCalculators[staffIndex], activeClefs, openOctaveShifts, openLyricWords, staffIndex, staffEntryLinks
             );
             );
+            restInAllGraphicalMeasures = restInAllGraphicalMeasures && measure.hasOnlyRests;
             verticalMeasureList.push(measure);
             verticalMeasureList.push(measure);
         }
         }
+        sourceMeasure.allRests = restInAllGraphicalMeasures;
         sourceMeasure.VerticalMeasureList = verticalMeasureList; // much easier way to link sourceMeasure to graphicalMeasures than Dictionary
         sourceMeasure.VerticalMeasureList = verticalMeasureList; // much easier way to link sourceMeasure to graphicalMeasures than Dictionary
         //this.graphicalMusicSheet.sourceToGraphicalMeasureLinks.setValue(sourceMeasure, verticalMeasureList); // overwrites entries because:
         //this.graphicalMusicSheet.sourceToGraphicalMeasureLinks.setValue(sourceMeasure, verticalMeasureList); // overwrites entries because:
         //this.graphicalMusicSheet.sourceToGraphicalMeasureLinks[sourceMeasure] = verticalMeasureList; // can't use SourceMeasure as key.
         //this.graphicalMusicSheet.sourceToGraphicalMeasureLinks[sourceMeasure] = verticalMeasureList; // can't use SourceMeasure as key.
@@ -2186,7 +2265,7 @@ export abstract class MusicSheetCalculator {
                                                           measure.parentSourceMeasure.CompleteNumberOfStaves),
                                                           measure.parentSourceMeasure.CompleteNumberOfStaves),
                     staff);
                     staff);
                 const voiceEntry: VoiceEntry = new VoiceEntry(new Fraction(0, 1), staff.Voices[0], sourceStaffEntry);
                 const voiceEntry: VoiceEntry = new VoiceEntry(new Fraction(0, 1), staff.Voices[0], sourceStaffEntry);
-                const note: Note = new Note(voiceEntry, sourceStaffEntry, Fraction.createFromFraction(sourceMeasure.Duration), undefined);
+                const note: Note = new Note(voiceEntry, sourceStaffEntry, Fraction.createFromFraction(sourceMeasure.Duration), undefined, sourceMeasure);
                 note.PrintObject = this.rules.FillEmptyMeasuresWithWholeRest === FillEmptyMeasuresWithWholeRests.YesVisible;
                 note.PrintObject = this.rules.FillEmptyMeasuresWithWholeRest === FillEmptyMeasuresWithWholeRests.YesVisible;
                   // don't display whole rest that wasn't given in XML, only for layout/voice completion
                   // don't display whole rest that wasn't given in XML, only for layout/voice completion
                 voiceEntry.Notes.push(note);
                 voiceEntry.Notes.push(note);
@@ -2203,6 +2282,17 @@ export abstract class MusicSheetCalculator {
                 gve.notes.push(graphicalNote);
                 gve.notes.push(graphicalNote);
             }
             }
         }
         }
+
+        measure.hasOnlyRests = true;
+        //if staff entries empty, loop will not start. so true is valid
+        for (const graphicalStaffEntry of measure.staffEntries) {
+            //Loop until we get just one false
+            measure.hasOnlyRests = graphicalStaffEntry.hasOnlyRests();
+            if (!measure.hasOnlyRests) {
+                break;
+            }
+        }
+
         return measure;
         return measure;
     }
     }
 
 
@@ -2420,17 +2510,11 @@ export abstract class MusicSheetCalculator {
     }
     }
 
 
     private calculateTieCurves(): void {
     private calculateTieCurves(): void {
-        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
-            const musicSystem: MusicSystem = this.musicSystems[idx2];
-            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
-                const staffLine: StaffLine = musicSystem.StaffLines[idx3];
-                for (let idx4: number = 0, len5: number = staffLine.Measures.length; idx4 < len5; ++idx4) {
-                    const measure: GraphicalMeasure = staffLine.Measures[idx4];
-                    for (let idx6: number = 0, len6: number = measure.staffEntries.length; idx6 < len6; ++idx6) {
-                        const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx6];
-                        const graphicalTies: GraphicalTie[] = staffEntry.GraphicalTies;
-                        for (let idx7: number = 0, len7: number = graphicalTies.length; idx7 < len7; ++idx7) {
-                            const graphicalTie: GraphicalTie = graphicalTies[idx7];
+        for (const musicSystem of this.musicSystems) {
+            for (const staffLine of musicSystem.StaffLines) {
+                for (const measure of staffLine.Measures) {
+                    for (const staffEntry of measure.staffEntries) {
+                        for (const graphicalTie of staffEntry.GraphicalTies) {
                             if (graphicalTie.StartNote !== undefined && graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry === staffEntry) {
                             if (graphicalTie.StartNote !== undefined && graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry === staffEntry) {
                                 const tieIsAtSystemBreak: boolean = (
                                 const tieIsAtSystemBreak: boolean = (
                                     graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentStaffLine !==
                                     graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentStaffLine !==
@@ -2635,13 +2719,16 @@ export abstract class MusicSheetCalculator {
                 break;
                 break;
             }
             }
             endStaffEntry = gse;
             endStaffEntry = gse;
-            endStaffLine = <StaffLine>endStaffEntry.parentMeasure.ParentStaffLine;
+            endStaffLine = endStaffEntry.parentMeasure.ParentStaffLine;
+            if (!endStaffLine) {
+                endStaffLine = startStaffEntry.parentMeasure.ParentStaffLine;
+            }
         }
         }
-        if (!endStaffEntry) {
+        if (!endStaffEntry || !endStaffLine) {
             return;
             return;
         }
         }
         // if on the same StaffLine
         // if on the same StaffLine
-        if (startStaffLine === endStaffLine) {
+        if (startStaffLine === endStaffLine && endStaffEntry.parentMeasure.ParentStaffLine) {
             // start- and End margins from the text Labels
             // start- and End margins from the text Labels
             const startX: number = startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
             const startX: number = startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
                 startStaffEntry.PositionAndShape.RelativePosition.x +
                 startStaffEntry.PositionAndShape.RelativePosition.x +
@@ -2674,7 +2761,8 @@ export abstract class MusicSheetCalculator {
                 return;
                 return;
             }
             }
             // second Underscore in the endStaffLine until endStaffEntry (if endStaffEntry isn't the first StaffEntry of the StaffLine))
             // second Underscore in the endStaffLine until endStaffEntry (if endStaffEntry isn't the first StaffEntry of the StaffLine))
-            if (!(endStaffEntry === endStaffEntry.parentMeasure.staffEntries[0] &&
+            if (endStaffEntry.parentMeasure.ParentStaffLine && endStaffEntry.parentMeasure.staffEntries &&
+                !(endStaffEntry === endStaffEntry.parentMeasure.staffEntries[0] &&
                 endStaffEntry.parentMeasure === endStaffEntry.parentMeasure.ParentStaffLine.Measures[0])) {
                 endStaffEntry.parentMeasure === endStaffEntry.parentMeasure.ParentStaffLine.Measures[0])) {
                 const secondStartX: number = endStaffLine.Measures[0].staffEntries[0].PositionAndShape.RelativePosition.x;
                 const secondStartX: number = endStaffLine.Measures[0].staffEntries[0].PositionAndShape.RelativePosition.x;
                 const secondEndX: number = endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
                 const secondEndX: number = endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +

+ 3 - 4
src/MusicalScore/Graphical/MusicSystem.ts

@@ -13,7 +13,7 @@ import {EngravingRules} from "./EngravingRules";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
 import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
 import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
 import {SystemLinesEnum} from "./SystemLinesEnum";
 import {SystemLinesEnum} from "./SystemLinesEnum";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import { Dictionary } from "typescript-collections";
 import {GraphicalComment} from "./GraphicalComment";
 import {GraphicalComment} from "./GraphicalComment";
 import {GraphicalMarkedArea} from "./GraphicalMarkedArea";
 import {GraphicalMarkedArea} from "./GraphicalMarkedArea";
 import {SystemLine} from "./SystemLine";
 import {SystemLine} from "./SystemLine";
@@ -301,9 +301,8 @@ export abstract class MusicSystem extends GraphicalObject {
                     systemLabelsRightMargin = 0; // might affect lyricist/tempo placement. but without this there's still some extra x-spacing.
                     systemLabelsRightMargin = 0; // might affect lyricist/tempo placement. but without this there's still some extra x-spacing.
                 }
                 }
             } else {
             } else {
-                if (!this.rules.RenderPartAbbreviations
-                    // don't render part abbreviations if there's only one instrument/part (could be an option in the future)
-                    || this.staffLines.length === 1
+                if (!this.rules.RenderPartAbbreviations || !this.rules.RenderPartNames // don't render abbreviations if we don't render part names
+                    || this.staffLines.length === 1 // don't render part abbreviations if there's only one instrument/part (could be an option in the future)
                     || !instrument.PartAbbreviation
                     || !instrument.PartAbbreviation
                     || instrument.PartAbbreviation === "") {
                     || instrument.PartAbbreviation === "") {
                     return;
                     return;

+ 7 - 4
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -82,6 +82,7 @@ export class MusicSystemBuilder {
             }
             }
             const sourceMeasure: SourceMeasure = graphicalMeasures[0].parentSourceMeasure;
             const sourceMeasure: SourceMeasure = graphicalMeasures[0].parentSourceMeasure;
             const sourceMeasureEndsPart: boolean = sourceMeasure.HasEndLine;
             const sourceMeasureEndsPart: boolean = sourceMeasure.HasEndLine;
+            const sourceMeasureBreaksSystem: boolean = sourceMeasureEndsPart && this.rules.NewPartAndSystemAfterFinalBarline;
             const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
             const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
             const isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
             const isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
             let currentMeasureBeginInstructionsWidth: number = this.rules.MeasureLeftMargin;
             let currentMeasureBeginInstructionsWidth: number = this.rules.MeasureLeftMargin;
@@ -140,8 +141,8 @@ export class MusicSystemBuilder {
                 );
                 );
                 this.updateActiveClefs(sourceMeasure, graphicalMeasures);
                 this.updateActiveClefs(sourceMeasure, graphicalMeasures);
                 this.measureListIndex++;
                 this.measureListIndex++;
-                if (sourceMeasureEndsPart) {
-                    this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, true, false);
+                if (sourceMeasureBreaksSystem) {
+                    this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, !this.rules.StretchLastSystemLine, false);
                 }
                 }
                 prevMeasureEndsPart = sourceMeasureEndsPart;
                 prevMeasureEndsPart = sourceMeasureEndsPart;
             } else {
             } else {
@@ -152,7 +153,7 @@ export class MusicSystemBuilder {
             }
             }
         }
         }
         if (this.currentSystemParams.systemMeasures.length > 0) {
         if (this.currentSystemParams.systemMeasures.length > 0) {
-            this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], true, false);
+            this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], !this.rules.StretchLastSystemLine, false);
         }
         }
         return this.musicSystems;
         return this.musicSystems;
     }
     }
@@ -290,7 +291,7 @@ export class MusicSystemBuilder {
         const instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
         const instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
         for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
         for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
             const instrument: Instrument = instruments[idx];
             const instrument: Instrument = instruments[idx];
-            if (instrument.Voices.length === 0 || !instrument.Visible) {
+            if (!instrument.Visible || instrument.Voices.length === 0) {
                 continue;
                 continue;
             }
             }
             for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
             for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
@@ -965,6 +966,8 @@ export class MusicSystemBuilder {
      * @returns {GraphicalMusicPage}
      * @returns {GraphicalMusicPage}
      */
      */
     protected createMusicPage(): GraphicalMusicPage {
     protected createMusicPage(): GraphicalMusicPage {
+        // const previousPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages.last();
+        // const previousSizeY: number = previousPage ? previousPage.PositionAndShape.Size.height : 0;
         const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
         const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
         this.graphicalMusicSheet.MusicPages.push(page);
         this.graphicalMusicSheet.MusicPages.push(page);
         page.PageNumber = this.graphicalMusicSheet.MusicPages.length; // caution: page number = page index + 1
         page.PageNumber = this.graphicalMusicSheet.MusicPages.length; // caution: page number = page index + 1

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

@@ -4,7 +4,7 @@ import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicEx
 import { AbstractGraphicalExpression } from "../AbstractGraphicalExpression";
 import { AbstractGraphicalExpression } from "../AbstractGraphicalExpression";
 import { PointF2D } from "../../../Common/DataObjects/PointF2D";
 import { PointF2D } from "../../../Common/DataObjects/PointF2D";
 import { EngravingRules } from "../EngravingRules";
 import { EngravingRules } from "../EngravingRules";
-import { PlacementEnum } from "../../VoiceData/Expressions";
+import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
 
 
 export class AlignmentManager {
 export class AlignmentManager {
     private parentStaffline: StaffLine;
     private parentStaffline: StaffLine;

+ 9 - 1
src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts

@@ -6,7 +6,7 @@ import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {VexFlowConverter} from "./VexFlowConverter";
 import {VexFlowConverter} from "./VexFlowConverter";
-import {BackendType} from "../../../OpenSheetMusicDisplay";
+import {BackendType} from "../../../OpenSheetMusicDisplay/OSMDOptions";
 import {EngravingRules} from "../EngravingRules";
 import {EngravingRules} from "../EngravingRules";
 import {GraphicalMusicPage} from "../GraphicalMusicPage";
 import {GraphicalMusicPage} from "../GraphicalMusicPage";
 
 
@@ -24,6 +24,13 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
         return BackendType.Canvas;
         return BackendType.Canvas;
     }
     }
 
 
+    public getCanvasSize(): number {
+        return document.getElementById("osmdCanvasPage" + this.graphicalMusicPage.PageNumber)?.offsetHeight;
+        // smaller inner canvas:
+        // return Number.parseInt(
+        //     document.getElementById("osmdCanvasVexFlowBackendCanvas" + this.graphicalMusicPage.PageNumber)?.style.height, 10);
+    }
+
     public initialize(container: HTMLElement): void {
     public initialize(container: HTMLElement): void {
         this.canvas = document.createElement("canvas");
         this.canvas = document.createElement("canvas");
         if (!this.graphicalMusicPage) {
         if (!this.graphicalMusicPage) {
@@ -32,6 +39,7 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
         }
         }
         this.canvas.id = "osmdCanvasVexFlowBackendCanvas" + this.graphicalMusicPage.PageNumber; // needed to extract image buffer from js
         this.canvas.id = "osmdCanvasVexFlowBackendCanvas" + this.graphicalMusicPage.PageNumber; // needed to extract image buffer from js
         this.inner = document.createElement("div");
         this.inner = document.createElement("div");
+        this.inner.id = "osmdCanvasPage" + this.graphicalMusicPage.PageNumber;
         this.inner.style.position = "relative";
         this.inner.style.position = "relative";
         this.canvas.style.zIndex = "0";
         this.canvas.style.zIndex = "0";
         this.inner.appendChild(this.canvas);
         this.inner.appendChild(this.canvas);

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

@@ -6,8 +6,8 @@ import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
-import {EngravingRules} from "..";
-import {BackendType} from "../../../OpenSheetMusicDisplay";
+import {BackendType} from "../../../OpenSheetMusicDisplay/OSMDOptions";
+import {EngravingRules} from "../EngravingRules";
 
 
 export class SvgVexFlowBackend extends VexFlowBackend {
 export class SvgVexFlowBackend extends VexFlowBackend {
 
 
@@ -26,8 +26,13 @@ export class SvgVexFlowBackend extends VexFlowBackend {
         return BackendType.SVG;
         return BackendType.SVG;
     }
     }
 
 
+    public getCanvasSize(): number {
+        return document.getElementById("osmdCanvasPage" + this.graphicalMusicPage.PageNumber)?.offsetHeight;
+    }
+
     public initialize(container: HTMLElement): void {
     public initialize(container: HTMLElement): void {
         this.canvas = document.createElement("div");
         this.canvas = document.createElement("div");
+        this.canvas.id = "osmdCanvasPage" + this.graphicalMusicPage.PageNumber;
         // this.canvas.id = uniqueID // TODO create unique tagName like with cursor now?
         // this.canvas.id = uniqueID // TODO create unique tagName like with cursor now?
         this.inner = this.canvas;
         this.inner = this.canvas;
         this.inner.style.position = "relative";
         this.inner.style.position = "relative";

+ 11 - 4
src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts

@@ -3,8 +3,9 @@ import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
-import {GraphicalMusicPage, EngravingRules} from "..";
-import {BackendType} from "../../../OpenSheetMusicDisplay";
+import {BackendType} from "../../../OpenSheetMusicDisplay/OSMDOptions";
+import {GraphicalMusicPage} from "../GraphicalMusicPage";
+import {EngravingRules} from "../EngravingRules";
 
 
 export class VexFlowBackends {
 export class VexFlowBackends {
   public static CANVAS: 0;
   public static CANVAS: 0;
@@ -20,6 +21,8 @@ export abstract class VexFlowBackend {
   /** The GraphicalMusicPage the backend is drawing from. Each backend only renders one GraphicalMusicPage, to which the coordinates are relative. */
   /** The GraphicalMusicPage the backend is drawing from. Each backend only renders one GraphicalMusicPage, to which the coordinates are relative. */
   public graphicalMusicPage: GraphicalMusicPage;
   public graphicalMusicPage: GraphicalMusicPage;
   protected rules: EngravingRules;
   protected rules: EngravingRules;
+  public width: number; // read-only
+  public height: number; // read-only
 
 
   public abstract initialize(container: HTMLElement): void;
   public abstract initialize(container: HTMLElement): void;
 
 
@@ -31,6 +34,8 @@ export abstract class VexFlowBackend {
     return this.canvas;
     return this.canvas;
   }
   }
 
 
+  public abstract getCanvasSize(): number;
+
   public getRenderElement(): HTMLElement {
   public getRenderElement(): HTMLElement {
     //console.log("backend type: " + this.getVexflowBackendType());
     //console.log("backend type: " + this.getVexflowBackendType());
     let renderingHtmlElement: HTMLElement = this.canvas; // for SVGBackend
     let renderingHtmlElement: HTMLElement = this.canvas; // for SVGBackend
@@ -74,8 +79,10 @@ public abstract getContext(): Vex.IRenderContext;
 
 
   public abstract scale(k: number): void;
   public abstract scale(k: number): void;
 
 
-  public resize(x: number, y: number): void {
-    this.renderer.resize(x, y);
+  public resize(width: number, height: number): void {
+    this.renderer.resize(width, height);
+    this.width = width;
+    this.height = height;
   }
   }
 
 
   public abstract clear(): void;
   public abstract clear(): void;

+ 80 - 13
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -16,17 +16,19 @@ import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEnums";
 import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEnums";
 import log from "loglevel";
 import log from "loglevel";
-import { ArticulationEnum, StemDirectionType } from "../../VoiceData/VoiceEntry";
+import { ArticulationEnum, StemDirectionType, VoiceEntry } from "../../VoiceData/VoiceEntry";
 import { SystemLinePosition } from "../SystemLinePosition";
 import { SystemLinePosition } from "../SystemLinePosition";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { OrnamentEnum, OrnamentContainer } from "../../VoiceData/OrnamentContainer";
 import { OrnamentEnum, OrnamentContainer } from "../../VoiceData/OrnamentContainer";
 import { Notehead, NoteHeadShape } from "../../VoiceData/Notehead";
 import { Notehead, NoteHeadShape } from "../../VoiceData/Notehead";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
 import { EngravingRules } from "../EngravingRules";
 import { EngravingRules } from "../EngravingRules";
-import { Note } from "../..";
+import { Note } from "../../../MusicalScore/VoiceData/Note";
 import StaveNote = Vex.Flow.StaveNote;
 import StaveNote = Vex.Flow.StaveNote;
-import { ArpeggioType } from "../../VoiceData";
+import { ArpeggioType } from "../../VoiceData/Arpeggio";
 import { TabNote } from "../../VoiceData/TabNote";
 import { TabNote } from "../../VoiceData/TabNote";
+import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
+import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
 
 
 /**
 /**
  * Helper class, which contains static methods which actually convert
  * Helper class, which contains static methods which actually convert
@@ -114,17 +116,46 @@ export class VexFlowConverter {
      * @param pitch
      * @param pitch
      * @returns {string[]}
      * @returns {string[]}
      */
      */
-    public static pitch(note: VexFlowGraphicalNote, pitch: Pitch): [string, string, ClefInstruction] {
+    public static pitch(pitch: Pitch, isRest: boolean, clef: ClefInstruction,
+                        notehead: Notehead = undefined): [string, string, ClefInstruction] {
+        //FIXME: The octave seems to need a shift of three?
+        //FIXME: Also rests seem to use different offsets depending on the clef.
+        let fixmeOffset: number = 3;
+        if (isRest) {
+            fixmeOffset = 0;
+            if (clef.ClefType === ClefEnum.F) {
+                fixmeOffset = 2;
+            }
+            if (clef.ClefType === ClefEnum.C) {
+                fixmeOffset = 2;
+            }
+            // TODO the pitch for rests will be the start position, for eights rests it will be the bottom point
+            // maybe we want to center on the display position instead of having the bottom there?
+        }
         const fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
         const fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
         const acc: string = Pitch.accidentalVexflow(pitch.Accidental);
         const acc: string = Pitch.accidentalVexflow(pitch.Accidental);
-        // The octave seems to need a shift of three FIXME?
-        const octave: number = pitch.Octave - note.Clef().OctaveOffset + 3;
-        const notehead: Notehead = note.sourceNote.Notehead;
+        const octave: number = pitch.Octave - clef.OctaveOffset + fixmeOffset;
         let noteheadCode: string = "";
         let noteheadCode: string = "";
         if (notehead) {
         if (notehead) {
             noteheadCode = this.NoteHeadCode(notehead);
             noteheadCode = this.NoteHeadCode(notehead);
         }
         }
-        return [fund + "n/" + octave + noteheadCode, acc, note.Clef()];
+        return [fund + "n/" + octave + noteheadCode, acc, clef];
+    }
+
+    public static restToNotePitch(pitch: Pitch, clefType: ClefEnum): Pitch {
+        let octave: number = pitch.Octave;
+        // offsets see pitch()
+        switch (clefType) {
+            case ClefEnum.C:
+            case ClefEnum.F: {
+                octave += 2;
+                break;
+            }
+            case ClefEnum.G:
+            default:
+        }
+
+        return new Pitch(pitch.FundamentalNote, octave, AccidentalEnum.NONE);
     }
     }
 
 
     /** returns the Vexflow code for a note head. Some are still unsupported, see Vexflow/tables.js */
     /** returns the Vexflow code for a note head. Some are still unsupported, see Vexflow/tables.js */
@@ -197,7 +228,43 @@ export class VexFlowConverter {
             // if it is a rest:
             // if it is a rest:
             if (note.sourceNote.isRest()) {
             if (note.sourceNote.isRest()) {
                 isRest = true;
                 isRest = true;
-                keys = ["b/4"];
+                if (note.sourceNote.Pitch) {
+                    const restVfPitch: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
+                    keys = [restVfPitch[0]];
+                    break;
+                } else {
+                    keys = ["b/4"]; // default placement
+
+                    // pause rest encircled by two beamed notes: place rest just below previous note
+                    const pauseVoiceEntry: VoiceEntry = note.parentVoiceEntry?.parentVoiceEntry;
+                    if (pauseVoiceEntry) {
+                        const neighborGSEs: GraphicalStaffEntry[] = note.parentVoiceEntry?.parentStaffEntry.parentMeasure.staffEntries;
+                        let previousVoiceEntry: VoiceEntry, followingVoiceEntry: VoiceEntry;
+                        let pauseVEIndex: number = -1;
+                        for (let i: number = 0; i < neighborGSEs.length; i++) {
+                            if (neighborGSEs[i]?.graphicalVoiceEntries[0].parentVoiceEntry === pauseVoiceEntry) {
+                                pauseVEIndex = i;
+                                break;
+                            }
+                        }
+                        if (pauseVEIndex >= 1 && (neighborGSEs.length - 1) >= (pauseVEIndex + 1)) {
+                            previousVoiceEntry = neighborGSEs[pauseVEIndex - 1]?.graphicalVoiceEntries[0]?.parentVoiceEntry;
+                            followingVoiceEntry = neighborGSEs[pauseVEIndex + 1]?.graphicalVoiceEntries[0]?.parentVoiceEntry;
+                            if (previousVoiceEntry && followingVoiceEntry) {
+                                const previousNote: Note = previousVoiceEntry.Notes[0];
+                                const followingNote: Note = followingVoiceEntry.Notes[0];
+                                if (previousNote.NoteBeam?.Notes.includes(followingNote)) {
+                                    const previousNotePitch: Pitch = previousVoiceEntry.Notes.last().Pitch;
+                                    const clef: ClefInstruction = (note as VexFlowGraphicalNote).Clef();
+                                    const vfpitch: [string, string, ClefInstruction] = VexFlowConverter.pitch(
+                                        VexFlowConverter.restToNotePitch(previousNotePitch.getTransposedPitch(-2), clef.ClefType),
+                                        false, clef, undefined);
+                                    keys = [vfpitch[0]];
+                                }
+                            }
+                        }
+                    }
+                }
                 // TODO do collision checking, place rest e.g. either below staff (A3, for stem direction below voice) or above (C5)
                 // TODO do collision checking, place rest e.g. either below staff (A3, for stem direction below voice) or above (C5)
                 // if it is a full measure rest:
                 // if it is a full measure rest:
                 if (note.parentVoiceEntry.parentStaffEntry.parentMeasure.parentSourceMeasure.Duration.RealValue <= frac.RealValue) {
                 if (note.parentVoiceEntry.parentStaffEntry.parentMeasure.parentSourceMeasure.Duration.RealValue <= frac.RealValue) {
@@ -458,7 +525,7 @@ export class VexFlowConverter {
 
 
     public static generateOrnaments(vfnote: Vex.Flow.StemmableNote, oContainer: OrnamentContainer): void {
     public static generateOrnaments(vfnote: Vex.Flow.StemmableNote, oContainer: OrnamentContainer): void {
         let vfPosition: number = Vex.Flow.Modifier.Position.ABOVE;
         let vfPosition: number = Vex.Flow.Modifier.Position.ABOVE;
-        if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
+        if (oContainer.placement === PlacementEnum.Below) {
             vfPosition = Vex.Flow.Modifier.Position.BELOW;
             vfPosition = Vex.Flow.Modifier.Position.BELOW;
         }
         }
 
 
@@ -475,7 +542,7 @@ export class VexFlowConverter {
                 break;
                 break;
             }
             }
             case OrnamentEnum.InvertedMordent: {
             case OrnamentEnum.InvertedMordent: {
-                vfOrna = new Vex.Flow.Ornament("mordent_inverted");
+                vfOrna = new Vex.Flow.Ornament("mordent"); // Vexflow uses baroque, not MusicXML definition
                 vfOrna.setDelayed(false);
                 vfOrna.setDelayed(false);
                 break;
                 break;
             }
             }
@@ -485,7 +552,7 @@ export class VexFlowConverter {
                 break;
                 break;
             }
             }
             case OrnamentEnum.Mordent: {
             case OrnamentEnum.Mordent: {
-                vfOrna = new Vex.Flow.Ornament("mordent");
+                vfOrna = new Vex.Flow.Ornament("mordent_inverted");
                 vfOrna.setDelayed(false);
                 vfOrna.setDelayed(false);
                 break;
                 break;
             }
             }
@@ -511,7 +578,7 @@ export class VexFlowConverter {
             if (oContainer.AccidentalAbove !== AccidentalEnum.NONE) {
             if (oContainer.AccidentalAbove !== AccidentalEnum.NONE) {
                 vfOrna.setUpperAccidental(Pitch.accidentalVexflow(oContainer.AccidentalAbove));
                 vfOrna.setUpperAccidental(Pitch.accidentalVexflow(oContainer.AccidentalAbove));
             }
             }
-            vfOrna.setPosition(vfPosition);
+            vfOrna.setPosition(vfPosition); // Vexflow draws it above right now in any case, never below
             (vfnote as StaveNote).addModifier(0, vfOrna);
             (vfnote as StaveNote).addModifier(0, vfOrna);
         }
         }
     }
     }

+ 4 - 4
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts

@@ -20,8 +20,8 @@ export class VexFlowGraphicalNote extends GraphicalNote {
         this.octaveShift = octaveShift;
         this.octaveShift = octaveShift;
         if (note.Pitch) {
         if (note.Pitch) {
             // TODO: Maybe shift to Transpose function when available
             // TODO: Maybe shift to Transpose function when available
-            const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(note.Pitch, octaveShift);
-            this.vfpitch = VexFlowConverter.pitch(this, drawPitch);
+            const drawPitch: Pitch = note.isRest() ? note.Pitch : OctaveShift.getPitchFromOctaveShift(note.Pitch, octaveShift);
+            this.vfpitch = VexFlowConverter.pitch(drawPitch, note.isRest(), this.clef, this.sourceNote.Notehead);
             this.vfpitch[1] = undefined;
             this.vfpitch[1] = undefined;
         }
         }
     }
     }
@@ -51,13 +51,13 @@ export class VexFlowGraphicalNote extends GraphicalNote {
         // revert octave shift, as the placement of the note is independent of octave brackets
         // revert octave shift, as the placement of the note is independent of octave brackets
         const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(pitch, this.octaveShift);
         const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(pitch, this.octaveShift);
         // recalculate the pitch, and this time don't ignore the accidental:
         // recalculate the pitch, and this time don't ignore the accidental:
-        this.vfpitch = VexFlowConverter.pitch(this, drawPitch);
+        this.vfpitch = VexFlowConverter.pitch(drawPitch, this.sourceNote.isRest(), this.clef, this.sourceNote.Notehead);
         //}
         //}
     }
     }
     public Transpose(keyInstruction: KeyInstruction, activeClef: ClefInstruction, halfTones: number, octaveEnum: OctaveEnum): Pitch {
     public Transpose(keyInstruction: KeyInstruction, activeClef: ClefInstruction, halfTones: number, octaveEnum: OctaveEnum): Pitch {
         const tranposedPitch: Pitch = super.Transpose(keyInstruction, activeClef, halfTones, octaveEnum);
         const tranposedPitch: Pitch = super.Transpose(keyInstruction, activeClef, halfTones, octaveEnum);
         const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(tranposedPitch, this.octaveShift);
         const drawPitch: Pitch = OctaveShift.getPitchFromOctaveShift(tranposedPitch, this.octaveShift);
-        this.vfpitch = VexFlowConverter.pitch(this, drawPitch);
+        this.vfpitch = VexFlowConverter.pitch(drawPitch, this.sourceNote.isRest(), this.clef, this.sourceNote.Notehead);
         this.vfpitch[1] = undefined;
         this.vfpitch[1] = undefined;
         return drawPitch;
         return drawPitch;
     }
     }

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

@@ -33,8 +33,9 @@ import {TechnicalInstruction, TechnicalInstructionType} from "../../VoiceData/In
 import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
 import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
 import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
 import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
 import {AutoBeamOptions} from "../../../OpenSheetMusicDisplay/OSMDOptions";
 import {AutoBeamOptions} from "../../../OpenSheetMusicDisplay/OSMDOptions";
-import {NoteType, Arpeggio} from "../../VoiceData";
 import {SkyBottomLineCalculator} from "../SkyBottomLineCalculator";
 import {SkyBottomLineCalculator} from "../SkyBottomLineCalculator";
+import { NoteType } from "../../VoiceData/NoteType";
+import { Arpeggio } from "../../VoiceData/Arpeggio";
 
 
 // type StemmableNote = Vex.Flow.StemmableNote;
 // type StemmableNote = Vex.Flow.StemmableNote;
 
 
@@ -466,10 +467,11 @@ export class VexFlowMeasure extends GraphicalMeasure {
             for (let i: number = 0; i < this.ParentStaffLine.Measures.length; i++) {
             for (let i: number = 0; i < this.ParentStaffLine.Measures.length; i++) {
                 const tempMeasure: GraphicalMeasure = this.ParentStaffLine.Measures[i];
                 const tempMeasure: GraphicalMeasure = this.ParentStaffLine.Measures[i];
                 if (!(tempMeasure instanceof VexFlowMeasure)) {
                 if (!(tempMeasure instanceof VexFlowMeasure)) {
-                    //should never be the case... But check just to be sure
+                    // can happen for MultipleRestMeasures
                     continue;
                     continue;
                 }
                 }
-                if (tempMeasure.MeasureNumber === currentMeasureNumber - 1) {
+                if (tempMeasure.MeasureNumber === currentMeasureNumber - 1 ||
+                    tempMeasure.MeasureNumber + tempMeasure.parentSourceMeasure?.multipleRestMeasures === currentMeasureNumber) {
                     //We found the previous top measure
                     //We found the previous top measure
                     prevMeasure = tempMeasure as VexFlowMeasure;
                     prevMeasure = tempMeasure as VexFlowMeasure;
                 }
                 }
@@ -993,9 +995,14 @@ export class VexFlowMeasure extends GraphicalMeasure {
                       const bracketed: boolean = tuplet.Bracket ||
                       const bracketed: boolean = tuplet.Bracket ||
                         (tuplet.TupletLabelNumber === 3 && this.rules.TripletsBracketed) ||
                         (tuplet.TupletLabelNumber === 3 && this.rules.TripletsBracketed) ||
                         (tuplet.TupletLabelNumber !== 3 && this.rules.TupletsBracketed);
                         (tuplet.TupletLabelNumber !== 3 && this.rules.TupletsBracketed);
+                      let location: number = Vex.Flow.Tuplet.LOCATION_TOP;
+                      if (tuplet.tupletLabelNumberPlacement === PlacementEnum.Below) {
+                          location = Vex.Flow.Tuplet.LOCATION_BOTTOM;
+                      }
                       vftuplets.push(new Vex.Flow.Tuplet( tupletStaveNotes,
                       vftuplets.push(new Vex.Flow.Tuplet( tupletStaveNotes,
                                                           {
                                                           {
                                                             bracketed: bracketed,
                                                             bracketed: bracketed,
+                                                            location: location,
                                                             notes_occupied: notesOccupied,
                                                             notes_occupied: notesOccupied,
                                                             num_notes: tuplet.TupletLabelNumber, //, location: -1, ratioed: true
                                                             num_notes: tuplet.TupletLabelNumber, //, location: -1, ratioed: true
                                                             ratioed: this.rules.TupletsRatioed,
                                                             ratioed: this.rules.TupletsRatioed,

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

@@ -44,8 +44,8 @@ import { GraphicalSlur } from "../GraphicalSlur";
 import { BoundingBox } from "../BoundingBox";
 import { BoundingBox } from "../BoundingBox";
 import { ContinuousDynamicExpression } from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
 import { ContinuousDynamicExpression } from "../../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
 import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
 import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
-import { InstantaneousTempoExpression } from "../../VoiceData/Expressions";
-import { AlignRestOption } from "../../../OpenSheetMusicDisplay";
+import { InstantaneousTempoExpression } from "../../VoiceData/Expressions/InstantaneousTempoExpression";
+import { AlignRestOption } from "../../../OpenSheetMusicDisplay/OSMDOptions";
 import { VexFlowStaffLine } from "./VexFlowStaffLine";
 import { VexFlowStaffLine } from "./VexFlowStaffLine";
 import { EngravingRules } from "../EngravingRules";
 import { EngravingRules } from "../EngravingRules";
 import { VexflowStafflineNoteCalculator } from "./VexflowStafflineNoteCalculator";
 import { VexflowStafflineNoteCalculator } from "./VexflowStafflineNoteCalculator";
@@ -96,7 +96,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           (<VexFlowStaffEntry>staffEntry).calculateXPosition();
           (<VexFlowStaffEntry>staffEntry).calculateXPosition();
         }
         }
         // const t0: number = performance.now();
         // const t0: number = performance.now();
-        if (this.beamsNeedUpdate) { // finalizeBeams takes a few milliseconds, so we can save some performance here
+        if (true || this.beamsNeedUpdate) {
+          // finalizeBeams takes a few milliseconds, so we can save some performance here sometimes,
+          // but we'd have to check for every setting change that would affect beam rendering. See #843
           (measure as VexFlowMeasure).finalizeBeams(); // without this, when zooming a lot (e.g. 250%), beams keep their old, now wrong slope.
           (measure as VexFlowMeasure).finalizeBeams(); // without this, when zooming a lot (e.g. 250%), beams keep their old, now wrong slope.
           // totalFinalizeBeamsTime += performance.now() - t0;
           // totalFinalizeBeamsTime += performance.now() - t0;
           // console.log("Total calls to finalizeBeams in VexFlowMusicSheetCalculator took " + totalFinalizeBeamsTime + " milliseconds.");
           // console.log("Total calls to finalizeBeams in VexFlowMusicSheetCalculator took " + totalFinalizeBeamsTime + " milliseconds.");
@@ -611,14 +613,18 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   }
   }
 
 
   protected createMetronomeMark(metronomeExpression: InstantaneousTempoExpression): void {
   protected createMetronomeMark(metronomeExpression: InstantaneousTempoExpression): void {
-    const vfStave: Vex.Flow.Stave = (this.graphicalMusicSheet.MeasureList[0][0] as VexFlowMeasure).getVFStave();
+    // note: sometimes MeasureNumber is 0 here, e.g. in Christbaum, maybe because of pickup measure (auftakt)
+    const measureNumber: number = Math.max(metronomeExpression.ParentMultiTempoExpression.SourceMeasureParent.MeasureNumber - 1, 0);
+    const staffNumber: number = Math.max(metronomeExpression.StaffNumber - 1, 0);
+    const firstMetronomeMark: boolean = measureNumber === 0 && staffNumber === 0;
+    const vfStave: Vex.Flow.Stave = (this.graphicalMusicSheet.MeasureList[measureNumber][staffNumber] as VexFlowMeasure).getVFStave();
     //vfStave.addModifier(new Vex.Flow.StaveTempo( // needs Vexflow PR
     //vfStave.addModifier(new Vex.Flow.StaveTempo( // needs Vexflow PR
     let vexflowDuration: string = "q";
     let vexflowDuration: string = "q";
     if (metronomeExpression.beatUnit) {
     if (metronomeExpression.beatUnit) {
       const duration: Fraction = NoteTypeHandler.getNoteDurationFromType(metronomeExpression.beatUnit);
       const duration: Fraction = NoteTypeHandler.getNoteDurationFromType(metronomeExpression.beatUnit);
       vexflowDuration = VexFlowConverter.duration(duration, false);
       vexflowDuration = VexFlowConverter.duration(duration, false);
     }
     }
-    // const noteType: NoteType = NoteTypeHandler.StringToNoteType(metronomeExpression.beatUnit);
+
     vfStave.setTempo(
     vfStave.setTempo(
       {
       {
           bpm: metronomeExpression.TempoInBpm,
           bpm: metronomeExpression.TempoInBpm,
@@ -628,9 +634,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
       this.rules.MetronomeMarkYShift * unitInPixels);
       this.rules.MetronomeMarkYShift * unitInPixels);
        // -50, -30), 0); //needs Vexflow PR
        // -50, -30), 0); //needs Vexflow PR
        //.setShiftX(-50);
        //.setShiftX(-50);
-
+    const xShift: number = firstMetronomeMark ? this.rules.MetronomeMarkXShift * unitInPixels : 0;
     (<any>vfStave.getModifiers()[vfStave.getModifiers().length - 1]).setShiftX(
     (<any>vfStave.getModifiers()[vfStave.getModifiers().length - 1]).setShiftX(
-      this.rules.MetronomeMarkXShift * unitInPixels
+      xShift
     );
     );
     // TODO calculate bounding box of metronome mark instead of hacking skyline to fix lyricist collision
     // TODO calculate bounding box of metronome mark instead of hacking skyline to fix lyricist collision
     const skyline: number[] = this.graphicalMusicSheet.MeasureList[0][0].ParentStaffLine.SkyLine;
     const skyline: number[] = this.graphicalMusicSheet.MeasureList[0][0].ParentStaffLine.SkyLine;
@@ -697,11 +703,35 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     if (endMeasure && startStaffLine && endStaffLine) {
     if (endMeasure && startStaffLine && endStaffLine) {
       // calculate GraphicalOctaveShift and RelativePositions
       // calculate GraphicalOctaveShift and RelativePositions
       const graphicalOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, startStaffLine.PositionAndShape);
       const graphicalOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, startStaffLine.PositionAndShape);
-      if (!graphicalOctaveShift.getStartNote()) { // fix for rendering range set
-        graphicalOctaveShift.setStartNote(startMeasure.staffEntries[0]);
+      if (!graphicalOctaveShift.startNote) { // fix for rendering range set
+        let startGse: GraphicalStaffEntry;
+        for (const gse of startMeasure.staffEntries) {
+          if (gse) {
+            startGse = gse;
+            break;
+          } // sometimes the first graphical staff entry is undefined, not sure why.
+        }
+        if (!startGse) {
+          return; // couldn't find a start staffentry, don't draw the octave shift
+        }
+        graphicalOctaveShift.setStartNote(startGse);
+        if (!graphicalOctaveShift.startNote) {
+          return; // couldn't find a start note, don't draw the octave shift
+        }
       }
       }
-      if (!graphicalOctaveShift.getStartNote()) { // fix for rendering range set
-        graphicalOctaveShift.setEndNote(endMeasure.staffEntries.last());
+      if (!graphicalOctaveShift.endNote) { // fix for rendering range set
+        let endGse: GraphicalStaffEntry;
+        for (let i: number = endMeasure.staffEntries.length - 1; i >= 0; i++) {
+          // search backwards from end of measure
+          if (endMeasure.staffEntries[i]) {
+            endGse = endMeasure.staffEntries[i];
+            break;
+          }
+        }
+        graphicalOctaveShift.setEndNote(endGse);
+        if (!graphicalOctaveShift.endNote) {
+          return;
+        }
       }
       }
       startStaffLine.OctaveShifts.push(graphicalOctaveShift);
       startStaffLine.OctaveShifts.push(graphicalOctaveShift);
 
 
@@ -785,7 +815,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     const measures: VexFlowMeasure[] = <VexFlowMeasure[]>this.graphicalMusicSheet.MeasureList[measureIndex];
     const measures: VexFlowMeasure[] = <VexFlowMeasure[]>this.graphicalMusicSheet.MeasureList[measureIndex];
     for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
     for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
       const graphicalMeasure: VexFlowMeasure = measures[idx];
       const graphicalMeasure: VexFlowMeasure = measures[idx];
-      if (graphicalMeasure.ParentStaffLine && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
+      if (graphicalMeasure && graphicalMeasure.ParentStaffLine && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
         uppermostMeasure = <VexFlowMeasure>graphicalMeasure;
         uppermostMeasure = <VexFlowMeasure>graphicalMeasure;
         break;
         break;
       }
       }

+ 4 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -21,7 +21,7 @@ import { GraphicalSlur } from "../GraphicalSlur";
 import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
 import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
 import { GraphicalInstantaneousTempoExpression } from "../GraphicalInstantaneousTempoExpression";
 import { GraphicalInstantaneousTempoExpression } from "../GraphicalInstantaneousTempoExpression";
 import { GraphicalInstantaneousDynamicExpression } from "../GraphicalInstantaneousDynamicExpression";
 import { GraphicalInstantaneousDynamicExpression } from "../GraphicalInstantaneousDynamicExpression";
-import log = require("loglevel");
+import log from "loglevel";
 import { GraphicalContinuousDynamicExpression } from "../GraphicalContinuousDynamicExpression";
 import { GraphicalContinuousDynamicExpression } from "../GraphicalContinuousDynamicExpression";
 import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
 import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
 import { DrawingParameters } from "../DrawingParameters";
 import { DrawingParameters } from "../DrawingParameters";
@@ -425,6 +425,9 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
             const linePosition: PointF2D = new PointF2D(screenPosition.x + xOffsetInPixel, screenPosition.y);
             const linePosition: PointF2D = new PointF2D(screenPosition.x + xOffsetInPixel, screenPosition.y);
             this.backend.renderText(height, fontStyle, font, currLine.text, fontHeightInPixel, linePosition, color, graphicalLabel.Label.fontFamily);
             this.backend.renderText(height, fontStyle, font, currLine.text, fontHeightInPixel, linePosition, color, graphicalLabel.Label.fontFamily);
             screenPosition.y = screenPosition.y + fontHeightInPixel;
             screenPosition.y = screenPosition.y + fontHeightInPixel;
+            if (graphicalLabel.TextLines.length > 1) {
+             screenPosition.y += this.rules.SpacingBetweenTextLines;
+            }
         }
         }
         // font currently unused, replaced by fontFamily
         // font currently unused, replaced by fontFamily
     }
     }

+ 21 - 14
src/MusicalScore/Graphical/VexFlow/VexFlowOctaveShift.ts

@@ -12,9 +12,9 @@ import log from "loglevel";
 export class VexFlowOctaveShift extends GraphicalOctaveShift {
 export class VexFlowOctaveShift extends GraphicalOctaveShift {
 
 
     /** Defines the note where the octave shift starts */
     /** Defines the note where the octave shift starts */
-    private startNote: Vex.Flow.StemmableNote;
+    public startNote: Vex.Flow.StemmableNote;
     /** Defines the note where the octave shift ends */
     /** Defines the note where the octave shift ends */
-    private endNote: Vex.Flow.StemmableNote;
+    public endNote: Vex.Flow.StemmableNote;
     /** Top or bottom of the staffline */
     /** Top or bottom of the staffline */
     private position: Vex.Flow.TextBracket.Positions;
     private position: Vex.Flow.TextBracket.Positions;
     /** Supscript is a smaller text after the regular text (e.g. va after 8) */
     /** Supscript is a smaller text after the regular text (e.g. va after 8) */
@@ -60,24 +60,31 @@ export class VexFlowOctaveShift extends GraphicalOctaveShift {
      * Set a start note using a staff entry
      * Set a start note using a staff entry
      * @param graphicalStaffEntry the staff entry that holds the start note
      * @param graphicalStaffEntry the staff entry that holds the start note
      */
      */
-    public setStartNote(graphicalStaffEntry: GraphicalStaffEntry): void {
-        this.startNote = (graphicalStaffEntry.graphicalVoiceEntries[0] as VexFlowVoiceEntry).vfStaveNote;
+    public setStartNote(graphicalStaffEntry: GraphicalStaffEntry): boolean {
+        for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
+            const vve: VexFlowVoiceEntry = (gve as VexFlowVoiceEntry);
+            if (vve?.vfStaveNote) {
+                this.startNote = vve.vfStaveNote;
+                return true;
+            }
+        }
+        return false; // couldn't find a startNote
     }
     }
 
 
     /**
     /**
      * Set an end note using a staff entry
      * Set an end note using a staff entry
      * @param graphicalStaffEntry the staff entry that holds the end note
      * @param graphicalStaffEntry the staff entry that holds the end note
      */
      */
-    public setEndNote(graphicalStaffEntry: GraphicalStaffEntry): void {
-        this.endNote = (graphicalStaffEntry.graphicalVoiceEntries[0] as VexFlowVoiceEntry).vfStaveNote;
-    }
-
-    public getStartNote(): Vex.Flow.StemmableNote {
-        return this.startNote;
-    }
-
-    public getEndNote(): Vex.Flow.StemmableNote {
-        return this.endNote;
+    public setEndNote(graphicalStaffEntry: GraphicalStaffEntry): boolean {
+        // this is duplicate code from setStartNote, but if we make one general method, we add a lot of branching.
+        for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
+            const vve: VexFlowVoiceEntry = (gve as VexFlowVoiceEntry);
+            if (vve?.vfStaveNote) {
+                this.endNote = vve.vfStaveNote;
+                return true;
+            }
+        }
+        return false; // couldn't find an endNote
     }
     }
 
 
     /**
     /**

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

@@ -3,11 +3,10 @@ import { VoiceEntry } from "../../VoiceData/VoiceEntry";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
 import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
 import { unitInPixels } from "./VexFlowMusicSheetDrawer";
-import { GraphicalNote } from "..";
 import { NoteEnum } from "../../../Common/DataObjects/Pitch";
 import { NoteEnum } from "../../../Common/DataObjects/Pitch";
 import { Note } from "../../VoiceData/Note";
 import { Note } from "../../VoiceData/Note";
 import { ColoringModes } from "./../DrawingParameters";
 import { ColoringModes } from "./../DrawingParameters";
-import { TabNote } from "../../VoiceData/TabNote";
+import { GraphicalNote } from "../GraphicalNote";
 
 
 export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
 export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
     private mVexFlowStaveNote: Vex.Flow.StemmableNote;
     private mVexFlowStaveNote: Vex.Flow.StemmableNote;
@@ -122,7 +121,7 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
                     }
                     }
                 }
                 }
                 // set ledger line color. TODO coordinate this with VexFlowConverter.StaveNote(), where there's also still code for this, maybe unnecessarily.
                 // set ledger line color. TODO coordinate this with VexFlowConverter.StaveNote(), where there's also still code for this, maybe unnecessarily.
-                if (!(note.sourceNote instanceof TabNote)) { // setLedgerLineStyle doesn't exist on TabNote, would throw error.
+                if ((vfStaveNote as any).setLedgerLineStyle) { // setLedgerLineStyle doesn't exist on TabNote or rest, would throw error.
                     if (noteheadColor === transparentColor) {
                     if (noteheadColor === transparentColor) {
                         (vfStaveNote as any).setLedgerLineStyle(
                         (vfStaveNote as any).setLedgerLineStyle(
                             { fillStyle: noteheadColor, strokeStyle: noteheadColor, lineWidth: this.rules.LedgerLineWidth });
                             { fillStyle: noteheadColor, strokeStyle: noteheadColor, lineWidth: this.rules.LedgerLineWidth });

+ 3 - 2
src/MusicalScore/Graphical/VexFlow/VexflowStafflineNoteCalculator.ts

@@ -1,10 +1,11 @@
 import { IStafflineNoteCalculator } from "../../Interfaces/IStafflineNoteCalculator";
 import { IStafflineNoteCalculator } from "../../Interfaces/IStafflineNoteCalculator";
 import { GraphicalNote } from "../GraphicalNote";
 import { GraphicalNote } from "../GraphicalNote";
-import { ClefEnum, StemDirectionType, VoiceEntry } from "../../VoiceData";
-import { Pitch, NoteEnum } from "../../../Common";
+import { Pitch, NoteEnum } from "../../../Common/DataObjects/Pitch";
 import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote";
 import { VexFlowGraphicalNote } from "./VexFlowGraphicalNote";
 import { Dictionary } from "typescript-collections";
 import { Dictionary } from "typescript-collections";
 import { EngravingRules } from "../EngravingRules";
 import { EngravingRules } from "../EngravingRules";
+import { ClefEnum } from "../../VoiceData/Instructions/ClefInstruction";
+import { StemDirectionType, VoiceEntry } from "../../VoiceData/VoiceEntry";
 
 
 export class VexflowStafflineNoteCalculator implements IStafflineNoteCalculator {
 export class VexflowStafflineNoteCalculator implements IStafflineNoteCalculator {
     private rules: EngravingRules;
     private rules: EngravingRules;

+ 1 - 1
src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts

@@ -15,7 +15,7 @@ import { TechnicalInstruction } from "../VoiceData/Instructions/TechnicalInstruc
 import { GraphicalVoiceEntry } from "../Graphical/GraphicalVoiceEntry";
 import { GraphicalVoiceEntry } from "../Graphical/GraphicalVoiceEntry";
 import { VoiceEntry } from "../VoiceData/VoiceEntry";
 import { VoiceEntry } from "../VoiceData/VoiceEntry";
 import { EngravingRules } from "../Graphical/EngravingRules";
 import { EngravingRules } from "../Graphical/EngravingRules";
-import { KeyInstruction } from "../VoiceData";
+import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction";
 
 
 export interface IGraphicalSymbolFactory {
 export interface IGraphicalSymbolFactory {
 
 

+ 6 - 4
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -24,8 +24,8 @@ import {ExpressionReader} from "./MusicSymbolModules/ExpressionReader";
 import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
 import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
 import {SlurReader} from "./MusicSymbolModules/SlurReader";
 import {SlurReader} from "./MusicSymbolModules/SlurReader";
 import {StemDirectionType} from "../VoiceData/VoiceEntry";
 import {StemDirectionType} from "../VoiceData/VoiceEntry";
-import {NoteType, NoteTypeHandler} from "../VoiceData";
-import {SystemLinesEnumHelper} from "../Graphical";
+import {NoteType, NoteTypeHandler} from "../VoiceData/NoteType";
+import { SystemLinesEnumHelper } from "../Graphical/SystemLinesEnum";
 // import {Dictionary} from "typescript-collections";
 // import {Dictionary} from "typescript-collections";
 
 
 // FIXME: The following classes are missing
 // FIXME: The following classes are missing
@@ -134,7 +134,8 @@ export class InstrumentReader {
     let lastNoteWasGrace: boolean = false;
     let lastNoteWasGrace: boolean = false;
     try {
     try {
       const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
       const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
-      for (const xmlNode of xmlMeasureListArr) {
+      for (let xmlNodeIndex: number = 0; xmlNodeIndex < xmlMeasureListArr.length; xmlNodeIndex++) {
+        const xmlNode: IXmlElement = xmlMeasureListArr[xmlNodeIndex];
         if (xmlNode.name === "print") {
         if (xmlNode.name === "print") {
           const newSystemAttr: IXmlAttribute = xmlNode.attribute("new-system");
           const newSystemAttr: IXmlAttribute = xmlNode.attribute("new-system");
           if (newSystemAttr?.value === "yes") {
           if (newSystemAttr?.value === "yes") {
@@ -535,7 +536,8 @@ export class InstrumentReader {
            }
            }
           }
           }
           const location: IXmlAttribute = xmlNode.attribute("location");
           const location: IXmlAttribute = xmlNode.attribute("location");
-          if (location && location.value === "right") {
+          const isEndingBarline: boolean = (xmlNodeIndex === xmlMeasureListArr.length - 1);
+          if (isEndingBarline || (location && location.value === "right")) {
             const stringValue: string = xmlNode.element("bar-style")?.value;
             const stringValue: string = xmlNode.element("bar-style")?.value;
             // TODO apparently we didn't anticipate bar-style not existing (the ? above was missing). how to handle?
             // TODO apparently we didn't anticipate bar-style not existing (the ? above was missing). how to handle?
             if (stringValue) {
             if (stringValue) {

+ 3 - 2
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -21,7 +21,7 @@ import {MusicSymbolModuleFactory} from "./MusicSymbolModuleFactory";
 import {IAfterSheetReadingModule} from "../Interfaces/IAfterSheetReadingModule";
 import {IAfterSheetReadingModule} from "../Interfaces/IAfterSheetReadingModule";
 import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
 import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
 import {RepetitionCalculator} from "./MusicSymbolModules/RepetitionCalculator";
 import {RepetitionCalculator} from "./MusicSymbolModules/RepetitionCalculator";
-import {EngravingRules} from "../Graphical";
+import {EngravingRules} from "../Graphical/EngravingRules";
 
 
 export class MusicSheetReader /*implements IMusicSheetReader*/ {
 export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
 
@@ -147,7 +147,8 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         }
         }
 
 
         while (couldReadMeasure) {
         while (couldReadMeasure) {
-            if (this.currentMeasure !== undefined && this.currentMeasure.HasEndLine) {
+            // TODO changing this.rules.PartAndSystemAfterFinalBarline requires a reload of the piece for measure numbers to be updated
+            if (this.currentMeasure !== undefined && this.currentMeasure.HasEndLine && this.rules.NewPartAndSystemAfterFinalBarline) {
                 sourceMeasureCounter = 0;
                 sourceMeasureCounter = 0;
             }
             }
             this.currentMeasure = new SourceMeasure(this.completeNumberOfStaves, this.musicSheet.Rules);
             this.currentMeasure = new SourceMeasure(this.completeNumberOfStaves, this.musicSheet.Rules);

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

@@ -188,6 +188,13 @@ export class ArticulationReader {
         const node: IXmlElement = ornamentsNode.element(ornamentElement);
         const node: IXmlElement = ornamentsNode.element(ornamentElement);
         if (node) {
         if (node) {
           ornament = new OrnamentContainer(elementToOrnamentEnum[ornamentElement]);
           ornament = new OrnamentContainer(elementToOrnamentEnum[ornamentElement]);
+          const placementAttr: Attr = node.attribute("placement");
+          if (placementAttr) {
+            const placementString: string = placementAttr.value;
+            if (placementString === "below") {
+              ornament.placement = PlacementEnum.Below;
+            }
+          }
         }
         }
       }
       }
       if (ornament) {
       if (ornament) {

+ 18 - 5
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -16,6 +16,7 @@ import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
 import {TextAlignmentEnum} from "../../../Common/Enums/TextAlignment";
 import {TextAlignmentEnum} from "../../../Common/Enums/TextAlignment";
 import {ITextTranslation} from "../../Interfaces/ITextTranslation";
 import {ITextTranslation} from "../../Interfaces/ITextTranslation";
 import log from "loglevel";
 import log from "loglevel";
+import { FontStyles } from "../../../Common/Enums/FontStyles";
 
 
 export class ExpressionReader {
 export class ExpressionReader {
     private musicSheet: MusicSheet;
     private musicSheet: MusicSheet;
@@ -350,6 +351,14 @@ export class ExpressionReader {
     }
     }
     private interpretWords(wordsNode: IXmlElement, currentMeasure: SourceMeasure, inSourceMeasureCurrentFraction: Fraction): void {
     private interpretWords(wordsNode: IXmlElement, currentMeasure: SourceMeasure, inSourceMeasureCurrentFraction: Fraction): void {
         const text: string = wordsNode.value;
         const text: string = wordsNode.value;
+        let fontStyle: FontStyles;
+        const fontStyleAttr: Attr = wordsNode.attribute("font-style");
+        if (fontStyleAttr) {
+            const fontStyleText: string = fontStyleAttr.value;
+            if (fontStyleText === "italic") {
+                fontStyle = FontStyles.Italic;
+            }
+        }
         if (text.length > 0) {
         if (text.length > 0) {
             if (wordsNode.hasAttributes && wordsNode.attribute("default-x")) {
             if (wordsNode.hasAttributes && wordsNode.attribute("default-x")) {
                 this.directionTimestamp = Fraction.createFromFraction(inSourceMeasureCurrentFraction);
                 this.directionTimestamp = Fraction.createFromFraction(inSourceMeasureCurrentFraction);
@@ -357,7 +366,7 @@ export class ExpressionReader {
             if (this.checkIfWordsNodeIsRepetitionInstruction(text)) {
             if (this.checkIfWordsNodeIsRepetitionInstruction(text)) {
                 return;
                 return;
             }
             }
-            this.fillMultiOrTempoExpression(text, currentMeasure);
+            this.fillMultiOrTempoExpression(text, currentMeasure, fontStyle);
             this.initialize();
             this.initialize();
         }
         }
     }
     }
@@ -423,7 +432,7 @@ export class ExpressionReader {
             }
             }
         }
         }
     }
     }
-    private fillMultiOrTempoExpression(inputString: string, currentMeasure: SourceMeasure): void {
+    private fillMultiOrTempoExpression(inputString: string, currentMeasure: SourceMeasure, fontStyle: FontStyles): void {
         if (!inputString) {
         if (!inputString) {
             return;
             return;
         }
         }
@@ -432,7 +441,7 @@ export class ExpressionReader {
         //const splitStrings: string[] = tmpInputString.split(/([\s,\r\n]and[\s,\r\n]|[\s,\r\n]und[\s,\r\n]|[\s,\r\n]e[\s,\r\n]|[\s,\r\n])+/g);
         //const splitStrings: string[] = tmpInputString.split(/([\s,\r\n]and[\s,\r\n]|[\s,\r\n]und[\s,\r\n]|[\s,\r\n]e[\s,\r\n]|[\s,\r\n])+/g);
 
 
         //for (const splitStr of splitStrings) {
         //for (const splitStr of splitStrings) {
-        this.createExpressionFromString("", tmpInputString, currentMeasure, inputString);
+        this.createExpressionFromString("", tmpInputString, currentMeasure, inputString, fontStyle);
         //}
         //}
     }
     }
     /*
     /*
@@ -464,7 +473,8 @@ export class ExpressionReader {
     }
     }
     */
     */
     private createExpressionFromString(prefix: string, stringTrimmed: string,
     private createExpressionFromString(prefix: string, stringTrimmed: string,
-                                       currentMeasure: SourceMeasure, inputString: string): boolean {
+                                       currentMeasure: SourceMeasure, inputString: string,
+                                       fontStyle: FontStyles): boolean {
         if (InstantaneousTempoExpression.isInputStringInstantaneousTempo(stringTrimmed) ||
         if (InstantaneousTempoExpression.isInputStringInstantaneousTempo(stringTrimmed) ||
             ContinuousTempoExpression.isInputStringContinuousTempo(stringTrimmed)) {
             ContinuousTempoExpression.isInputStringContinuousTempo(stringTrimmed)) {
             // first check if there is already a tempo expression with the same function
             // first check if there is already a tempo expression with the same function
@@ -478,7 +488,7 @@ export class ExpressionReader {
                     }
                     }
                 }
                 }
             }
             }
-            this.createNewTempoExpressionIfNeeded(currentMeasure);
+            this.createNewTempoExpressionIfNeeded(currentMeasure); // TODO process fontStyle? (also for other expressions)
             this.currentMultiTempoExpression.CombinedExpressionsText = inputString;
             this.currentMultiTempoExpression.CombinedExpressionsText = inputString;
             if (InstantaneousTempoExpression.isInputStringInstantaneousTempo(stringTrimmed)) {
             if (InstantaneousTempoExpression.isInputStringInstantaneousTempo(stringTrimmed)) {
                 const instantaneousTempoExpression: InstantaneousTempoExpression = new InstantaneousTempoExpression(  stringTrimmed,
                 const instantaneousTempoExpression: InstantaneousTempoExpression = new InstantaneousTempoExpression(  stringTrimmed,
@@ -537,7 +547,9 @@ export class ExpressionReader {
         }
         }
         if (MoodExpression.isInputStringMood(stringTrimmed)) {
         if (MoodExpression.isInputStringMood(stringTrimmed)) {
             this.createNewMultiExpressionIfNeeded(currentMeasure);
             this.createNewMultiExpressionIfNeeded(currentMeasure);
+            currentMeasure.hasMoodExpressions = true;
             const moodExpression: MoodExpression = new MoodExpression(stringTrimmed, this.placement, this.staffNumber);
             const moodExpression: MoodExpression = new MoodExpression(stringTrimmed, this.placement, this.staffNumber);
+            moodExpression.fontStyle = fontStyle;
             this.getMultiExpression.addExpression(moodExpression, prefix);
             this.getMultiExpression.addExpression(moodExpression, prefix);
             return true;
             return true;
         }
         }
@@ -570,6 +582,7 @@ export class ExpressionReader {
         }
         }
         const unknownExpression: UnknownExpression = new UnknownExpression(
         const unknownExpression: UnknownExpression = new UnknownExpression(
             stringTrimmed, this.placement, textAlignment, this.staffNumber);
             stringTrimmed, this.placement, textAlignment, this.staffNumber);
+        unknownExpression.fontStyle = fontStyle;
         this.getMultiExpression.addExpression(unknownExpression, prefix);
         this.getMultiExpression.addExpression(unknownExpression, prefix);
 
 
         return false;
         return false;

+ 27 - 15
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -30,6 +30,7 @@ import { Notehead } from "../VoiceData/Notehead";
 import { Arpeggio, ArpeggioType } from "../VoiceData/Arpeggio";
 import { Arpeggio, ArpeggioType } from "../VoiceData/Arpeggio";
 import { NoteType, NoteTypeHandler } from "../VoiceData/NoteType";
 import { NoteType, NoteTypeHandler } from "../VoiceData/NoteType";
 import { TabNote } from "../VoiceData/TabNote";
 import { TabNote } from "../VoiceData/TabNote";
+import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
 
 
 export class VoiceGenerator {
 export class VoiceGenerator {
   constructor(instrument: Instrument, voiceId: number, slurReader: SlurReader, mainVoice: Voice = undefined) {
   constructor(instrument: Instrument, voiceId: number, slurReader: SlurReader, mainVoice: Voice = undefined) {
@@ -114,9 +115,10 @@ export class VoiceGenerator {
     this.currentStaffEntry = parentStaffEntry;
     this.currentStaffEntry = parentStaffEntry;
     this.currentMeasure = parentMeasure;
     this.currentMeasure = parentMeasure;
     //log.debug("read called:", restNote);
     //log.debug("read called:", restNote);
+
     try {
     try {
       this.currentNote = restNote
       this.currentNote = restNote
-        ? this.addRestNote(noteDuration, noteTypeXml, printObject, isCueNote, noteheadColorXml)
+        ? this.addRestNote(noteNode.element("rest"), noteDuration, noteTypeXml, printObject, isCueNote, noteheadColorXml)
         : this.addSingleNote(noteNode, noteDuration, noteTypeXml, typeDuration, normalNotes, chord, guitarPro,
         : this.addSingleNote(noteNode, noteDuration, noteTypeXml, typeDuration, normalNotes, chord, guitarPro,
                              printObject, isCueNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml, vibratoStrokes);
                              printObject, isCueNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml, vibratoStrokes);
       // read lyrics
       // read lyrics
@@ -271,7 +273,7 @@ export class VoiceGenerator {
       if (openTieDict.hasOwnProperty(key)) {
       if (openTieDict.hasOwnProperty(key)) {
         const tie: Tie = openTieDict[key];
         const tie: Tie = openTieDict[key];
         if (Fraction.plus(tie.StartNote.ParentStaffEntry.Timestamp, tie.Duration)
         if (Fraction.plus(tie.StartNote.ParentStaffEntry.Timestamp, tie.Duration)
-          .lt(tie.StartNote.ParentStaffEntry.VerticalContainerParent.ParentMeasure.Duration)) {
+          .lt(tie.StartNote.SourceMeasure.Duration)) {
           delete openTieDict[key];
           delete openTieDict[key];
         }
         }
       }
       }
@@ -439,10 +441,11 @@ export class VoiceGenerator {
 
 
     if (stringNumber < 0 || fretNumber < 0) {
     if (stringNumber < 0 || fretNumber < 0) {
       // create normal Note
       // create normal Note
-      note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
+      note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch, this.currentMeasure);
     } else {
     } else {
       // create TabNote
       // create TabNote
-      note = new TabNote(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch, stringNumber, fretNumber, bends, vibratoStrokes);
+      note = new TabNote(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch, this.currentMeasure,
+                         stringNumber, fretNumber, bends, vibratoStrokes);
     }
     }
 
 
     note.TypeLength = typeDuration;
     note.TypeLength = typeDuration;
@@ -477,9 +480,17 @@ export class VoiceGenerator {
    * @param divisions
    * @param divisions
    * @returns {Note}
    * @returns {Note}
    */
    */
-  private addRestNote(noteDuration: Fraction, noteTypeXml: NoteType, printObject: boolean, isCueNote: boolean, noteheadColorXml: string): Note {
+  private addRestNote(node: IXmlElement, noteDuration: Fraction, noteTypeXml: NoteType,
+                      printObject: boolean, isCueNote: boolean, noteheadColorXml: string): Note {
     const restFraction: Fraction = Fraction.createFromFraction(noteDuration);
     const restFraction: Fraction = Fraction.createFromFraction(noteDuration);
-    const restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, undefined);
+    const displayStep: IXmlElement = node.element("display-step");
+    const octave: IXmlElement = node.element("display-octave");
+    let pitch: Pitch = undefined;
+    if (displayStep && octave) {
+        const noteStep: NoteEnum = NoteEnum[displayStep.value.toUpperCase()];
+        pitch = new Pitch(noteStep, parseInt(octave.value, 10), AccidentalEnum.NONE);
+    }
+    const restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, pitch, this.currentMeasure, true);
     restNote.NoteTypeXml = noteTypeXml;
     restNote.NoteTypeXml = noteTypeXml;
     restNote.PrintObject = printObject;
     restNote.PrintObject = printObject;
     restNote.IsCueNote = isCueNote;
     restNote.IsCueNote = isCueNote;
@@ -593,6 +604,7 @@ export class VoiceGenerator {
    */
    */
   private addTuplet(node: IXmlElement, tupletNodeList: IXmlElement[]): number {
   private addTuplet(node: IXmlElement, tupletNodeList: IXmlElement[]): number {
     let bracketed: boolean = false; // xml bracket attribute value
     let bracketed: boolean = false; // xml bracket attribute value
+    // TODO refactor this to not duplicate lots of code for the cases tupletNodeList.length == 1 and > 1
     if (tupletNodeList !== undefined && tupletNodeList.length > 1) {
     if (tupletNodeList !== undefined && tupletNodeList.length > 1) {
       let timeModNode: IXmlElement = node.element("time-modification");
       let timeModNode: IXmlElement = node.element("time-modification");
       if (timeModNode) {
       if (timeModNode) {
@@ -606,6 +618,8 @@ export class VoiceGenerator {
           if (bracketAttr && bracketAttr.value === "yes") {
           if (bracketAttr && bracketAttr.value === "yes") {
             bracketed = true;
             bracketed = true;
           }
           }
+          const placementAttr: Attr = tupletNode.attribute("placement");
+          const placementBelow: boolean = placementAttr && placementAttr.value === "below";
           const type: Attr = tupletNode.attribute("type");
           const type: Attr = tupletNode.attribute("type");
           if (type && type.value === "start") {
           if (type && type.value === "start") {
             let tupletNumber: number = 1;
             let tupletNumber: number = 1;
@@ -625,6 +639,7 @@ export class VoiceGenerator {
 
 
             }
             }
             const tuplet: Tuplet = new Tuplet(tupletLabelNumber, bracketed);
             const tuplet: Tuplet = new Tuplet(tupletLabelNumber, bracketed);
+            tuplet.tupletLabelNumberPlacement = placementBelow ? PlacementEnum.Below : PlacementEnum.Above;
             if (this.tupletDict[tupletNumber]) {
             if (this.tupletDict[tupletNumber]) {
               delete this.tupletDict[tupletNumber];
               delete this.tupletDict[tupletNumber];
               if (Object.keys(this.tupletDict).length === 0) {
               if (Object.keys(this.tupletDict).length === 0) {
@@ -676,6 +691,8 @@ export class VoiceGenerator {
         if (bracketAttr && bracketAttr.value === "yes") {
         if (bracketAttr && bracketAttr.value === "yes") {
           bracketed = true;
           bracketed = true;
         }
         }
+        const placementAttr: Attr = n.attribute("placement");
+        const placementBelow: boolean = placementAttr && placementAttr.value === "below";
 
 
         if (type === "start") {
         if (type === "start") {
           let tupletLabelNumber: number = 0;
           let tupletLabelNumber: number = 0;
@@ -701,6 +718,7 @@ export class VoiceGenerator {
           let tuplet: Tuplet = this.tupletDict[tupletnumber];
           let tuplet: Tuplet = this.tupletDict[tupletnumber];
           if (!tuplet) {
           if (!tuplet) {
             tuplet = this.tupletDict[tupletnumber] = new Tuplet(tupletLabelNumber, bracketed);
             tuplet = this.tupletDict[tupletnumber] = new Tuplet(tupletLabelNumber, bracketed);
+            tuplet.tupletLabelNumberPlacement = placementBelow ? PlacementEnum.Below : PlacementEnum.Above;
           }
           }
           const subnotelist: Note[] = [];
           const subnotelist: Note[] = [];
           subnotelist.push(this.currentNote);
           subnotelist.push(this.currentNote);
@@ -792,9 +810,6 @@ export class VoiceGenerator {
               const tie: Tie = this.openTieDict[tieNumber];
               const tie: Tie = this.openTieDict[tieNumber];
               if (tie) {
               if (tie) {
                 tie.AddNote(this.currentNote);
                 tie.AddNote(this.currentNote);
-                if (maxTieNoteFraction.lt(Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length))) {
-                  maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
-                }
                 delete this.openTieDict[tieNumber];
                 delete this.openTieDict[tieNumber];
               }
               }
             }
             }
@@ -809,9 +824,6 @@ export class VoiceGenerator {
         if (tieNumber >= 0) {
         if (tieNumber >= 0) {
           const tie: Tie = this.openTieDict[tieNumber];
           const tie: Tie = this.openTieDict[tieNumber];
           tie.AddNote(this.currentNote);
           tie.AddNote(this.currentNote);
-          if (maxTieNoteFraction.lt(Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length))) {
-            maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
-          }
         }
         }
       }
       }
     }
     }
@@ -848,10 +860,10 @@ export class VoiceGenerator {
         const tieTabNote: TabNote = tie.Notes[0] as TabNote;
         const tieTabNote: TabNote = tie.Notes[0] as TabNote;
         const tieCandidateNote: TabNote = candidateNote as TabNote;
         const tieCandidateNote: TabNote = candidateNote as TabNote;
         if (tie.Pitch.FundamentalNote === candidateNote.Pitch.FundamentalNote && tie.Pitch.Octave === candidateNote.Pitch.Octave) {
         if (tie.Pitch.FundamentalNote === candidateNote.Pitch.FundamentalNote && tie.Pitch.Octave === candidateNote.Pitch.Octave) {
-          return +key;
-        } else {
+          return parseInt(key, 10);
+        } else if (tieTabNote.StringNumber !== undefined) {
           if (tieTabNote.StringNumber === tieCandidateNote.StringNumber) {
           if (tieTabNote.StringNumber === tieCandidateNote.StringNumber) {
-            return +key;
+            return parseInt(key, 10);
           }
           }
         }
         }
       }
       }

+ 1 - 1
src/MusicalScore/VoiceData/ChordSymbolContainer.ts

@@ -2,7 +2,7 @@ import {Pitch} from "../../Common/DataObjects/Pitch";
 import {KeyInstruction} from "./Instructions/KeyInstruction";
 import {KeyInstruction} from "./Instructions/KeyInstruction";
 import {MusicSheetCalculator} from "../Graphical/MusicSheetCalculator";
 import {MusicSheetCalculator} from "../Graphical/MusicSheetCalculator";
 import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
 import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
-import { EngravingRules } from "../Graphical";
+import { EngravingRules } from "../Graphical/EngravingRules";
 
 
 export class ChordSymbolContainer {
 export class ChordSymbolContainer {
     private rootPitch: Pitch;
     private rootPitch: Pitch;

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

@@ -1,4 +1,5 @@
 import {PlacementEnum, AbstractExpression} from "./AbstractExpression";
 import {PlacementEnum, AbstractExpression} from "./AbstractExpression";
+import { FontStyles } from "../../../Common/Enums/FontStyles";
 
 
 export class MoodExpression extends AbstractExpression {
 export class MoodExpression extends AbstractExpression {
     constructor(label: string, placement: PlacementEnum, staffNumber: number) {
     constructor(label: string, placement: PlacementEnum, staffNumber: number) {
@@ -46,6 +47,7 @@ export class MoodExpression extends AbstractExpression {
     private moodType: MoodEnum;
     private moodType: MoodEnum;
     private label: string;
     private label: string;
     private staffNumber: number;
     private staffNumber: number;
+    public fontStyle: FontStyles;
 
 
     public static isInputStringMood(inputString: string): boolean {
     public static isInputStringMood(inputString: string): boolean {
         if (!inputString) {
         if (!inputString) {

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

@@ -119,7 +119,8 @@ export class MultiExpression {
            } else if (this.expressions[0].expression instanceof MoodExpression) {
            } else if (this.expressions[0].expression instanceof MoodExpression) {
             fontStyle = FontStyles.Italic;
             fontStyle = FontStyles.Italic;
            } else if (this.expressions[0].expression instanceof UnknownExpression) {
            } else if (this.expressions[0].expression instanceof UnknownExpression) {
-            fontStyle = FontStyles.Regular;
+            const unknownExpression: UnknownExpression = (this.expressions[0].expression as UnknownExpression);
+            fontStyle = unknownExpression.fontStyle ?? FontStyles.Regular;
            }
            }
        }
        }
        return fontStyle;
        return fontStyle;

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

@@ -1,5 +1,6 @@
-import {PlacementEnum, AbstractExpression} from "./AbstractExpression";
-import {TextAlignmentEnum} from "../../../Common/Enums/TextAlignment";
+import { PlacementEnum, AbstractExpression } from "./AbstractExpression";
+import { TextAlignmentEnum } from "../../../Common/Enums/TextAlignment";
+import { FontStyles } from "../../../Common/Enums/FontStyles";
 
 
 export class UnknownExpression extends AbstractExpression {
 export class UnknownExpression extends AbstractExpression {
     constructor(label: string, placement: PlacementEnum, textAlignment: TextAlignmentEnum, staffNumber: number) {
     constructor(label: string, placement: PlacementEnum, textAlignment: TextAlignmentEnum, staffNumber: number) {
@@ -14,6 +15,7 @@ export class UnknownExpression extends AbstractExpression {
     private label: string;
     private label: string;
     private textAlignment: TextAlignmentEnum;
     private textAlignment: TextAlignmentEnum;
     private staffNumber: number;
     private staffNumber: number;
+    public fontStyle: FontStyles;
 
 
     public get Label(): string {
     public get Label(): string {
         return this.label;
         return this.label;

+ 15 - 5
src/MusicalScore/VoiceData/Note.ts

@@ -11,17 +11,20 @@ import {NoteState} from "../Graphical/DrawingEnums";
 import {Notehead} from "./Notehead";
 import {Notehead} from "./Notehead";
 import {Arpeggio} from "./Arpeggio";
 import {Arpeggio} from "./Arpeggio";
 import {NoteType} from "./NoteType";
 import {NoteType} from "./NoteType";
+import { SourceMeasure } from "./SourceMeasure";
 
 
 /**
 /**
  * Represents a single pitch with a duration (length)
  * Represents a single pitch with a duration (length)
  */
  */
 export class Note {
 export class Note {
 
 
-    constructor(voiceEntry: VoiceEntry, parentStaffEntry: SourceStaffEntry, length: Fraction, pitch: Pitch) {
+    constructor(voiceEntry: VoiceEntry, parentStaffEntry: SourceStaffEntry, length: Fraction, pitch: Pitch, sourceMeasure: SourceMeasure, isRest?: boolean) {
         this.voiceEntry = voiceEntry;
         this.voiceEntry = voiceEntry;
         this.parentStaffEntry = parentStaffEntry;
         this.parentStaffEntry = parentStaffEntry;
         this.length = length;
         this.length = length;
         this.pitch = pitch;
         this.pitch = pitch;
+        this.sourceMeasure = sourceMeasure;
+        this.isRestFlag = isRest ?? false;
         if (pitch) {
         if (pitch) {
             this.halfTone = pitch.getHalfTone();
             this.halfTone = pitch.getHalfTone();
         } else {
         } else {
@@ -37,16 +40,21 @@ export class Note {
     private voiceEntry: VoiceEntry;
     private voiceEntry: VoiceEntry;
     private parentStaffEntry: SourceStaffEntry;
     private parentStaffEntry: SourceStaffEntry;
     private length: Fraction;
     private length: Fraction;
+    private sourceMeasure: SourceMeasure;
     /** The length/duration given in the <type> tag. different from length for tuplets/tremolos. */
     /** The length/duration given in the <type> tag. different from length for tuplets/tremolos. */
     private typeLength: Fraction;
     private typeLength: Fraction;
     /** The NoteType given in the XML, e.g. quarter, which can be a normal quarter or tuplet quarter -> can have different length/fraction */
     /** The NoteType given in the XML, e.g. quarter, which can be a normal quarter or tuplet quarter -> can have different length/fraction */
     private noteTypeXml: NoteType;
     private noteTypeXml: NoteType;
     /** The amount of notes the tuplet of this note (if there is one) replaces. */
     /** The amount of notes the tuplet of this note (if there is one) replaces. */
     private normalNotes: number;
     private normalNotes: number;
+    private isRestFlag: boolean;
     /**
     /**
      * The untransposed (!!!) source data.
      * The untransposed (!!!) source data.
      */
      */
     private pitch: Pitch;
     private pitch: Pitch;
+    public get NoteAsString(): string {
+        return this.pitch.toString();
+    }
     private beam: Beam;
     private beam: Beam;
     private tuplet: Tuplet;
     private tuplet: Tuplet;
     private tie: Tie;
     private tie: Tie;
@@ -102,6 +110,9 @@ export class Note {
     public set Length(value: Fraction) {
     public set Length(value: Fraction) {
         this.length = value;
         this.length = value;
     }
     }
+    public get SourceMeasure(): SourceMeasure {
+        return this.sourceMeasure;
+    }
     public get TypeLength(): Fraction {
     public get TypeLength(): Fraction {
         return this.typeLength;
         return this.typeLength;
     }
     }
@@ -216,16 +227,15 @@ export class Note {
     }
     }
 
 
     public isRest(): boolean {
     public isRest(): boolean {
-        return this.Pitch === undefined || this.Pitch === null;
+        return this.isRestFlag;
     }
     }
 
 
     /** Note: May be dangerous to use if ParentStaffEntry.VerticalContainerParent etc is not set.
     /** Note: May be dangerous to use if ParentStaffEntry.VerticalContainerParent etc is not set.
      * better calculate this directly when you have access to the note's measure.
      * better calculate this directly when you have access to the note's measure.
      * whole rest: length = measure length. (4/4 in a 4/4 time signature, 3/4 in a 3/4 time signature, 1/4 in a 1/4 time signature, etc.)
      * whole rest: length = measure length. (4/4 in a 4/4 time signature, 3/4 in a 3/4 time signature, 1/4 in a 1/4 time signature, etc.)
-     * TODO give a Note a reference to its measure?
      */
      */
     public isWholeRest(): boolean {
     public isWholeRest(): boolean {
-        return this.isRest() && this.Length.RealValue === this.ParentStaffEntry.VerticalContainerParent.ParentMeasure.ActiveTimeSignature.RealValue;
+        return this.isRest() && this.Length.RealValue === this.sourceMeasure.ActiveTimeSignature.RealValue;
     }
     }
 
 
     public ToString(): string {
     public ToString(): string {
@@ -238,7 +248,7 @@ export class Note {
     public getAbsoluteTimestamp(): Fraction {
     public getAbsoluteTimestamp(): Fraction {
         return Fraction.plus(
         return Fraction.plus(
             this.voiceEntry.Timestamp,
             this.voiceEntry.Timestamp,
-            this.parentStaffEntry.VerticalContainerParent.ParentMeasure.AbsoluteTimestamp
+            this.sourceMeasure.AbsoluteTimestamp
         );
         );
     }
     }
     public checkForDoubleSlur(slur: Slur): boolean {
     public checkForDoubleSlur(slur: Slur): boolean {

+ 3 - 1
src/MusicalScore/VoiceData/OrnamentContainer.ts

@@ -1,4 +1,5 @@
-import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
+import { AccidentalEnum } from "../../Common/DataObjects/Pitch";
+import { PlacementEnum } from "./Expressions/AbstractExpression";
 
 
 export class OrnamentContainer {
 export class OrnamentContainer {
 
 
@@ -7,6 +8,7 @@ export class OrnamentContainer {
     }
     }
 
 
     private ornament: OrnamentEnum;
     private ornament: OrnamentEnum;
+    public placement: PlacementEnum = PlacementEnum.Above;
     private accidentalAbove: AccidentalEnum = AccidentalEnum.NONE;
     private accidentalAbove: AccidentalEnum = AccidentalEnum.NONE;
     private accidentalBelow: AccidentalEnum = AccidentalEnum.NONE;
     private accidentalBelow: AccidentalEnum = AccidentalEnum.NONE;
 
 

+ 36 - 2
src/MusicalScore/VoiceData/SourceMeasure.ts

@@ -8,10 +8,13 @@ import {Voice} from "./Voice";
 import {MusicSheet} from "../MusicSheet";
 import {MusicSheet} from "../MusicSheet";
 import {MultiExpression} from "./Expressions/MultiExpression";
 import {MultiExpression} from "./Expressions/MultiExpression";
 import {MultiTempoExpression} from "./Expressions/MultiTempoExpression";
 import {MultiTempoExpression} from "./Expressions/MultiTempoExpression";
-import {KeyInstruction} from "./Instructions/KeyInstruction";
 import {AbstractNotationInstruction} from "./Instructions/AbstractNotationInstruction";
 import {AbstractNotationInstruction} from "./Instructions/AbstractNotationInstruction";
+import {ClefInstruction} from "./Instructions/ClefInstruction";
+import {KeyInstruction} from "./Instructions/KeyInstruction";
 import {Repetition} from "../MusicSource/Repetition";
 import {Repetition} from "../MusicSource/Repetition";
-import {GraphicalMeasure, SystemLinesEnum, EngravingRules} from "../Graphical";
+import {SystemLinesEnum} from "../Graphical/SystemLinesEnum";
+import {EngravingRules} from "../Graphical/EngravingRules";
+import {GraphicalMeasure} from "../Graphical/GraphicalMeasure";
 //import {BaseIdClass} from "../../Util/BaseIdClass"; // SourceMeasure originally extended BaseIdClass, but ids weren't used.
 //import {BaseIdClass} from "../../Util/BaseIdClass"; // SourceMeasure originally extended BaseIdClass, but ids weren't used.
 
 
 /**
 /**
@@ -23,6 +26,7 @@ export class SourceMeasure {
      * The data entries and data lists will be filled with null values according to the total number of staves,
      * The data entries and data lists will be filled with null values according to the total number of staves,
      * so that existing objects can be referred to by staff index.
      * so that existing objects can be referred to by staff index.
      * @param completeNumberOfStaves
      * @param completeNumberOfStaves
+     * @param rules
      */
      */
     constructor(completeNumberOfStaves: number, rules: EngravingRules) {
     constructor(completeNumberOfStaves: number, rules: EngravingRules) {
         this.completeNumberOfStaves = completeNumberOfStaves;
         this.completeNumberOfStaves = completeNumberOfStaves;
@@ -62,6 +66,14 @@ export class SourceMeasure {
     private duration: Fraction;
     private duration: Fraction;
     private activeTimeSignature: Fraction;
     private activeTimeSignature: Fraction;
     public hasLyrics: boolean = false;
     public hasLyrics: boolean = false;
+    public hasMoodExpressions: boolean = false;
+    /** Whether the SourceMeasure only has rests, no other entries.
+     *  Not the same as GraphicalMeasure.hasOnlyRests, because one SourceMeasure can have many GraphicalMeasures (staffs).
+     */
+    public allRests: boolean = false;
+    public isReducedToMultiRest: boolean = false;
+    /** If this measure is a MultipleRestMeasure, this is the number of the measure in that sequence of measures. */
+    public multipleRestMeasureNumber: number = 0;
     private staffLinkedExpressions: MultiExpression[][] = [];
     private staffLinkedExpressions: MultiExpression[][] = [];
     private tempoExpressions: MultiTempoExpression[] = [];
     private tempoExpressions: MultiTempoExpression[] = [];
     private verticalSourceStaffEntryContainers: VerticalSourceStaffEntryContainer[] = [];
     private verticalSourceStaffEntryContainers: VerticalSourceStaffEntryContainer[] = [];
@@ -569,4 +581,26 @@ export class SourceMeasure {
         }
         }
         return entry;
         return entry;
     }
     }
+
+    public canBeReducedToMultiRest(): boolean {
+        if (!this.allRests || this.hasLyrics || this.hasMoodExpressions || this.tempoExpressions.length > 0) {
+            return false;
+        }
+        // check for StaffLinkedExpressions (e.g. MultiExpression, StaffText) (per staff)
+        for (const multiExpressions of this.staffLinkedExpressions) {
+            if (multiExpressions.length > 0) {
+                return false;
+            }
+        }
+        // check for clef instruction for next measure
+        for (const lastStaffEntry of this.lastInstructionsStaffEntries) {
+            for (let idx: number = 0, len: number = lastStaffEntry?.Instructions.length; idx < len; ++idx) {
+                const abstractNotationInstruction: AbstractNotationInstruction = lastStaffEntry.Instructions[idx];
+                if (abstractNotationInstruction instanceof ClefInstruction) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
 }
 }

+ 3 - 2
src/MusicalScore/VoiceData/TabNote.ts

@@ -3,12 +3,13 @@ import { Fraction } from "../../Common/DataObjects/Fraction";
 import { VoiceEntry } from "./VoiceEntry";
 import { VoiceEntry } from "./VoiceEntry";
 import { SourceStaffEntry } from "./SourceStaffEntry";
 import { SourceStaffEntry } from "./SourceStaffEntry";
 import { Pitch } from "../../Common/DataObjects/Pitch";
 import { Pitch } from "../../Common/DataObjects/Pitch";
+import { SourceMeasure } from "./SourceMeasure";
 
 
 export class TabNote extends Note {
 export class TabNote extends Note {
-    constructor(voiceEntry: VoiceEntry, parentStaffEntry: SourceStaffEntry, length: Fraction, pitch: Pitch,
+    constructor(voiceEntry: VoiceEntry, parentStaffEntry: SourceStaffEntry, length: Fraction, pitch: Pitch, sourceMeasure: SourceMeasure,
                 stringNumber: number, fretNumber: number, bendArray: { bendalter: number, direction: string }[],
                 stringNumber: number, fretNumber: number, bendArray: { bendalter: number, direction: string }[],
                 vibratoStroke: boolean) {
                 vibratoStroke: boolean) {
-        super(voiceEntry, parentStaffEntry, length, pitch);
+        super(voiceEntry, parentStaffEntry, length, pitch, sourceMeasure);
         this.stringNumber = stringNumber;
         this.stringNumber = stringNumber;
         this.fretNumber = fretNumber;
         this.fretNumber = fretNumber;
         this.bendArray = bendArray;
         this.bendArray = bendArray;

+ 4 - 2
src/MusicalScore/VoiceData/Tuplet.ts

@@ -1,5 +1,6 @@
-import {Note} from "./Note";
-import {Fraction} from "../../Common/DataObjects/Fraction";
+import { Note } from "./Note";
+import { Fraction } from "../../Common/DataObjects/Fraction";
+import { PlacementEnum } from "./Expressions/AbstractExpression";
 
 
 /**
 /**
  * Tuplets create irregular rhythms; e.g. triplets, quadruplets, quintuplets, etc.
  * Tuplets create irregular rhythms; e.g. triplets, quadruplets, quintuplets, etc.
@@ -12,6 +13,7 @@ export class Tuplet {
     }
     }
 
 
     private tupletLabelNumber: number;
     private tupletLabelNumber: number;
+    public tupletLabelNumberPlacement: PlacementEnum;
     /** Notes contained in the tuplet, per VoiceEntry (list of VoiceEntries, which has a list of notes). */
     /** Notes contained in the tuplet, per VoiceEntry (list of VoiceEntries, which has a list of notes). */
     private notes: Note[][] = []; // TODO should probably be VoiceEntry[], not Note[][].
     private notes: Note[][] = []; // TODO should probably be VoiceEntry[], not Note[][].
     private fractions: Fraction[] = [];
     private fractions: Fraction[] = [];

+ 16 - 15
src/MusicalScore/VoiceData/VoiceEntry.ts

@@ -9,8 +9,9 @@ import {OrnamentContainer} from "./OrnamentContainer";
 import {KeyInstruction} from "./Instructions/KeyInstruction";
 import {KeyInstruction} from "./Instructions/KeyInstruction";
 import {OrnamentEnum} from "./OrnamentContainer";
 import {OrnamentEnum} from "./OrnamentContainer";
 import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
 import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import { Dictionary } from "typescript-collections";
 import {Arpeggio} from "./Arpeggio";
 import {Arpeggio} from "./Arpeggio";
+import { SourceMeasure } from "./SourceMeasure";
 
 
 /**
 /**
  * A [[VoiceEntry]] contains the notes in a voice at a timestamp.
  * A [[VoiceEntry]] contains the notes in a voice at a timestamp.
@@ -247,7 +248,7 @@ export class VoiceEntry {
                     if ((i % 2) === 0) {
                     if ((i % 2) === 0) {
                         this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                         this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                     } else {
                     } else {
-                        this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, higherPitch, alteration, voiceEntries);
+                        this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, baseNote.SourceMeasure, higherPitch, alteration, voiceEntries);
                     }
                     }
                 }
                 }
                 break;
                 break;
@@ -259,13 +260,13 @@ export class VoiceEntry {
                 const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
                 const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
                 const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createAlteratedVoiceEntry(
                 this.createAlteratedVoiceEntry(
-                    currentTimestamp, length, baseVoice, higherPitch, higherAlteration, voiceEntries
+                    currentTimestamp, length, baseVoice, baseNote.SourceMeasure, higherPitch, higherAlteration, voiceEntries
                 );
                 );
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createAlteratedVoiceEntry(
                 this.createAlteratedVoiceEntry(
-                    currentTimestamp, length, baseVoice, lowerPitch, lowerAlteration, voiceEntries
+                    currentTimestamp, length, baseVoice, baseNote.SourceMeasure, lowerPitch, lowerAlteration, voiceEntries
                 );
                 );
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
@@ -278,13 +279,13 @@ export class VoiceEntry {
                 const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
                 const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
                 const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createAlteratedVoiceEntry(
                 this.createAlteratedVoiceEntry(
-                    currentTimestamp, length, baseVoice, lowerPitch, lowerAlteration, voiceEntries
+                    currentTimestamp, length, baseVoice, baseNote.SourceMeasure, lowerPitch, lowerAlteration, voiceEntries
                 );
                 );
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createAlteratedVoiceEntry(
                 this.createAlteratedVoiceEntry(
-                    currentTimestamp, length, baseVoice, higherPitch, higherAlteration, voiceEntries
+                    currentTimestamp, length, baseVoice, baseNote.SourceMeasure, higherPitch, higherAlteration, voiceEntries
                 );
                 );
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
@@ -299,11 +300,11 @@ export class VoiceEntry {
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 length.Denominator = baselength.Denominator * 8;
                 length.Denominator = baselength.Denominator * 8;
-                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, higherPitch, higherAlteration, voiceEntries);
+                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, baseNote.SourceMeasure, higherPitch, higherAlteration, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
-                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, lowerPitch, lowerAlteration, voiceEntries);
+                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, baseNote.SourceMeasure, lowerPitch, lowerAlteration, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 break;
                 break;
@@ -317,11 +318,11 @@ export class VoiceEntry {
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 length.Denominator = baselength.Denominator * 8;
                 length.Denominator = baselength.Denominator * 8;
-                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, lowerPitch, lowerAlteration, voiceEntries);
+                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, baseNote.SourceMeasure, lowerPitch, lowerAlteration, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
-                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, higherPitch, higherAlteration, voiceEntries);
+                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, baseNote.SourceMeasure, higherPitch, higherAlteration, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 break;
                 break;
@@ -332,7 +333,7 @@ export class VoiceEntry {
                 const alteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 const alteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
-                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, higherPitch, alteration, voiceEntries);
+                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, baseNote.SourceMeasure, higherPitch, alteration, voiceEntries);
                 length.Denominator = baselength.Denominator * 2;
                 length.Denominator = baselength.Denominator * 2;
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
@@ -344,7 +345,7 @@ export class VoiceEntry {
                 const alteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
                 const alteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 currentTimestamp.Add(length);
-                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, lowerPitch, alteration, voiceEntries);
+                this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, baseNote.SourceMeasure, lowerPitch, alteration, voiceEntries);
                 length.Denominator = baselength.Denominator * 2;
                 length.Denominator = baselength.Denominator * 2;
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
@@ -360,17 +361,17 @@ export class VoiceEntry {
     ): void {
     ): void {
         const voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, baseNote.ParentStaffEntry);
         const voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, baseNote.ParentStaffEntry);
         const pitch: Pitch = new Pitch(baseNote.Pitch.FundamentalNote, baseNote.Pitch.Octave, baseNote.Pitch.Accidental);
         const pitch: Pitch = new Pitch(baseNote.Pitch.FundamentalNote, baseNote.Pitch.Octave, baseNote.Pitch.Accidental);
-        const note: Note = new Note(voiceEntry, undefined, length, pitch);
+        const note: Note = new Note(voiceEntry, undefined, length, pitch, baseNote.SourceMeasure);
         voiceEntry.Notes.push(note);
         voiceEntry.Notes.push(note);
         voiceEntries.push(voiceEntry);
         voiceEntries.push(voiceEntry);
     }
     }
     private createAlteratedVoiceEntry(
     private createAlteratedVoiceEntry(
-        currentTimestamp: Fraction, length: Fraction, baseVoice: Voice, higherPitch: Pitch,
+        currentTimestamp: Fraction, length: Fraction, baseVoice: Voice, sourceMeasure: SourceMeasure, higherPitch: Pitch,
         alteration: AccidentalEnum, voiceEntries: VoiceEntry[]
         alteration: AccidentalEnum, voiceEntries: VoiceEntry[]
     ): void {
     ): void {
         const voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, undefined);
         const voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, undefined);
         const pitch: Pitch = new Pitch(higherPitch.FundamentalNote, higherPitch.Octave, alteration);
         const pitch: Pitch = new Pitch(higherPitch.FundamentalNote, higherPitch.Octave, alteration);
-        const note: Note = new Note(voiceEntry, undefined, length, pitch);
+        const note: Note = new Note(voiceEntry, undefined, length, pitch, sourceMeasure);
         voiceEntry.Notes.push(note);
         voiceEntry.Notes.push(note);
         voiceEntries.push(voiceEntry);
         voiceEntries.push(voiceEntry);
     }
     }

+ 61 - 22
src/OpenSheetMusicDisplay/Cursor.ts

@@ -7,8 +7,12 @@ import {OpenSheetMusicDisplay} from "./OpenSheetMusicDisplay";
 import {GraphicalMusicSheet} from "../MusicalScore/Graphical/GraphicalMusicSheet";
 import {GraphicalMusicSheet} from "../MusicalScore/Graphical/GraphicalMusicSheet";
 import {Instrument} from "../MusicalScore/Instrument";
 import {Instrument} from "../MusicalScore/Instrument";
 import {Note} from "../MusicalScore/VoiceData/Note";
 import {Note} from "../MusicalScore/VoiceData/Note";
-import {EngravingRules, Fraction} from "..";
-import {SourceMeasure, StaffLine} from "../MusicalScore";
+import {Fraction} from "../Common/DataObjects/Fraction";
+import { EngravingRules } from "../MusicalScore/Graphical/EngravingRules";
+import { SourceMeasure } from "../MusicalScore/VoiceData/SourceMeasure";
+import { StaffLine } from "../MusicalScore/Graphical/StaffLine";
+import { GraphicalMeasure } from "../MusicalScore/Graphical/GraphicalMeasure";
+import { VexFlowMeasure } from "../MusicalScore/Graphical/VexFlow/VexFlowMeasure";
 
 
 /**
 /**
  * A cursor which can iterate through the music sheet.
  * A cursor which can iterate through the music sheet.
@@ -50,6 +54,7 @@ export class Cursor {
   public iterator: MusicPartManagerIterator;
   public iterator: MusicPartManagerIterator;
   private graphic: GraphicalMusicSheet;
   private graphic: GraphicalMusicSheet;
   public hidden: boolean = true;
   public hidden: boolean = true;
+  public currentPageNumber: number = 1;
 
 
   /** Initialize the cursor. Necessary before using functions like show() and next(). */
   /** Initialize the cursor. Necessary before using functions like show() and next(). */
   public init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void {
   public init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void {
@@ -100,10 +105,11 @@ export class Cursor {
   }
   }
 
 
   public update(): void {
   public update(): void {
-    // Warning! This should NEVER call this.openSheetMusicDisplay.render()
     if (this.hidden || this.hidden === undefined || this.hidden === null) {
     if (this.hidden || this.hidden === undefined || this.hidden === null) {
       return;
       return;
     }
     }
+    this.updateCurrentPage(); // attach cursor to new page DOM if necessary
+
     // this.graphic?.Cursors?.length = 0;
     // this.graphic?.Cursors?.length = 0;
     const iterator: MusicPartManagerIterator = this.iterator;
     const iterator: MusicPartManagerIterator = this.iterator;
     // TODO when measure draw range (drawUpToMeasureNumber) was changed, next/update can fail to move cursor. but of course it can be reset before.
     // TODO when measure draw range (drawUpToMeasureNumber) was changed, next/update can fail to move cursor. but of course it can be reset before.
@@ -113,35 +119,43 @@ export class Cursor {
       return;
       return;
     }
     }
     let x: number = 0, y: number = 0, height: number = 0;
     let x: number = 0, y: number = 0, height: number = 0;
+    let musicSystem: MusicSystem;
+    if (iterator.CurrentMeasure.isReducedToMultiRest) {
+      const multiRestGMeasure: GraphicalMeasure = this.graphic.findGraphicalMeasure(iterator.CurrentMeasureIndex, 0);
+      const totalRestMeasures: number = multiRestGMeasure.parentSourceMeasure.multipleRestMeasures;
+      const currentRestMeasureNumber: number = iterator.CurrentMeasure.multipleRestMeasureNumber;
+      const progressRatio: number = currentRestMeasureNumber / (totalRestMeasures + 1);
+      const effectiveWidth: number = multiRestGMeasure.PositionAndShape.Size.width - (multiRestGMeasure as VexFlowMeasure).beginInstructionsWidth;
+      x = multiRestGMeasure.PositionAndShape.AbsolutePosition.x + (multiRestGMeasure as VexFlowMeasure).beginInstructionsWidth + progressRatio * effectiveWidth;
+
+      musicSystem = multiRestGMeasure.ParentMusicSystem;
+    } else {
+          // get all staff entries inside the current voice entry
+          const gseArr: VexFlowStaffEntry[] = voiceEntries.map(ve => this.getStaffEntryFromVoiceEntry(ve));
+          // sort them by x position and take the leftmost entry
+          const gse: VexFlowStaffEntry =
+                gseArr.sort((a, b) => a?.PositionAndShape?.AbsolutePosition?.x <= b?.PositionAndShape?.AbsolutePosition?.x ? -1 : 1 )[0];
+          x = gse.PositionAndShape.AbsolutePosition.x;
+          musicSystem = gse.parentMeasure.ParentMusicSystem;
 
 
-    // get all staff entries inside the current voice entry
-    const gseArr: VexFlowStaffEntry[] = voiceEntries.map(ve => this.getStaffEntryFromVoiceEntry(ve));
-    // sort them by x position and take the leftmost entry
-    const gse: VexFlowStaffEntry =
-          gseArr.sort((a, b) => a?.PositionAndShape?.AbsolutePosition?.x <= b?.PositionAndShape?.AbsolutePosition?.x ? -1 : 1 )[0];
-    x = gse.PositionAndShape.AbsolutePosition.x;
-    const musicSystem: MusicSystem = gse.parentMeasure.ParentMusicSystem;
+          // debug: change color of notes under cursor (needs re-render)
+          // for (const gve of gse.graphicalVoiceEntries) {
+          //   for (const note of gve.notes) {
+          //     note.sourceNote.NoteheadColor = "#0000FF";
+          //   }
+          // }
+    }
     if (!musicSystem) {
     if (!musicSystem) {
       return;
       return;
     }
     }
+
+    // y is common for both multirest and non-multirest, given the MusicSystem
     y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
     y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
     const bottomStaffline: StaffLine = musicSystem.StaffLines[musicSystem.StaffLines.length - 1];
     const bottomStaffline: StaffLine = musicSystem.StaffLines[musicSystem.StaffLines.length - 1];
     const endY: number = musicSystem.PositionAndShape.AbsolutePosition.y +
     const endY: number = musicSystem.PositionAndShape.AbsolutePosition.y +
     bottomStaffline.PositionAndShape.RelativePosition.y + bottomStaffline.StaffHeight;
     bottomStaffline.PositionAndShape.RelativePosition.y + bottomStaffline.StaffHeight;
     height = endY - y;
     height = endY - y;
 
 
-    // The following code is not necessary (for now, but it could come useful later):
-    // it highlights the notes under the cursor.
-    //let vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = gse.vfNotes;
-    //for (let voiceId in vfNotes) {
-    //    if (vfNotes.hasOwnProperty(voiceId)) {
-    //        vfNotes[voiceId].setStyle({
-    //            fillStyle: "red",
-    //            strokeStyle: "red",
-    //        });
-    //    }
-    //}
-
     // Update the graphical cursor
     // Update the graphical cursor
     // The following is the legacy cursor rendered on the canvas:
     // The following is the legacy cursor rendered on the canvas:
     // // let cursor: GraphicalLine = new GraphicalLine(new PointF2D(x, y), new PointF2D(x, y + height), 3, OutlineAndFillStyleEnum.PlaybackCursor);
     // // let cursor: GraphicalLine = new GraphicalLine(new PointF2D(x, y), new PointF2D(x, y + height), 3, OutlineAndFillStyleEnum.PlaybackCursor);
@@ -237,4 +251,29 @@ export class Cursor {
     });
     });
     return notes;
     return notes;
   }
   }
+
+  /** Check if there was a change in current page, and attach cursor element to the corresponding HTMLElement (div).
+   *  This is only necessary if using PageFormat (multiple pages).
+   */
+  public updateCurrentPage(): number {
+    const timestamp: Fraction = this.iterator.currentTimeStamp;
+    for (const page of this.graphic.MusicPages) {
+      const lastSystemTimestamp: Fraction = page.MusicSystems.last().GetSystemsLastTimeStamp();
+      if (lastSystemTimestamp.gt(timestamp)) {
+        // gt: the last timestamp of the last system is equal to the first of the next page,
+        //   so we do need to use gt, not gte here.
+        const newPageNumber: number = page.PageNumber;
+        if (newPageNumber !== this.currentPageNumber) {
+          this.container.removeChild(this.cursorElement);
+          this.container = document.getElementById("osmdCanvasPage" + newPageNumber);
+          this.container.appendChild(this.cursorElement);
+          // TODO maybe store this.pageCurrentlyAttachedTo, though right now it isn't necessary
+          // alternative to remove/append:
+          // this.openSheetMusicDisplay.enableOrDisableCursor(true);
+        }
+        return this.currentPageNumber = newPageNumber;
+      }
+    }
+    return 1;
+  }
 }
 }

+ 15 - 2
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -94,7 +94,7 @@ export interface IOSMDOptions {
     measureNumberInterval?: number;
     measureNumberInterval?: number;
     /** Whether to draw fingerings (only left to the note for now). Default true (unless solo part). */
     /** Whether to draw fingerings (only left to the note for now). Default true (unless solo part). */
     drawFingerings?: boolean;
     drawFingerings?: boolean;
-    /** Where to draw fingerings (left, right, above, below, auto).
+    /** Where to draw fingerings (left, right, above, below, or auto).
      * Default left. Auto, above, below experimental (potential collisions because bounding box not correct)
      * Default left. Auto, above, below experimental (potential collisions because bounding box not correct)
      */
      */
     fingeringPosition?: string;
     fingeringPosition?: string;
@@ -203,6 +203,19 @@ export interface IOSMDOptions {
      *  Setting this is the same as setting osmd.EngravingRules.SoftmaxFactorVexFlow.
      *  Setting this is the same as setting osmd.EngravingRules.SoftmaxFactorVexFlow.
      */
      */
     spacingFactorSoftmax?: number;
     spacingFactorSoftmax?: number;
+    /**
+     * Number in pixels, of spacing between multi-line labels
+     */
+    spacingBetweenTextLines?: number;
+    /**
+     * Set to true if the last system line should be streched across the whole page just as the other systems. Default is false
+     */
+    stretchLastSystemLine?: boolean;
+    /**
+     * Set to true if subsequent measures full of rests should be auto-converted to multi-rest measure. Default is true
+     * This works across instruments- If all instruments have subsequent measures with nothing but rests, multirest measures are generated
+     */
+    autoGenerateMutipleRestMeasuresFromRestMeasures?: boolean;
 }
 }
 
 
 export enum AlignRestOption {
 export enum AlignRestOption {
@@ -214,7 +227,7 @@ export enum AlignRestOption {
 export enum FillEmptyMeasuresWithWholeRests {
 export enum FillEmptyMeasuresWithWholeRests {
     No = 0,
     No = 0,
     YesVisible = 1,
     YesVisible = 1,
-    YesInvisible = 2
+    YesInvisible = 2 // fill with invisible whole rests
 }
 }
 
 
 export enum BackendType {
 export enum BackendType {

+ 25 - 7
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -17,17 +17,18 @@ import { IOSMDOptions, OSMDOptions, AutoBeamOptions, BackendType } from "./OSMDO
 import { EngravingRules, PageFormat } from "../MusicalScore/Graphical/EngravingRules";
 import { EngravingRules, PageFormat } from "../MusicalScore/Graphical/EngravingRules";
 import { AbstractExpression } from "../MusicalScore/VoiceData/Expressions/AbstractExpression";
 import { AbstractExpression } from "../MusicalScore/VoiceData/Expressions/AbstractExpression";
 import { Dictionary } from "typescript-collections";
 import { Dictionary } from "typescript-collections";
-import { NoteEnum } from "..";
-import { AutoColorSet, GraphicalMusicPage } from "../MusicalScore";
-import { MusicPartManagerIterator } from "../MusicalScore/MusicParts";
-import { ITransposeCalculator } from "../MusicalScore/Interfaces";
+import { AutoColorSet } from "../MusicalScore/Graphical/DrawingEnums";
+import { GraphicalMusicPage } from "../MusicalScore/Graphical/GraphicalMusicPage";
+import { MusicPartManagerIterator } from "../MusicalScore/MusicParts/MusicPartManagerIterator";
+import { ITransposeCalculator } from "../MusicalScore/Interfaces/ITransposeCalculator";
+import { NoteEnum } from "../Common/DataObjects/Pitch";
 /**
 /**
  * The main class and control point of OpenSheetMusicDisplay.<br>
  * The main class and control point of OpenSheetMusicDisplay.<br>
  * It can display MusicXML sheet music files in an HTML element container.<br>
  * It can display MusicXML sheet music files in an HTML element container.<br>
  * After the constructor, use load() and render() to load and render a MusicXML file.
  * After the constructor, use load() and render() to load and render a MusicXML file.
  */
  */
 export class OpenSheetMusicDisplay {
 export class OpenSheetMusicDisplay {
-    private version: string = "0.8.3-release"; // getter: this.Version
+    private version: string = "0.8.3-dev"; // getter: this.Version
     // at release, bump version and change to -release, afterwards to -dev again
     // at release, bump version and change to -release, afterwards to -dev again
 
 
     /**
     /**
@@ -524,6 +525,15 @@ export class OpenSheetMusicDisplay {
         if (options.spacingFactorSoftmax !== undefined) {
         if (options.spacingFactorSoftmax !== undefined) {
             this.rules.SoftmaxFactorVexFlow = options.spacingFactorSoftmax;
             this.rules.SoftmaxFactorVexFlow = options.spacingFactorSoftmax;
         }
         }
+        if (options.spacingBetweenTextLines !== undefined) {
+            this.rules.SpacingBetweenTextLines = options.spacingBetweenTextLines;
+        }
+        if (options.stretchLastSystemLine !== undefined) {
+            this.rules.StretchLastSystemLine = options.stretchLastSystemLine;
+        }
+        if (options.autoGenerateMutipleRestMeasuresFromRestMeasures !== undefined) {
+            this.rules.AutoGenerateMutipleRestMeasuresFromRestMeasures = options.autoGenerateMutipleRestMeasuresFromRestMeasures;
+        }
     }
     }
 
 
     public setColoringMode(options: IOSMDOptions): void {
     public setColoringMode(options: IOSMDOptions): void {
@@ -697,10 +707,17 @@ export class OpenSheetMusicDisplay {
             // save previous cursor state
             // save previous cursor state
             const hidden: boolean = this.cursor?.Hidden;
             const hidden: boolean = this.cursor?.Hidden;
             const previousIterator: MusicPartManagerIterator = this.cursor?.Iterator;
             const previousIterator: MusicPartManagerIterator = this.cursor?.Iterator;
+            this.cursor?.hide();
 
 
+            // check which page/backend to draw the cursor on (the pages may have changed since last cursor)
+            let backendToDrawOn: VexFlowBackend = this.drawer?.Backends[0];
+            if (backendToDrawOn && this.rules.RestoreCursorAfterRerender && this.cursor) {
+                const newPageNumber: number = this.cursor.updateCurrentPage();
+                backendToDrawOn = this.drawer.Backends[newPageNumber - 1];
+            }
             // create new cursor
             // create new cursor
-            if (this.drawer?.Backends?.length >= 1 && this.drawer.Backends[0].getRenderElement()) {
-                this.cursor = new Cursor(this.drawer.Backends[0].getRenderElement(), this);
+            if (backendToDrawOn && backendToDrawOn.getRenderElement()) {
+                this.cursor = new Cursor(backendToDrawOn.getRenderElement(), this);
             }
             }
             if (this.sheet && this.graphic && this.cursor) { // else init is called in load()
             if (this.sheet && this.graphic && this.cursor) { // else init is called in load()
                 this.cursor.init(this.sheet.MusicPartManager, this.graphic);
                 this.cursor.init(this.sheet.MusicPartManager, this.graphic);
@@ -711,6 +728,7 @@ export class OpenSheetMusicDisplay {
                 this.cursor.hidden = hidden;
                 this.cursor.hidden = hidden;
                 if (previousIterator) {
                 if (previousIterator) {
                     this.cursor.iterator = previousIterator;
                     this.cursor.iterator = previousIterator;
+                    this.cursor.update();
                 }
                 }
             }
             }
         } else { // disable cursor
         } else { // disable cursor

+ 1 - 1
src/Util/CollectionUtil.ts

@@ -1,4 +1,4 @@
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import { Dictionary } from "typescript-collections";
 
 
 declare global {
 declare global {
     interface Array<T> {
     interface Array<T> {

+ 9 - 0
src/VexFlowPatch/readme.txt

@@ -0,0 +1,9 @@
+These files are custom patches for the currently installed vexflow version.
+They should be copied to ../../node_modules/vexflow/src/
+This will be by a pre-build script soon, but for now has to be done by hand.
+
+Currently, we are using Vexflow 1.2.93, because of some formatter advantages
+compared to Vexflow 3.x versions.
+Because of that, we need to patch in a few fixes that came after 1.2.93.
+
+All the current fixes are already merged into the Vexflow repository.

+ 69 - 0
src/VexFlowPatch/stavevolta.js

@@ -0,0 +1,69 @@
+// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
+// Author Larry Kuhns 2011
+
+import { StaveModifier } from './stavemodifier';
+
+export class Volta extends StaveModifier {
+  static get CATEGORY() { return 'voltas'; }
+  static get type() {
+    return {
+      NONE: 1,
+      BEGIN: 2,
+      MID: 3,
+      END: 4,
+      BEGIN_END: 5,
+    };
+  }
+
+  constructor(type, number, x, y_shift) {
+    super();
+    this.setAttribute('type', 'Volta');
+    this.volta = type;
+    this.x = x;
+    this.y_shift = y_shift;
+    this.number = number;
+    this.font = {
+      family: 'sans-serif',
+      size: 9,
+      weight: 'bold',
+    };
+  }
+
+  getCategory() { return Volta.CATEGORY; }
+  setShiftY(y) { this.y_shift = y; return this; }
+
+  draw(stave, x) {
+    const ctx = stave.checkContext();
+    this.setRendered();
+
+    let width = stave.width - x; // don't add x offset to width
+    const top_y = stave.getYForTopText(stave.options.num_lines) + this.y_shift;
+    const vert_height = 1.5 * stave.options.spacing_between_lines_px;
+    switch (this.volta) {
+      case Volta.type.BEGIN:
+        ctx.fillRect(this.x + x, top_y, 1, vert_height);
+        break;
+      case Volta.type.END:
+        width -= 5;
+        ctx.fillRect(this.x + x + width, top_y, 1, vert_height);
+        break;
+      case Volta.type.BEGIN_END:
+        width -= 3;
+        ctx.fillRect(this.x + x, top_y, 1, vert_height);
+        ctx.fillRect(this.x + x + width, top_y, 1, vert_height);
+        break;
+      default:
+        break;
+    }
+    // If the beginning of a volta, draw measure number
+    if (this.volta === Volta.type.BEGIN || this.volta === Volta.type.BEGIN_END) {
+      ctx.save();
+      ctx.setFont(this.font.family, this.font.size, this.font.weight);
+      ctx.fillText(this.number, this.x + x + 5, top_y + 15);
+      ctx.restore();
+    }
+
+    ctx.fillRect(this.x + x, top_y, width, 1);
+    return this;
+  }
+}

+ 491 - 0
src/VexFlowPatch/tabnote.js

@@ -0,0 +1,491 @@
+// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
+//
+// ## Description
+//
+// The file implements notes for Tablature notation. This consists of one or
+// more fret positions, and can either be drawn with or without stems.
+//
+// See `tests/tabnote_tests.js` for usage examples
+
+import { Vex } from './vex';
+import { Flow } from './tables';
+import { Modifier } from './modifier';
+import { Stem } from './stem';
+import { StemmableNote } from './stemmablenote';
+import { Dot } from './dot';
+import { Glyph } from './glyph';
+
+// Gets the unused strings grouped together if consecutive.
+//
+// Parameters:
+// * num_lines - The number of lines
+// * strings_used - An array of numbers representing which strings have fret positions
+function getUnusedStringGroups(num_lines, strings_used) {
+  const stem_through = [];
+  let group = [];
+  for (let string = 1; string <= num_lines; string++) {
+    const is_used = strings_used.indexOf(string) > -1;
+
+    if (!is_used) {
+      group.push(string);
+    } else {
+      stem_through.push(group);
+      group = [];
+    }
+  }
+  if (group.length > 0) stem_through.push(group);
+
+  return stem_through;
+}
+
+// Gets groups of points that outline the partial stem lines
+// between fret positions
+//
+// Parameters:
+// * stem_Y - The `y` coordinate the stem is located on
+// * unused_strings - An array of groups of unused strings
+// * stave - The stave to use for reference
+// * stem_direction - The direction of the stem
+function getPartialStemLines(stem_y, unused_strings, stave, stem_direction) {
+  const up_stem = stem_direction !== 1;
+  const down_stem = stem_direction !== -1;
+
+  const line_spacing = stave.getSpacingBetweenLines();
+  const total_lines = stave.getNumLines();
+
+  const stem_lines = [];
+
+  unused_strings.forEach(strings => {
+    const containsLastString = strings.indexOf(total_lines) > -1;
+    const containsFirstString =  strings.indexOf(1) > -1;
+
+    if ((up_stem && containsFirstString) ||
+       (down_stem && containsLastString)) {
+      return;
+    }
+
+    // If there's only one string in the group, push a duplicate value.
+    // We do this because we need 2 strings to convert into upper/lower y
+    // values.
+    if (strings.length === 1) {
+      strings.push(strings[0]);
+    }
+
+    const line_ys = [];
+    // Iterate through each group string and store it's y position
+    strings.forEach((string, index, strings) => {
+      const isTopBound = string === 1;
+      const isBottomBound = string === total_lines;
+
+      // Get the y value for the appropriate staff line,
+      // we adjust for a 0 index array, since string numbers are index 1
+      let y = stave.getYForLine(string - 1);
+
+      // Unless the string is the first or last, add padding to each side
+      // of the line
+      if (index === 0 && !isTopBound) {
+        y -= line_spacing / 2 - 1;
+      } else if (index === strings.length - 1 && !isBottomBound) {
+        y += line_spacing / 2 - 1;
+      }
+
+      // Store the y value
+      line_ys.push(y);
+
+      // Store a subsequent y value connecting this group to the main
+      // stem above/below the stave if it's the top/bottom string
+      if (stem_direction === 1 && isTopBound) {
+        line_ys.push(stem_y - 2);
+      } else if (stem_direction === -1 && isBottomBound) {
+        line_ys.push(stem_y + 2);
+      }
+    });
+
+    // Add the sorted y values to the
+    stem_lines.push(line_ys.sort((a, b) => a - b));
+  });
+
+  return stem_lines;
+}
+
+export class TabNote extends StemmableNote {
+  static get CATEGORY() { return 'tabnotes'; }
+
+  // Initialize the TabNote with a `tab_struct` full of properties
+  // and whether to `draw_stem` when rendering the note
+  constructor(tab_struct, draw_stem) {
+    super(tab_struct);
+    this.setAttribute('type', 'TabNote');
+
+    this.ghost = false; // Renders parenthesis around notes
+    // Note properties
+    //
+    // The fret positions in the note. An array of `{ str: X, fret: X }`
+    this.positions = tab_struct.positions;
+
+    // Render Options
+    Vex.Merge(this.render_options, {
+      // font size for note heads and rests
+      glyph_font_scale: Flow.DEFAULT_TABLATURE_FONT_SCALE,
+      // Flag to draw a stem
+      draw_stem,
+      // Flag to draw dot modifiers
+      draw_dots: draw_stem,
+      // Flag to extend the main stem through the stave and fret positions
+      draw_stem_through_stave: false,
+      // vertical shift from stave line
+      y_shift: 0,
+      // normal glyph scale
+      scale: 1.0,
+      // default tablature font
+      font: '10pt Arial',
+    });
+
+    this.glyph = Flow.getGlyphProps(this.duration, this.noteType);
+
+    if (!this.glyph) {
+      throw new Vex.RuntimeError(
+        'BadArguments',
+        `Invalid note initialization data (No glyph found): ${JSON.stringify(tab_struct)}`
+      );
+    }
+
+    this.buildStem();
+
+    if (tab_struct.stem_direction) {
+      this.setStemDirection(tab_struct.stem_direction);
+    } else {
+      this.setStemDirection(Stem.UP);
+    }
+
+    // Renders parenthesis around notes
+    this.ghost = false;
+    this.updateWidth();
+  }
+
+  reset() {
+    if (this.stave) this.setStave(this.stave);
+  }
+
+  // The ModifierContext category
+  getCategory() { return TabNote.CATEGORY; }
+
+  // Set as ghost `TabNote`, surrounds the fret positions with parenthesis.
+  // Often used for indicating frets that are being bent to
+  setGhost(ghost) {
+    this.ghost = ghost;
+    this.updateWidth();
+    return this;
+  }
+
+  // Determine if the note has a stem
+  hasStem() { return this.render_options.draw_stem; }
+
+  // Get the default stem extension for the note
+  getStemExtension() {
+    const glyph = this.getGlyph();
+
+    if (this.stem_extension_override != null) {
+      return this.stem_extension_override;
+    }
+
+    if (glyph) {
+      return this.getStemDirection() === 1
+        ? glyph.tabnote_stem_up_extension
+        : glyph.tabnote_stem_down_extension;
+    }
+
+    return 0;
+  }
+
+  // Add a dot to the note
+  addDot() {
+    const dot = new Dot();
+    this.dots += 1;
+    return this.addModifier(dot, 0);
+  }
+
+  // Calculate and store the width of the note
+  updateWidth() {
+    this.glyphs = [];
+    this.width = 0;
+    for (let i = 0; i < this.positions.length; ++i) {
+      let fret = this.positions[i].fret;
+      if (this.ghost) fret = '(' + fret + ')';
+      const glyph = Flow.tabToGlyph(fret, this.render_options.scale);
+      this.glyphs.push(glyph);
+      this.width = Math.max(glyph.getWidth(), this.width);
+    }
+    // For some reason we associate a notehead glyph with a TabNote, and this
+    // glyph is used for certain width calculations. Of course, this is totally
+    // incorrect since a notehead is a poor approximation for the dimensions of
+    // a fret number which can have multiple digits. As a result, we must
+    // overwrite getWidth() to return the correct width
+    this.glyph.getWidth = () => this.width;
+  }
+
+  // Set the `stave` to the note
+  setStave(stave) {
+    super.setStave(stave);
+    this.context = stave.context;
+
+    // Calculate the fret number width based on font used
+    let i;
+    if (this.context) {
+      const ctx = this.context;
+      this.width = 0;
+      for (i = 0; i < this.glyphs.length; ++i) {
+        const glyph = this.glyphs[i];
+        const text = '' + glyph.text;
+        if (text.toUpperCase() !== 'X') {
+          ctx.save();
+          ctx.setRawFont(this.render_options.font);
+          glyph.width = ctx.measureText(text).width;
+          ctx.restore();
+          glyph.getWidth = () => glyph.width;
+        }
+        this.width = Math.max(glyph.getWidth(), this.width);
+      }
+      this.glyph.getWidth = () => this.width;
+    }
+
+    // we subtract 1 from `line` because getYForLine expects a 0-based index,
+    // while the position.str is a 1-based index
+    const ys = this.positions.map(({ str: line }) => stave.getYForLine(line - 1));
+
+    this.setYs(ys);
+
+    if (this.stem) {
+      this.stem.setYBounds(this.getStemY(), this.getStemY());
+    }
+
+    return this;
+  }
+
+  // Get the fret positions for the note
+  getPositions() { return this.positions; }
+
+  // Add self to the provided modifier context `mc`
+  addToModifierContext(mc) {
+    this.setModifierContext(mc);
+    for (let i = 0; i < this.modifiers.length; ++i) {
+      this.modifierContext.addModifier(this.modifiers[i]);
+    }
+    this.modifierContext.addModifier(this);
+    this.preFormatted = false;
+    return this;
+  }
+
+  // Get the `x` coordinate to the right of the note
+  getTieRightX() {
+    let tieStartX = this.getAbsoluteX();
+    const note_glyph_width = this.glyph.getWidth();
+    tieStartX += note_glyph_width / 2;
+    tieStartX += (-this.width / 2) + this.width + 2;
+
+    return tieStartX;
+  }
+
+  // Get the `x` coordinate to the left of the note
+  getTieLeftX() {
+    let tieEndX = this.getAbsoluteX();
+    const note_glyph_width = this.glyph.getWidth();
+    tieEndX += note_glyph_width / 2;
+    tieEndX -= (this.width / 2) + 2;
+
+    return tieEndX;
+  }
+
+  // Get the default `x` and `y` coordinates for a modifier at a specific
+  // `position` at a fret position `index`
+  getModifierStartXY(position, index) {
+    if (!this.preFormatted) {
+      throw new Vex.RERR('UnformattedNote', "Can't call GetModifierStartXY on an unformatted note");
+    }
+
+    if (this.ys.length === 0) {
+      throw new Vex.RERR('NoYValues', 'No Y-Values calculated for this note.');
+    }
+
+    let x = 0;
+    if (position === Modifier.Position.LEFT) {
+      x = -1 * 2;  // extra_left_px
+    } else if (position === Modifier.Position.RIGHT) {
+      x = this.width + 2; // extra_right_px
+    } else if (position === Modifier.Position.BELOW || position === Modifier.Position.ABOVE) {
+      const note_glyph_width = this.glyph.getWidth();
+      x = note_glyph_width / 2;
+    }
+
+    return {
+      x: this.getAbsoluteX() + x,
+      y: this.ys[index],
+    };
+  }
+
+  // Get the default line for rest
+  getLineForRest() { return this.positions[0].str; }
+
+  // Pre-render formatting
+  preFormat() {
+    if (this.preFormatted) return;
+    if (this.modifierContext) this.modifierContext.preFormat();
+    // width is already set during init()
+    this.setPreFormatted(true);
+  }
+
+  // Get the x position for the stem
+  getStemX() { return this.getCenterGlyphX(); }
+
+  // Get the y position for the stem
+  getStemY() {
+    const num_lines = this.stave.getNumLines();
+
+    // The decimal staff line amounts provide optimal spacing between the
+    // fret number and the stem
+    const stemUpLine = -0.5;
+    const stemDownLine = num_lines - 0.5;
+    const stemStartLine = Stem.UP === this.stem_direction ? stemUpLine : stemDownLine;
+
+    return this.stave.getYForLine(stemStartLine);
+  }
+
+  // Get the stem extents for the tabnote
+  getStemExtents() {
+    return this.stem.getExtents();
+  }
+
+  // Draw the fal onto the context
+  drawFlag() {
+    const {
+      beam, glyph, context, stem, stem_direction,
+      render_options: { draw_stem, glyph_font_scale },
+    } = this;
+
+    const shouldDrawFlag = beam == null && draw_stem;
+
+    // Now it's the flag's turn.
+    if (glyph.flag && shouldDrawFlag) {
+      const flag_x = this.getStemX() + 1;
+      const flag_y = this.getStemY() - stem.getHeight();
+
+      const flag_code = stem_direction === Stem.DOWN
+        ? glyph.code_flag_downstem // Down stems have flags on the left.
+        : glyph.code_flag_upstem;
+
+      // Draw the Flag
+      Glyph.renderGlyph(context, flag_x, flag_y, glyph_font_scale, flag_code);
+    }
+  }
+
+  // Render the modifiers onto the context
+  drawModifiers() {
+    // Draw the modifiers
+    this.modifiers.forEach((modifier) => {
+      // Only draw the dots if enabled
+      if (modifier.getCategory() === 'dots' && !this.render_options.draw_dots) return;
+
+      modifier.setContext(this.context);
+      modifier.drawWithStyle();
+    });
+  }
+
+  // Render the stem extension through the fret positions
+  drawStemThrough() {
+    const stem_x = this.getStemX();
+    const stem_y = this.getStemY();
+    const ctx = this.context;
+
+    const stem_through = this.render_options.draw_stem_through_stave;
+    const draw_stem = this.render_options.draw_stem;
+    if (draw_stem && stem_through) {
+      const total_lines = this.stave.getNumLines();
+      const strings_used = this.positions.map(position => position.str);
+
+      const unused_strings = getUnusedStringGroups(total_lines, strings_used);
+      const stem_lines = getPartialStemLines(
+        stem_y,
+        unused_strings,
+        this.getStave(),
+        this.getStemDirection()
+      );
+
+      ctx.save();
+      ctx.setLineWidth(Stem.WIDTH);
+      stem_lines.forEach(bounds => {
+        if (bounds.length === 0) return;
+
+        ctx.beginPath();
+        ctx.moveTo(stem_x, bounds[0]);
+        ctx.lineTo(stem_x, bounds[bounds.length - 1]);
+        ctx.stroke();
+        ctx.closePath();
+      });
+      ctx.restore();
+    }
+  }
+
+  // Render the fret positions onto the context
+  drawPositions() {
+    const ctx = this.context;
+    const x = this.getAbsoluteX();
+    const ys = this.ys;
+    for (let i = 0; i < this.positions.length; ++i) {
+      const y = ys[i] + this.render_options.y_shift;
+      const glyph = this.glyphs[i];
+
+      // Center the fret text beneath the notation note head
+      const note_glyph_width = this.glyph.getWidth();
+      const tab_x = x + (note_glyph_width / 2) - (glyph.getWidth() / 2);
+
+      // FIXME: Magic numbers.
+      ctx.clearRect(tab_x - 2, y - 3, glyph.getWidth() + 4, 6);
+
+      if (glyph.code) {
+        Glyph.renderGlyph(ctx, tab_x, y,
+          this.render_options.glyph_font_scale * this.render_options.scale,
+          glyph.code);
+      } else {
+        ctx.save();
+        ctx.setRawFont(this.render_options.font);
+        const text = glyph.text.toString();
+        ctx.fillText(text, tab_x, y + 5 * this.render_options.scale);
+        ctx.restore();
+      }
+    }
+  }
+
+  // The main rendering function for the entire note
+  draw() {
+    this.checkContext();
+
+    if (!this.stave) {
+      throw new Vex.RERR('NoStave', "Can't draw without a stave.");
+    }
+
+    if (this.ys.length === 0) {
+      throw new Vex.RERR('NoYValues', "Can't draw note without Y values.");
+    }
+
+    this.setRendered();
+    const render_stem = this.beam == null && this.render_options.draw_stem;
+
+    this.context.openGroup('tabnote', null, { pointerBBox: true });
+    this.drawPositions();
+    this.drawStemThrough();
+
+    const stem_x = this.getStemX();
+
+    this.stem.setNoteHeadXBounds(stem_x, stem_x);
+
+    if (render_stem) {
+      this.context.openGroup('stem', null, { pointerBBox: true });
+      this.stem.setContext(this.context).draw();
+      this.context.closeGroup();
+    }
+
+    this.drawFlag();
+    this.drawModifiers();
+    this.context.closeGroup();
+  }
+}

+ 4 - 4
test/Common/DataObjects/Pitch_Test.ts

@@ -3,16 +3,16 @@ import { Pitch, NoteEnum, AccidentalEnum } from "../../../src/Common/DataObjects
 describe("Pitch Unit Tests:", () => {
 describe("Pitch Unit Tests:", () => {
     describe("transpose Pitch", () => {
     describe("transpose Pitch", () => {
         const pitch: Pitch = new Pitch(NoteEnum.A, 1, AccidentalEnum.NONE);
         const pitch: Pitch = new Pitch(NoteEnum.A, 1, AccidentalEnum.NONE);
-        const transposedFundamentalAndOctave: {value: number; overflow: number; } =
+        const transposedFundamentalAndOctave: {halftone: number; overflow: number; } =
           Pitch.CalculateTransposedHalfTone(pitch, 12);
           Pitch.CalculateTransposedHalfTone(pitch, 12);
-        const higherTransposedFundamentalAndOctave: {value: number; overflow: number; } =
+        const higherTransposedFundamentalAndOctave: {halftone: number; overflow: number; } =
           Pitch.CalculateTransposedHalfTone(pitch, 26);
           Pitch.CalculateTransposedHalfTone(pitch, 26);
 
 
         it("should be 1 octave higher and same fundamental", (done: MochaDone) => {
         it("should be 1 octave higher and same fundamental", (done: MochaDone) => {
             chai.expect(transposedFundamentalAndOctave.overflow).to.equal(1);
             chai.expect(transposedFundamentalAndOctave.overflow).to.equal(1);
-            chai.expect(transposedFundamentalAndOctave.value).to.equal(pitch.FundamentalNote);
+            chai.expect(transposedFundamentalAndOctave.halftone).to.equal(pitch.FundamentalNote);
             chai.expect(higherTransposedFundamentalAndOctave.overflow).to.equal(2);
             chai.expect(higherTransposedFundamentalAndOctave.overflow).to.equal(2);
-            chai.expect(higherTransposedFundamentalAndOctave.value).to.equal(pitch.FundamentalNote + 2);
+            chai.expect(higherTransposedFundamentalAndOctave.halftone).to.equal(pitch.FundamentalNote + 2);
             done();
             done();
         });
         });
     });
     });

+ 2 - 1
test/Common/OSMD/OSMD_Test.ts

@@ -359,7 +359,8 @@ describe("OpenSheetMusicDisplay Main Export", () => {
                     voiceEntry,
                     voiceEntry,
                     voiceEntry.ParentSourceStaffEntry,
                     voiceEntry.ParentSourceStaffEntry,
                     new Fraction(1),
                     new Fraction(1),
-                    new Pitch(11, 2, AccidentalEnum.NATURAL));
+                    new Pitch(11, 2, AccidentalEnum.NATURAL),
+                    voiceEntry.ParentSourceStaffEntry.VerticalContainerParent.ParentMeasure);
                     // note: if the pitch is such that the voice entry frequencies aren't ordered correctly,
                     // note: if the pitch is such that the voice entry frequencies aren't ordered correctly,
                     // Vexflow will complain about unsorted pitches. see below
                     // Vexflow will complain about unsorted pitches. see below
                 voiceEntry.Notes.push(newNote);
                 voiceEntry.Notes.push(newNote);

+ 1 - 1
test/MusicalScore/VoiceData/NoteType_Test.ts

@@ -1,4 +1,4 @@
-import { NoteType, NoteTypeHandler } from "../../../src/MusicalScore/VoiceData";
+import { NoteType, NoteTypeHandler } from "../../../src/MusicalScore/VoiceData/NoteType";
 
 
 /* tslint:disable:no-unused-expression */
 /* tslint:disable:no-unused-expression */
 describe("NoteType", () => {
 describe("NoteType", () => {

+ 0 - 1
test/Util/visual_regression.sh

@@ -13,7 +13,6 @@
 #
 #
 #
 #
 #  First generate the known good or previous state PNG images you want to compare to, e.g. the develop branch or last release:
 #  First generate the known good or previous state PNG images you want to compare to, e.g. the develop branch or last release:
-#    (Server has to be running for this: npm start)
 #
 #
 #    npm run generate:blessed
 #    npm run generate:blessed
 #
 #

+ 377 - 0
test/data/OSMD_Function_Test_Tablature_Alleffects.musicxml

@@ -0,0 +1,377 @@
+<?xml version="1.0"?>
+<score-partwise version="3.1">
+    <work>
+        <work-title>OSMD Function Test Tablature - All Effects</work-title>
+    </work>
+    <identification>
+        <creator type="composer">momo</creator>
+        <encoding>
+            <software>www.guitartabcreator.com</software>
+            <encoding-date>2020-07-12</encoding-date>
+            <supports attribute="new-system" element="print" type="yes" value="yes" />
+            <supports attribute="new-page" element="print" type="yes" value="yes" />
+            <supports element="accidental" type="yes" />
+            <supports element="beam" type="yes" />
+            <supports element="stem" type="yes" />
+        </encoding>
+    </identification>
+    <defaults>
+        <scaling>
+            <millimeters>6.35</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1760</page-height>
+            <page-width>1360</page-width>
+            <page-margins type="both">
+                <left-margin>80</left-margin>
+                <right-margin>80</right-margin>
+                <top-margin>80</top-margin>
+                <bottom-margin>80</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <system-layout>
+            <system-margins>
+                <left-margin>0</left-margin>
+                <right-margin>0</right-margin>
+            </system-margins>
+            <system-distance>173</system-distance>
+            <top-system-distance>68</top-system-distance>
+        </system-layout>
+        <staff-layout>
+            <staff-distance>67</staff-distance>
+        </staff-layout>
+        <appearance>
+            <line-width type="stem">0.8333</line-width>
+            <line-width type="beam">5</line-width>
+            <line-width type="staff">1.25</line-width>
+            <line-width type="light barline">1.4583</line-width>
+            <line-width type="heavy barline">5</line-width>
+            <line-width type="leger">1.875</line-width>
+            <line-width type="ending">1.4583</line-width>
+            <line-width type="wedge">0.9375</line-width>
+            <line-width type="enclosure">1.45843</line-width>
+            <line-width type="tuplet bracket">1.4583</line-width>
+            <note-size type="grace">50</note-size>
+            <note-size type="cue">50</note-size>
+            <distance type="hyphen">60</distance>
+            <distance type="beam">8</distance>
+        </appearance>
+        <music-font font-size="18" font-family="Maestro,engraved" />
+        <word-font font-size="9" font-family="Times New Roman" />
+    </defaults>
+    <part-list>
+        <score-part id="P1">
+            <part-name print-object="no">Guitar</part-name>
+            <part-abbreviation print-object="no">Gtr.</part-abbreviation>
+            <score-instrument id="P1-I1">
+                <instrument-name>Acoustic Guitar (steel)</instrument-name>
+                <instrument-sound>pluck.guitar</instrument-sound>
+            </score-instrument>
+            <midi-instrument id="P1-I1">
+                <midi-channel>1</midi-channel>
+                <midi-program>26</midi-program>
+                <volume>80</volume>
+                <pan>0</pan>
+            </midi-instrument>
+        </score-part>
+    </part-list>
+    <part id="P1">
+        <measure number="1">
+            <print>
+                <page-layout>
+                    <page-height>1850</page-height>
+                    <page-width>1310</page-width>
+                    <page-margins>
+                        <left-margin>80</left-margin>
+                        <right-margin>727</right-margin>
+                        <top-margin>80</top-margin>
+                        <bottom-margin>80</bottom-margin>
+                    </page-margins>
+                </page-layout>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>68</left-margin>
+                        <right-margin>0</right-margin>
+                    </system-margins>
+                    <top-system-distance>187</top-system-distance>
+                </system-layout>
+                <measure-number>system</measure-number>
+            </print>
+            <attributes>
+                <divisions>2</divisions>
+                <key>
+                    <fifths>0</fifths>
+                    <mode>major</mode>
+                </key>
+                <time>
+                    <beats>4</beats>
+                    <beat-type>4</beat-type>
+                </time>
+                <staves>1</staves>
+                <clef number="1">
+                    <sign>TAB</sign>
+                    <line>5</line>
+                </clef>
+                <transpose>
+                    <diatonic>0</diatonic>
+                    <chromatic>0</chromatic>
+                    <octave-change>-1</octave-change>
+                </transpose>
+                <staff-details number="1">
+                    <staff-lines>6</staff-lines>
+                    <staff-tuning line="1">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="2">
+                        <tuning-step>A</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="3">
+                        <tuning-step>D</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="4">
+                        <tuning-step>G</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="5">
+                        <tuning-step>B</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="6">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>4</tuning-octave>
+                    </staff-tuning>
+                </staff-details>
+            </attributes>
+            <sound tempo="120" />
+            <note>
+                <pitch>
+                    <step>E</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>0</fret>
+                        <hammer-on number="1" type="start">H</hammer-on>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>1</fret>
+                        <hammer-on number="1" type="stop" />
+                        <bend>
+                            <bend-alter>4</bend-alter>
+                        </bend>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>2</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>2</fret>
+                        <pull-off number="1" type="start">P</pull-off>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>G</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>3</fret>
+                        <pull-off number="1" type="stop" />
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>A</step>
+                    <octave>2</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>6</fret>
+                    </technical>
+                    <slide type="start" number="6" />
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>A</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>5</fret>
+                    </technical>
+                    <slide type="stop" number="6" />
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>A</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>5</fret>
+                    </technical>
+                    <slide type="start" number="6" />
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>A</step>
+                    <octave>2</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>6</fret>
+                    </technical>
+                    <slide type="stop" number="6" />
+                    <ornaments>
+                        <wavy-line type="start" />
+                    </ornaments>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>B</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>7</fret>
+                    </technical>
+                    <ornaments>
+                        <wavy-line type="stop" />
+                    </ornaments>
+                </notations>
+            </note>
+        </measure>
+        <measure number="2">
+            <print />
+            <attributes />
+            <sound />
+            <note>
+                <pitch>
+                    <step>C</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>8</fret>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <rest></rest>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+            </note>
+            <note>
+                <pitch>
+                    <step>C</step>
+                    <octave>3</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>6</string>
+                        <fret>9</fret>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <rest></rest>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+            </note>
+        </measure>
+    </part>
+</score-partwise>

+ 233 - 0
test/data/OSMD_Function_Test_Tablature_Bends.musicxml

@@ -0,0 +1,233 @@
+<?xml version="1.0"?>
+<score-partwise version="3.1">
+    <work>
+        <work-title>OSMD Function Test Tablature - Bends</work-title>
+    </work>
+    <identification>
+        <creator type="composer">momo</creator>
+        <encoding>
+            <software>www.guitartabcreator.com</software>
+            <encoding-date>2020-06-14</encoding-date>
+            <supports attribute="new-system" element="print" type="yes" value="yes" />
+            <supports attribute="new-page" element="print" type="yes" value="yes" />
+            <supports element="accidental" type="yes" />
+            <supports element="beam" type="yes" />
+            <supports element="stem" type="yes" />
+        </encoding>
+    </identification>
+    <defaults>
+        <scaling>
+            <millimeters>6.35</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1760</page-height>
+            <page-width>1360</page-width>
+            <page-margins type="both">
+                <left-margin>80</left-margin>
+                <right-margin>80</right-margin>
+                <top-margin>80</top-margin>
+                <bottom-margin>80</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <system-layout>
+            <system-margins>
+                <left-margin>0</left-margin>
+                <right-margin>0</right-margin>
+            </system-margins>
+            <system-distance>173</system-distance>
+            <top-system-distance>68</top-system-distance>
+        </system-layout>
+        <staff-layout>
+            <staff-distance>67</staff-distance>
+        </staff-layout>
+        <appearance>
+            <line-width type="stem">0.8333</line-width>
+            <line-width type="beam">5</line-width>
+            <line-width type="staff">1.25</line-width>
+            <line-width type="light barline">1.4583</line-width>
+            <line-width type="heavy barline">5</line-width>
+            <line-width type="leger">1.875</line-width>
+            <line-width type="ending">1.4583</line-width>
+            <line-width type="wedge">0.9375</line-width>
+            <line-width type="enclosure">1.45843</line-width>
+            <line-width type="tuplet bracket">1.4583</line-width>
+            <note-size type="grace">50</note-size>
+            <note-size type="cue">50</note-size>
+            <distance type="hyphen">60</distance>
+            <distance type="beam">8</distance>
+        </appearance>
+        <music-font font-size="18" font-family="Maestro,engraved" />
+        <word-font font-size="9" font-family="Times New Roman" />
+    </defaults>
+    <part-list>
+        <score-part id="P1">
+            <part-name print-object="no">Guitar</part-name>
+            <part-abbreviation print-object="no">Gtr.</part-abbreviation>
+            <score-instrument id="P1-I1">
+                <instrument-name>Acoustic Guitar (steel)</instrument-name>
+                <instrument-sound>pluck.guitar</instrument-sound>
+            </score-instrument>
+            <midi-instrument id="P1-I1">
+                <midi-channel>1</midi-channel>
+                <midi-program>26</midi-program>
+                <volume>80</volume>
+                <pan>0</pan>
+            </midi-instrument>
+        </score-part>
+    </part-list>
+    <part id="P1">
+        <measure number="1">
+            <print>
+                <page-layout>
+                    <page-height>1850</page-height>
+                    <page-width>1310</page-width>
+                    <page-margins>
+                        <left-margin>80</left-margin>
+                        <right-margin>727</right-margin>
+                        <top-margin>80</top-margin>
+                        <bottom-margin>80</bottom-margin>
+                    </page-margins>
+                </page-layout>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>68</left-margin>
+                        <right-margin>0</right-margin>
+                    </system-margins>
+                    <top-system-distance>187</top-system-distance>
+                </system-layout>
+                <measure-number>system</measure-number>
+            </print>
+            <attributes>
+                <divisions>2</divisions>
+                <key>
+                    <fifths>0</fifths>
+                    <mode>major</mode>
+                </key>
+                <time>
+                    <beats>4</beats>
+                    <beat-type>4</beat-type>
+                </time>
+                <staves>1</staves>
+                <clef number="1">
+                    <sign>TAB</sign>
+                    <line>5</line>
+                </clef>
+                <transpose>
+                    <diatonic>0</diatonic>
+                    <chromatic>0</chromatic>
+                    <octave-change>-1</octave-change>
+                </transpose>
+                <staff-details number="1">
+                    <staff-lines>6</staff-lines>
+                    <staff-tuning line="1">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="2">
+                        <tuning-step>A</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="3">
+                        <tuning-step>D</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="4">
+                        <tuning-step>G</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="5">
+                        <tuning-step>B</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="6">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>4</tuning-octave>
+                    </staff-tuning>
+                </staff-details>
+            </attributes>
+            <sound tempo="120" />
+            <note>
+                <pitch>
+                    <step>E</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>2</fret>
+                        <bend>
+                            <bend-alter>4</bend-alter>
+                        </bend>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>3</fret>
+                        <bend>
+                            <bend-alter>4</bend-alter>
+                        </bend>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>3</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>4</fret>
+                        <bend>
+                            <bend-alter>4</bend-alter>
+                        </bend>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>G</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>5</fret>
+                        <bend>
+                            <bend-alter>4</bend-alter>
+                        </bend>
+                    </technical>
+                </notations>
+            </note>
+        </measure>
+    </part>
+</score-partwise>

+ 224 - 0
test/data/OSMD_Function_Test_Tablature_Hammeron_Pulloff.musicxml

@@ -0,0 +1,224 @@
+<?xml version="1.0"?>
+<score-partwise version="3.1">
+    <work>
+        <work-title>OSMD Function Test Tablature - Hammeron, Pulloff</work-title>
+    </work>
+    <identification>
+        <creator type="composer">momo</creator>
+        <encoding>
+            <software>www.guitartabcreator.com</software>
+            <encoding-date>2020-07-07</encoding-date>
+            <supports attribute="new-system" element="print" type="yes" value="yes" />
+            <supports attribute="new-page" element="print" type="yes" value="yes" />
+            <supports element="accidental" type="yes" />
+            <supports element="beam" type="yes" />
+            <supports element="stem" type="yes" />
+        </encoding>
+    </identification>
+    <defaults>
+        <scaling>
+            <millimeters>6.35</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1760</page-height>
+            <page-width>1360</page-width>
+            <page-margins type="both">
+                <left-margin>80</left-margin>
+                <right-margin>80</right-margin>
+                <top-margin>80</top-margin>
+                <bottom-margin>80</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <system-layout>
+            <system-margins>
+                <left-margin>0</left-margin>
+                <right-margin>0</right-margin>
+            </system-margins>
+            <system-distance>173</system-distance>
+            <top-system-distance>68</top-system-distance>
+        </system-layout>
+        <staff-layout>
+            <staff-distance>67</staff-distance>
+        </staff-layout>
+        <appearance>
+            <line-width type="stem">0.8333</line-width>
+            <line-width type="beam">5</line-width>
+            <line-width type="staff">1.25</line-width>
+            <line-width type="light barline">1.4583</line-width>
+            <line-width type="heavy barline">5</line-width>
+            <line-width type="leger">1.875</line-width>
+            <line-width type="ending">1.4583</line-width>
+            <line-width type="wedge">0.9375</line-width>
+            <line-width type="enclosure">1.45843</line-width>
+            <line-width type="tuplet bracket">1.4583</line-width>
+            <note-size type="grace">50</note-size>
+            <note-size type="cue">50</note-size>
+            <distance type="hyphen">60</distance>
+            <distance type="beam">8</distance>
+        </appearance>
+        <music-font font-size="18" font-family="Maestro,engraved" />
+        <word-font font-size="9" font-family="Times New Roman" />
+    </defaults>
+    <part-list>
+        <score-part id="P1">
+            <part-name print-object="no">Guitar</part-name>
+            <part-abbreviation print-object="no">Gtr.</part-abbreviation>
+            <score-instrument id="P1-I1">
+                <instrument-name>Acoustic Guitar (steel)</instrument-name>
+                <instrument-sound>pluck.guitar</instrument-sound>
+            </score-instrument>
+            <midi-instrument id="P1-I1">
+                <midi-channel>1</midi-channel>
+                <midi-program>26</midi-program>
+                <volume>80</volume>
+                <pan>0</pan>
+            </midi-instrument>
+        </score-part>
+    </part-list>
+    <part id="P1">
+        <measure number="1">
+            <print>
+                <page-layout>
+                    <page-height>1850</page-height>
+                    <page-width>1310</page-width>
+                    <page-margins>
+                        <left-margin>80</left-margin>
+                        <right-margin>727</right-margin>
+                        <top-margin>80</top-margin>
+                        <bottom-margin>80</bottom-margin>
+                    </page-margins>
+                </page-layout>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>68</left-margin>
+                        <right-margin>0</right-margin>
+                    </system-margins>
+                    <top-system-distance>187</top-system-distance>
+                </system-layout>
+                <measure-number>system</measure-number>
+            </print>
+            <attributes>
+                <divisions>2</divisions>
+                <key>
+                    <fifths>0</fifths>
+                    <mode>major</mode>
+                </key>
+                <time>
+                    <beats>4</beats>
+                    <beat-type>4</beat-type>
+                </time>
+                <staves>1</staves>
+                <clef number="1">
+                    <sign>TAB</sign>
+                    <line>5</line>
+                </clef>
+                <transpose>
+                    <diatonic>0</diatonic>
+                    <chromatic>0</chromatic>
+                    <octave-change>-1</octave-change>
+                </transpose>
+                <staff-details number="1">
+                    <staff-lines>6</staff-lines>
+                    <staff-tuning line="1">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="2">
+                        <tuning-step>A</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="3">
+                        <tuning-step>D</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="4">
+                        <tuning-step>G</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="5">
+                        <tuning-step>B</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="6">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>4</tuning-octave>
+                    </staff-tuning>
+                </staff-details>
+            </attributes>
+            <sound tempo="120" />
+            <note>
+                <pitch>
+                    <step>B</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>5</string>
+                        <fret>2</fret>
+                        <hammer-on number="1" type="start">H</hammer-on>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>C</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>5</string>
+                        <fret>3</fret>
+                        <hammer-on number="1" type="stop" />
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>D</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>5</string>
+                        <fret>5</fret>
+                        <pull-off number="1" type="start">P</pull-off>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>A</step>
+                    <octave>2</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>5</string>
+                        <fret>0</fret>
+                        <pull-off number="1" type="stop" />
+                    </technical>
+                </notations>
+            </note>
+        </measure>
+    </part>
+</score-partwise>

+ 228 - 0
test/data/OSMD_Function_Test_Tablature_Multibends.musicxml

@@ -0,0 +1,228 @@
+<?xml version="1.0"?>
+<score-partwise version="3.1">
+    <work>
+        <work-title>OSMD Function Test Tablature - Multibends</work-title>
+    </work>
+    <identification>
+        <creator type="composer">momo</creator>
+        <encoding>
+            <software>www.guitartabcreator.com</software>
+            <encoding-date>2020-07-12</encoding-date>
+            <supports attribute="new-system" element="print" type="yes" value="yes" />
+            <supports attribute="new-page" element="print" type="yes" value="yes" />
+            <supports element="accidental" type="yes" />
+            <supports element="beam" type="yes" />
+            <supports element="stem" type="yes" />
+        </encoding>
+    </identification>
+    <defaults>
+        <scaling>
+            <millimeters>6.35</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1760</page-height>
+            <page-width>1360</page-width>
+            <page-margins type="both">
+                <left-margin>80</left-margin>
+                <right-margin>80</right-margin>
+                <top-margin>80</top-margin>
+                <bottom-margin>80</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <system-layout>
+            <system-margins>
+                <left-margin>0</left-margin>
+                <right-margin>0</right-margin>
+            </system-margins>
+            <system-distance>173</system-distance>
+            <top-system-distance>68</top-system-distance>
+        </system-layout>
+        <staff-layout>
+            <staff-distance>67</staff-distance>
+        </staff-layout>
+        <appearance>
+            <line-width type="stem">0.8333</line-width>
+            <line-width type="beam">5</line-width>
+            <line-width type="staff">1.25</line-width>
+            <line-width type="light barline">1.4583</line-width>
+            <line-width type="heavy barline">5</line-width>
+            <line-width type="leger">1.875</line-width>
+            <line-width type="ending">1.4583</line-width>
+            <line-width type="wedge">0.9375</line-width>
+            <line-width type="enclosure">1.45843</line-width>
+            <line-width type="tuplet bracket">1.4583</line-width>
+            <note-size type="grace">50</note-size>
+            <note-size type="cue">50</note-size>
+            <distance type="hyphen">60</distance>
+            <distance type="beam">8</distance>
+        </appearance>
+        <music-font font-size="18" font-family="Maestro,engraved" />
+        <word-font font-size="9" font-family="Times New Roman" />
+    </defaults>
+    <part-list>
+        <score-part id="P1">
+            <part-name print-object="no">Guitar</part-name>
+            <part-abbreviation print-object="no">Gtr.</part-abbreviation>
+            <score-instrument id="P1-I1">
+                <instrument-name>Acoustic Guitar (steel)</instrument-name>
+                <instrument-sound>pluck.guitar</instrument-sound>
+            </score-instrument>
+            <midi-instrument id="P1-I1">
+                <midi-channel>1</midi-channel>
+                <midi-program>26</midi-program>
+                <volume>80</volume>
+                <pan>0</pan>
+            </midi-instrument>
+        </score-part>
+    </part-list>
+    <part id="P1">
+        <measure number="1">
+            <print>
+                <page-layout>
+                    <page-height>1850</page-height>
+                    <page-width>1310</page-width>
+                    <page-margins>
+                        <left-margin>80</left-margin>
+                        <right-margin>727</right-margin>
+                        <top-margin>80</top-margin>
+                        <bottom-margin>80</bottom-margin>
+                    </page-margins>
+                </page-layout>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>68</left-margin>
+                        <right-margin>0</right-margin>
+                    </system-margins>
+                    <top-system-distance>187</top-system-distance>
+                </system-layout>
+                <measure-number>system</measure-number>
+            </print>
+            <attributes>
+                <divisions>2</divisions>
+                <key>
+                    <fifths>0</fifths>
+                    <mode>major</mode>
+                </key>
+                <time>
+                    <beats>4</beats>
+                    <beat-type>4</beat-type>
+                </time>
+                <staves>1</staves>
+                <clef number="1">
+                    <sign>TAB</sign>
+                    <line>5</line>
+                </clef>
+                <transpose>
+                    <diatonic>0</diatonic>
+                    <chromatic>0</chromatic>
+                    <octave-change>-1</octave-change>
+                </transpose>
+                <staff-details number="1">
+                    <staff-lines>6</staff-lines>
+                    <staff-tuning line="1">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="2">
+                        <tuning-step>A</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="3">
+                        <tuning-step>D</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="4">
+                        <tuning-step>G</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="5">
+                        <tuning-step>B</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="6">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>4</tuning-octave>
+                    </staff-tuning>
+                </staff-details>
+            </attributes>
+            <sound tempo="120" />
+            <note>
+                <pitch>
+                    <step>E</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>2</fret>
+                        <bend>
+                            <bend-alter>4</bend-alter>
+                        </bend>
+                        <bend>
+                            <bend-alter>2</bend-alter>
+                            <release></release>
+                        </bend>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>3</fret>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>3</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>4</fret>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>G</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>5</fret>
+                    </technical>
+                </notations>
+            </note>
+        </measure>
+    </part>
+</score-partwise>

+ 189 - 0
test/data/OSMD_Function_Test_Tablature_Slides.musicxml

@@ -0,0 +1,189 @@
+<?xml version="1.0"?>
+<score-partwise version="3.1">
+    <work>
+        <work-title>OSMD Function Test Tablature - Slides</work-title>
+    </work>
+    <identification>
+        <creator type="composer">momo</creator>
+        <encoding>
+            <software>www.guitartabcreator.com</software>
+            <encoding-date>2020-06-16</encoding-date>
+            <supports attribute="new-system" element="print" type="yes" value="yes" />
+            <supports attribute="new-page" element="print" type="yes" value="yes" />
+            <supports element="accidental" type="yes" />
+            <supports element="beam" type="yes" />
+            <supports element="stem" type="yes" />
+        </encoding>
+    </identification>
+    <defaults>
+        <scaling>
+            <millimeters>6.35</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1760</page-height>
+            <page-width>1360</page-width>
+            <page-margins type="both">
+                <left-margin>80</left-margin>
+                <right-margin>80</right-margin>
+                <top-margin>80</top-margin>
+                <bottom-margin>80</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <system-layout>
+            <system-margins>
+                <left-margin>0</left-margin>
+                <right-margin>0</right-margin>
+            </system-margins>
+            <system-distance>173</system-distance>
+            <top-system-distance>68</top-system-distance>
+        </system-layout>
+        <staff-layout>
+            <staff-distance>67</staff-distance>
+        </staff-layout>
+        <appearance>
+            <line-width type="stem">0.8333</line-width>
+            <line-width type="beam">5</line-width>
+            <line-width type="staff">1.25</line-width>
+            <line-width type="light barline">1.4583</line-width>
+            <line-width type="heavy barline">5</line-width>
+            <line-width type="leger">1.875</line-width>
+            <line-width type="ending">1.4583</line-width>
+            <line-width type="wedge">0.9375</line-width>
+            <line-width type="enclosure">1.45843</line-width>
+            <line-width type="tuplet bracket">1.4583</line-width>
+            <note-size type="grace">50</note-size>
+            <note-size type="cue">50</note-size>
+            <distance type="hyphen">60</distance>
+            <distance type="beam">8</distance>
+        </appearance>
+        <music-font font-size="18" font-family="Maestro,engraved" />
+        <word-font font-size="9" font-family="Times New Roman" />
+    </defaults>
+    <part-list>
+        <score-part id="P1">
+            <part-name print-object="no">Guitar</part-name>
+            <part-abbreviation print-object="no">Gtr.</part-abbreviation>
+            <score-instrument id="P1-I1">
+                <instrument-name>Acoustic Guitar (steel)</instrument-name>
+                <instrument-sound>pluck.guitar</instrument-sound>
+            </score-instrument>
+            <midi-instrument id="P1-I1">
+                <midi-channel>1</midi-channel>
+                <midi-program>26</midi-program>
+                <volume>80</volume>
+                <pan>0</pan>
+            </midi-instrument>
+        </score-part>
+    </part-list>
+    <part id="P1">
+        <measure number="1">
+            <print>
+                <page-layout>
+                    <page-height>1850</page-height>
+                    <page-width>1310</page-width>
+                    <page-margins>
+                        <left-margin>80</left-margin>
+                        <right-margin>727</right-margin>
+                        <top-margin>80</top-margin>
+                        <bottom-margin>80</bottom-margin>
+                    </page-margins>
+                </page-layout>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>68</left-margin>
+                        <right-margin>0</right-margin>
+                    </system-margins>
+                    <top-system-distance>187</top-system-distance>
+                </system-layout>
+                <measure-number>system</measure-number>
+            </print>
+            <attributes>
+                <divisions>2</divisions>
+                <key>
+                    <fifths>0</fifths>
+                    <mode>major</mode>
+                </key>
+                <time>
+                    <beats>4</beats>
+                    <beat-type>4</beat-type>
+                </time>
+                <staves>1</staves>
+                <clef number="1">
+                    <sign>TAB</sign>
+                    <line>5</line>
+                </clef>
+                <transpose>
+                    <diatonic>0</diatonic>
+                    <chromatic>0</chromatic>
+                    <octave-change>-1</octave-change>
+                </transpose>
+                <staff-details number="1">
+                    <staff-lines>6</staff-lines>
+                    <staff-tuning line="1">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="2">
+                        <tuning-step>A</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="3">
+                        <tuning-step>D</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="4">
+                        <tuning-step>G</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="5">
+                        <tuning-step>B</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="6">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>4</tuning-octave>
+                    </staff-tuning>
+                </staff-details>
+            </attributes>
+            <sound tempo="120" />
+            <note>
+                <pitch>
+                    <step>E</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>1</string>
+                        <fret>0</fret>
+                    </technical>
+                    <slide type="start" number="1" />
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>4</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>1</string>
+                        <fret>1</fret>
+                    </technical>
+                    <slide type="stop" number="1" />
+                </notations>
+            </note>
+
+        </measure>
+    </part>
+</score-partwise>

+ 193 - 0
test/data/OSMD_Function_Test_Tablature_Vibrato.musicxml

@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<score-partwise version="3.1">
+    <work>
+        <work-title>OSMD Function Test Tablature - Vibrato</work-title>
+    </work>
+    <identification>
+        <creator type="composer">momo</creator>
+        <encoding>
+            <software>Guitartabcreator.com</software>
+            <encoding-date>2020-05-24</encoding-date>
+            <supports type="yes" value="yes" element="print" attribute="new-system" />
+            <supports type="yes" value="yes" element="print" attribute="new-page" />
+            <supports type="yes" element="accidental" />
+            <supports type="yes" element="beam" />
+            <supports type="yes" element="stem" />
+        </encoding>
+    </identification>
+    <defaults>
+        <scaling>
+            <millimeters>6.35</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1760</page-height>
+            <page-width>1360</page-width>
+            <page-margins type="both">
+                <left-margin>80</left-margin>
+                <right-margin>80</right-margin>
+                <top-margin>80</top-margin>
+                <bottom-margin>80</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <system-layout>
+            <system-margins>
+                <left-margin>0</left-margin>
+                <right-margin>0</right-margin>
+            </system-margins>
+            <system-distance>173</system-distance>
+            <top-system-distance>68</top-system-distance>
+        </system-layout>
+        <staff-layout>
+            <staff-distance>67</staff-distance>
+        </staff-layout>
+        <appearance>
+            <line-width type="stem">0.8333</line-width>
+            <line-width type="beam">5</line-width>
+            <line-width type="staff">1.25</line-width>
+            <line-width type="light barline">1.4583</line-width>
+            <line-width type="heavy barline">5</line-width>
+            <line-width type="leger">1.875</line-width>
+            <line-width type="ending">1.4583</line-width>
+            <line-width type="wedge">0.9375</line-width>
+            <line-width type="enclosure">1.45843</line-width>
+            <line-width type="tuplet bracket">1.4583</line-width>
+            <note-size type="grace">50</note-size>
+            <note-size type="cue">50</note-size>
+            <distance type="hyphen">60</distance>
+            <distance type="beam">8</distance>
+        </appearance>
+        <music-font font-family="Maestro,engraved" font-size="18" />
+        <word-font font-family="Times New Roman" font-size="9" />
+    </defaults>
+    <part-list>
+        <score-part id="P1">
+            <part-name print-object="no">Guitar</part-name>
+            <part-abbreviation print-object="no">Gtr.</part-abbreviation>
+            <score-instrument id="P1-I1">
+                <instrument-name>Acoustic Guitar (steel)</instrument-name>
+                <instrument-sound>pluck.guitar</instrument-sound>
+            </score-instrument>
+            <midi-instrument id="P1-I1">
+                <midi-channel>1</midi-channel>
+                <midi-program>26</midi-program>
+                <volume>80</volume>
+                <pan>0</pan>
+            </midi-instrument>
+        </score-part>
+    </part-list>
+    <part id="P1">
+        <measure width="485" number="1">
+            <print>
+                <page-layout>
+                    <page-height>1850</page-height>
+                    <page-width>1310</page-width>
+                    <page-margins>
+                        <left-margin>80</left-margin>
+                        <right-margin>727</right-margin>
+                        <top-margin>80</top-margin>
+                        <bottom-margin>80</bottom-margin>
+                    </page-margins>
+                </page-layout>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>68</left-margin>
+                        <right-margin>0</right-margin>
+                    </system-margins>
+                    <top-system-distance>187</top-system-distance>
+                </system-layout>
+                <measure-number>system</measure-number>
+            </print>
+            <attributes>
+                <divisions>2</divisions>
+                <key>
+                    <fifths>0</fifths>
+                    <mode>major</mode>
+                </key>
+                <time>
+                    <beats>4</beats>
+                    <beat-type>4</beat-type>
+                </time>
+                <staves>1</staves>
+                <clef number="1">
+                    <sign>TAB</sign>
+                    <line>5</line>
+                </clef>
+                <transpose>
+                    <diatonic>0</diatonic>
+                    <chromatic>0</chromatic>
+                    <octave-change>-1</octave-change>
+                </transpose>
+                <staff-details number="1">
+                    <staff-lines>6</staff-lines>
+                    <staff-tuning line="1">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="2">
+                        <tuning-step>A</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="3">
+                        <tuning-step>D</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="4">
+                        <tuning-step>G</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="5">
+                        <tuning-step>B</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="6">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>4</tuning-octave>
+                    </staff-tuning>
+                </staff-details>
+            </attributes>
+            <sound tempo="120" />
+            <note>
+                <pitch>
+                    <step>D</step>
+                    <octave>3</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>1</fret>
+                    </technical>
+                    <ornaments>
+                        <wavy-line type="start" />
+                    </ornaments>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>E</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>2</fret>
+                    </technical>
+                    <ornaments>
+                        <wavy-line type="stop" />
+                    </ornaments>
+                </notations>
+            </note>
+        </measure>
+    </part>
+</score-partwise>

+ 18 - 0
test/data/OSMD_function_test_all.xml

@@ -83,6 +83,15 @@
           <line>2</line>
           <line>2</line>
           </clef>
           </clef>
         </attributes>
         </attributes>
+      <direction placement="above">
+        <direction-type>
+          <metronome>
+            <beat-unit>quarter</beat-unit>
+            <per-minute>60</per-minute>
+          </metronome>
+        </direction-type>
+        <sound tempo="40"/>
+      </direction>
       <note default-x="114.16" default-y="-15.00">
       <note default-x="114.16" default-y="-15.00">
         <pitch>
         <pitch>
           <step>C</step>
           <step>C</step>
@@ -354,6 +363,15 @@
           <system-distance>97.03</system-distance>
           <system-distance>97.03</system-distance>
           </system-layout>
           </system-layout>
         </print>
         </print>
+      <direction placement="above">
+        <direction-type>
+          <metronome>
+            <beat-unit>half</beat-unit>
+            <per-minute>110</per-minute>
+          </metronome>
+        </direction-type>
+        <sound tempo="40"/>
+      </direction>
       <note default-x="96.95" default-y="-15.00">
       <note default-x="96.95" default-y="-15.00">
         <pitch>
         <pitch>
           <step>C</step>
           <step>C</step>

BIN
test/data/OSMD_function_test_metronome_marks.mxl


+ 2 - 2
test/data/Slurtest_highNotes.musicxml

@@ -2,10 +2,10 @@
 <!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
 <!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
 <score-partwise version="3.1">
 <score-partwise version="3.1">
   <work>
   <work>
-    <work-title>Title</work-title>
+    <work-title>Slur Test - High Notes</work-title>
     </work>
     </work>
   <identification>
   <identification>
-    <creator type="composer">Composer</creator>
+    <creator type="composer">OSMD</creator>
     <encoding>
     <encoding>
       <software>MuseScore 3.0.2</software>
       <software>MuseScore 3.0.2</software>
       <encoding-date>2020-06-19</encoding-date>
       <encoding-date>2020-06-19</encoding-date>

+ 154 - 0
test/data/Test_Auto_Multirest_1.musicxml

@@ -0,0 +1,154 @@
+<?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 - Subsequent Measure Rests</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.4.2</software>
+      <encoding-date>2020-08-11</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7.05556</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1683.78</page-height>
+      <page-width>1190.55</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="595.275" default-y="1627.09" justify="center" valign="top" font-size="24">Test Subsequent Measure Rests</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="210.55">
+      <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>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>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="2" width="164.68">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="3" width="207.88">
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        </note>
+      <note default-x="77.81" 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>
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      </measure>
+    <measure number="4" width="164.68">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="5" width="164.68">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="6" width="164.68">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 999 - 0
test/data/Test_Auto_Multirest_2.musicxml

@@ -0,0 +1,999 @@
+<?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 - Various Auto Multirest Scenarios</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.4.2</software>
+      <encoding-date>2020-08-11</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7.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>
+  <credit page="1">
+    <credit-words default-x="595.44" default-y="1626.67" justify="center" valign="top" font-size="24">Test Diff Auto Multirest scenarios</credit-words>
+    </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Voice</part-name>
+      <part-abbreviation>Vo.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Voice</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>53</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <score-part id="P2">
+      <part-name>Piano</part-name>
+      <part-abbreviation>Pno.</part-abbreviation>
+      <score-instrument id="P2-I1">
+        <instrument-name>Piano</instrument-name>
+        </score-instrument>
+      <midi-device id="P2-I1" port="1"></midi-device>
+      <midi-instrument id="P2-I1">
+        <midi-channel>2</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <score-part id="P3">
+      <part-name>Contrabass</part-name>
+      <part-abbreviation>Cb.</part-abbreviation>
+      <score-instrument id="P3-I1">
+        <instrument-name>Contrabass</instrument-name>
+        </score-instrument>
+      <midi-device id="P3-I1" port="1"></midi-device>
+      <midi-instrument id="P3-I1">
+        <midi-channel>3</midi-channel>
+        <midi-program>44</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="172.87">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>134.20</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.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>
+      <note default-x="82.47" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="104.35" 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>
+      <note default-x="126.23" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="148.11" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <tie type="start"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <notations>
+          <tied type="start"/>
+          </notations>
+        </note>
+      </measure>
+    <measure number="2" width="84.93">
+      <note default-x="10.00" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <tie type="stop"/>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <notations>
+          <tied type="stop"/>
+          </notations>
+        </note>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        </note>
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      </measure>
+    <measure number="3" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="4" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="5" width="100.41">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="6" width="85.66">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="7" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="8" width="102.81">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="9" width="158.17">
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      </measure>
+    <measure number="10" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="11" width="192.14">
+      <print new-system="yes">
+        <system-layout>
+          <system-margins>
+            <left-margin>72.60</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <system-distance>150.00</system-distance>
+          </system-layout>
+        </print>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="12" width="196.09">
+      <note default-x="10.00" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        </note>
+      <note default-x="102.24" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="148.37" 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>
+      </measure>
+    <measure number="13" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="14" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="15" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="16" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  <part id="P2">
+    <measure number="1" width="172.87">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        <staff-layout number="2">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>2</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <staves>2</staves>
+        <clef number="1">
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        <clef number="2">
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="2" width="84.93">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="3" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="4" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="5" width="100.41">
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <staff>1</staff>
+        </note>
+      <note default-x="53.40" default-y="-140.00">
+        <pitch>
+          <step>F</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <note default-x="53.40" default-y="-130.00">
+        <chord/>
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <note default-x="53.40" default-y="-120.00">
+        <chord/>
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note default-x="10.00" default-y="-245.00">
+        <pitch>
+          <step>A</step>
+          <octave>2</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>2</staff>
+        </note>
+      <note default-x="31.88" default-y="-230.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>2</staff>
+        </note>
+      <note default-x="53.76" default-y="-225.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>2</staff>
+        </note>
+      <note default-x="75.64" default-y="-220.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <tie type="start"/>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>2</staff>
+        <notations>
+          <tied type="start"/>
+          </notations>
+        </note>
+      </measure>
+    <measure number="6" width="85.66">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note default-x="10.72" default-y="-220.00">
+        <pitch>
+          <step>F</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <tie type="stop"/>
+        <voice>5</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>2</staff>
+        <notations>
+          <tied type="stop"/>
+          </notations>
+        </note>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <type>quarter</type>
+        <staff>2</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>5</voice>
+        <type>half</type>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="7" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="8" width="102.81">
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="9" width="158.17">
+      <note default-x="10.00" default-y="-120.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <staff>1</staff>
+        </note>
+      <note default-x="48.57" default-y="-135.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        </note>
+      <note default-x="67.86" default-y="-145.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+        </note>
+      <note default-x="87.14" default-y="-145.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        <beam number="1">begin</beam>
+        </note>
+      <note default-x="106.43" default-y="-130.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>eighth</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        <beam number="1">end</beam>
+        </note>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="10" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="11" width="192.14">
+      <print new-system="yes">
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        <staff-layout number="2">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="12" width="196.09">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="13" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="14" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="15" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="16" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>8</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  <part id="P3">
+    <measure number="1" width="172.87">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>2</divisions>
+        <key>
+          <fifths>0</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        <transpose>
+          <diatonic>0</diatonic>
+          <chromatic>0</chromatic>
+          <octave-change>-1</octave-change>
+          </transpose>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="2" width="84.93">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="3" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="4" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="5" width="100.41">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="6" width="85.66">
+      <note default-x="10.36" default-y="-330.00">
+        <pitch>
+          <step>E</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="51.10" default-y="-335.00">
+        <pitch>
+          <step>D</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="7" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="8" width="102.81">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="9" width="158.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="10" width="59.61">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="11" width="192.14">
+      <print new-system="yes">
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="12" width="196.09">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="13" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="14" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="15" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="16" width="154.17">
+      <note>
+        <rest/>
+        <duration>8</duration>
+        <voice>1</voice>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 455 - 0
test/data/Tuplet_placement_test.xml

@@ -0,0 +1,455 @@
+<?xml version="1.0" encoding='UTF-8' standalone='no' ?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.0">
+ <work>
+  <work-title>Tuplet placement test</work-title>
+ </work>
+ <identification>
+  <rights>Copyright © </rights>
+  <encoding>
+   <encoding-date>2020-08-14</encoding-date>
+   <encoder>DONG JAE KIM</encoder>
+   <software>Sibelius 19.1.0</software>
+   <software>Direct export, not from Dolet</software>
+   <encoding-description>Sibelius / MusicXML 3.0</encoding-description>
+   <supports element="print" type="yes" value="yes" attribute="new-system" />
+   <supports element="print" type="yes" value="yes" attribute="new-page" />
+   <supports element="accidental" type="yes" />
+   <supports element="beam" type="yes" />
+   <supports element="stem" type="yes" />
+  </encoding>
+ </identification>
+ <defaults>
+  <scaling>
+   <millimeters>210</millimeters>
+   <tenths>1200</tenths>
+  </scaling>
+  <page-layout>
+   <page-height>1697</page-height>
+   <page-width>1200</page-width>
+   <page-margins type="both">
+    <left-margin>72</left-margin>
+    <right-margin>72</right-margin>
+    <top-margin>72</top-margin>
+    <bottom-margin>72</bottom-margin>
+   </page-margins>
+  </page-layout>
+  <system-layout>
+   <system-margins>
+    <left-margin>63</left-margin>
+    <right-margin>0</right-margin>
+   </system-margins>
+   <system-distance>92</system-distance>
+  </system-layout>
+  <appearance>
+   <line-width type="stem">0.9375</line-width>
+   <line-width type="beam">5</line-width>
+   <line-width type="staff">0.9375</line-width>
+   <line-width type="light barline">1.5625</line-width>
+   <line-width type="heavy barline">5</line-width>
+   <line-width type="leger">1.5625</line-width>
+   <line-width type="ending">1.5625</line-width>
+   <line-width type="wedge">1.25</line-width>
+   <line-width type="enclosure">0.9375</line-width>
+   <line-width type="tuplet bracket">1.25</line-width>
+   <line-width type="bracket">5</line-width>
+   <line-width type="dashes">1.5625</line-width>
+   <line-width type="extend">0.9375</line-width>
+   <line-width type="octave shift">1.5625</line-width>
+   <line-width type="pedal">1.5625</line-width>
+   <line-width type="slur middle">1.5625</line-width>
+   <line-width type="slur tip">0.625</line-width>
+   <line-width type="tie middle">1.5625</line-width>
+   <line-width type="tie tip">0.625</line-width>
+   <note-size type="cue">75</note-size>
+   <note-size type="grace">60</note-size>
+  </appearance>
+  <music-font font-family="Opus Std" font-size="19.8425" />
+  <lyric-font font-family="Times New Roman" font-size="11.4715" />
+  <lyric-language xml:lang="en" />
+ </defaults>
+ <credit page="1">
+  <credit-words default-x="600" default-y="155" font-family="Times New Roman" font-style="normal" font-size="22.0128" font-weight="normal" justify="center" valign="middle">Tuplet placement test</credit-words>
+ </credit>
+ <part-list>
+  <part-group type="start" number="1">
+   <group-symbol>brace</group-symbol>
+  </part-group>
+  <score-part id="P1">
+   <part-name>Piano</part-name>
+   <part-name-display>
+    <display-text>Piano</display-text>
+   </part-name-display>
+   <part-abbreviation>Pno.</part-abbreviation>
+   <part-abbreviation-display>
+    <display-text>Pno.</display-text>
+   </part-abbreviation-display>
+   <score-instrument id="P1-I1">
+    <instrument-name>Piano (2)</instrument-name>
+    <instrument-sound>keyboard.piano.grand</instrument-sound>
+    <solo />
+    <virtual-instrument>
+     <virtual-library>General MIDI</virtual-library>
+     <virtual-name>Acoustic Piano</virtual-name>
+    </virtual-instrument>
+   </score-instrument>
+  </score-part>
+  <part-group type="stop" number="1" />
+ </part-list>
+ <part id="P1">
+  <!--============== Part: P1, Measure: 1 ==============-->
+  <measure number="1" width="979">
+   <print new-page="yes">
+    <system-layout>
+     <system-margins>
+      <left-margin>76</left-margin>
+      <right-margin>0</right-margin>
+     </system-margins>
+     <top-system-distance>218</top-system-distance>
+    </system-layout>
+   </print>
+   <attributes>
+    <divisions>256</divisions>
+    <key color="#000000">
+     <fifths>0</fifths>
+     <mode>major</mode>
+    </key>
+    <time color="#000000">
+     <beats>4</beats>
+     <beat-type>4</beat-type>
+    </time>
+    <staves>1</staves>
+    <clef number="1" color="#000000">
+     <sign>G</sign>
+     <line>2</line>
+    </clef>
+    <staff-details number="1" print-object="yes" />
+   </attributes>
+   <note color="#000000" default-x="57" default-y="-52">
+    <pitch>
+     <step>A</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>85</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>eighth</type>
+    <time-modification>
+     <actual-notes>3</actual-notes>
+     <normal-notes>2</normal-notes>
+     <normal-type>eighth</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">begin</beam>
+    <notations>
+     <tuplet type="start" bracket="no" number="1" default-y="-20" placement="below" />
+    </notations>
+   </note>
+   <note color="#000000" default-x="119" default-y="-52">
+    <pitch>
+     <step>C</step>
+     <octave>5</octave>
+    </pitch>
+    <duration>85</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>eighth</type>
+    <time-modification>
+     <actual-notes>3</actual-notes>
+     <normal-notes>2</normal-notes>
+     <normal-type>eighth</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+   </note>
+   <note color="#000000" default-x="181" default-y="-52">
+    <pitch>
+     <step>C</step>
+     <octave>5</octave>
+    </pitch>
+    <duration>86</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>eighth</type>
+    <time-modification>
+     <actual-notes>3</actual-notes>
+     <normal-notes>2</normal-notes>
+     <normal-type>eighth</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">end</beam>
+    <notations>
+     <tuplet type="stop" bracket="no" number="1" default-y="-20" placement="below" />
+    </notations>
+   </note>
+   <note color="#000000" default-x="243" default-y="9">
+    <pitch>
+     <step>D</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>85</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>eighth</type>
+    <time-modification>
+     <actual-notes>3</actual-notes>
+     <normal-notes>2</normal-notes>
+     <normal-type>eighth</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">begin</beam>
+    <notations>
+     <tuplet type="start" bracket="no" number="1" default-y="-20" placement="above" />
+    </notations>
+   </note>
+   <note color="#000000" default-x="305" default-y="9">
+    <pitch>
+     <step>G</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>85</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>eighth</type>
+    <time-modification>
+     <actual-notes>3</actual-notes>
+     <normal-notes>2</normal-notes>
+     <normal-type>eighth</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+   </note>
+   <note color="#000000" default-x="367" default-y="9">
+    <pitch>
+     <step>A</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>86</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>eighth</type>
+    <time-modification>
+     <actual-notes>3</actual-notes>
+     <normal-notes>2</normal-notes>
+     <normal-type>eighth</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">end</beam>
+    <notations>
+     <tuplet type="stop" bracket="no" number="1" default-y="-20" placement="above" />
+    </notations>
+   </note>
+   <note color="#000000" default-x="429" default-y="9">
+    <pitch>
+     <step>A</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">begin</beam>
+    <beam number="2">begin</beam>
+    <notations>
+     <tuplet type="start" bracket="no" number="1" default-y="-20" placement="above" />
+    </notations>
+   </note>
+   <note color="#000000" default-x="482" default-y="9">
+    <pitch>
+     <step>G</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+    <beam number="2">continue</beam>
+   </note>
+   <note color="#000000" default-x="536" default-y="9">
+    <pitch>
+     <step>G</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+    <beam number="2">continue</beam>
+   </note>
+   <note color="#000000" default-x="589" default-y="9">
+    <pitch>
+     <step>A</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+    <beam number="2">continue</beam>
+   </note>
+   <note color="#000000" default-x="643" default-y="9">
+    <pitch>
+     <step>B</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>52</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>up</stem>
+    <staff>1</staff>
+    <beam number="1">end</beam>
+    <beam number="2">end</beam>
+    <notations>
+     <tuplet type="stop" bracket="no" number="1" default-y="-20" placement="above" />
+    </notations>
+   </note>
+   <note color="#000000" default-x="696" default-y="-52">
+    <pitch>
+     <step>D</step>
+     <octave>5</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">begin</beam>
+    <beam number="2">begin</beam>
+    <notations>
+     <tuplet type="start" bracket="no" number="1" default-y="-20" placement="below" />
+    </notations>
+   </note>
+   <note color="#000000" default-x="750" default-y="-52">
+    <pitch>
+     <step>F</step>
+     <octave>5</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+    <beam number="2">continue</beam>
+   </note>
+   <note color="#000000" default-x="803" default-y="-52">
+    <pitch>
+     <step>F</step>
+     <octave>5</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+    <beam number="2">continue</beam>
+   </note>
+   <note color="#000000" default-x="857" default-y="-52">
+    <pitch>
+     <step>B</step>
+     <octave>4</octave>
+    </pitch>
+    <duration>51</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">continue</beam>
+    <beam number="2">continue</beam>
+   </note>
+   <note color="#000000" default-x="910" default-y="-52">
+    <pitch>
+     <step>D</step>
+     <octave>5</octave>
+    </pitch>
+    <duration>52</duration>
+    <instrument id="P1-I1" />
+    <voice>1</voice>
+    <type>16th</type>
+    <time-modification>
+     <actual-notes>5</actual-notes>
+     <normal-notes>4</normal-notes>
+     <normal-type>16th</normal-type>
+    </time-modification>
+    <stem>down</stem>
+    <staff>1</staff>
+    <beam number="1">end</beam>
+    <beam number="2">end</beam>
+    <notations>
+     <tuplet type="stop" bracket="no" number="1" default-y="-20" placement="below" />
+    </notations>
+   </note>
+   <barline>
+    <bar-style>light-heavy</bar-style>
+   </barline>
+  </measure>
+ </part>
+</score-partwise>

+ 1 - 1
test/data/tabs_bend_and_release.musicxml

@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <?xml version="1.0"?>
 <score-partwise version="3.1">
 <score-partwise version="3.1">
     <work>
     <work>
-        <work-title>bend release</work-title>
+        <work-title>OSMD Function Test Tablature - Bend, Release</work-title>
     </work>
     </work>
     <identification>
     <identification>
         <encoding>
         <encoding>

Some files were not shown because too many files changed in this diff