소스 검색

Merge branch 'develop' of github.com:opensheetmusicdisplay/opensheetmusicdisplay into develop

Matthias Uiberacker 5 년 전
부모
커밋
57f39b0e0b

+ 1 - 1
package.json

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

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

@@ -114,6 +114,7 @@ export class EngravingRules {
     private repetitionEndingLabelYOffset: number;
     private repetitionEndingLineYLowerOffset: number;
     private repetitionEndingLineYUpperOffset: number;
+    private voltaOffset: number;
     /** Default alignment of lyrics.
      * Left alignments will extend text to the right of the bounding box,
      * which facilitates spacing by extending measure width.
@@ -364,6 +365,7 @@ export class EngravingRules {
         this.repetitionEndingLabelYOffset = 0.3;
         this.repetitionEndingLineYLowerOffset = 0.5;
         this.repetitionEndingLineYUpperOffset = 0.3;
+        this.voltaOffset = 2.5;
 
         // Lyrics
         this.lyricsAlignmentStandard = TextAlignmentEnum.LeftBottom; // CenterBottom and LeftBottom tested, spacing-optimized
@@ -1017,6 +1019,12 @@ export class EngravingRules {
     public set RepetitionEndingLineYUpperOffset(value: number) {
         this.repetitionEndingLineYUpperOffset = value;
     }
+    public get VoltaOffset(): number {
+        return this.voltaOffset;
+    }
+    public set VoltaOffset(value: number) {
+        this.voltaOffset = value;
+    }
     public get LyricsAlignmentStandard(): TextAlignmentEnum {
         return this.lyricsAlignmentStandard;
     }

+ 5 - 4
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -790,15 +790,16 @@ export class GraphicalMusicSheet {
         }
         let previousStaffEntryMusicSystem: MusicSystem = undefined;
         if (previousStaffEntry !== undefined) {
-            previousStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+            // TODO sometimes one of these ParentStaffLine is undefined, either fix this or handle it here
+            previousStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         } else {
-            previousStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+            previousStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         }
         let nextStaffEntryMusicSystem: MusicSystem = undefined;
         if (nextStaffEntry !== undefined) {
-            nextStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+            nextStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         } else {
-            nextStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+            nextStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine?.ParentMusicSystem;
         }
         if (previousStaffEntryMusicSystem === nextStaffEntryMusicSystem) {
             currentMusicSystem = previousStaffEntryMusicSystem;

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

@@ -633,7 +633,7 @@ export abstract class MusicSheetCalculator {
         this.formatMeasures();
 
         // check for Measures with only WholeRestNotes and correct their X-Position (middle of Measure)
-        this.checkMeasuresForWholeRestNotes();
+        // this.checkMeasuresForWholeRestNotes(); // this currently does nothing
         if (!this.leadSheet) {
             // calculate Beam Placement
             // this.calculateBeams(); // does nothing for now, because layoutBeams() is an empty method

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

@@ -69,6 +69,11 @@ export abstract class MusicSheetDrawer {
         this.drawSplitScreenLine();
         if (this.drawingParameters.drawCursors) {
             for (const line of graphicalMusicSheet.Cursors) {
+                if (!line) {
+                    // TODO GraphicalMusicSheet.calculateCursorLineAtTimestamp() can return undefined.
+                    // why does this happen in the VexFlowMusicSheetDrawer_Test? (it("draws cursor..."))
+                    continue;
+                }
                 const psi: BoundingBox = new BoundingBox(line);
                 psi.AbsolutePosition = line.Start;
                 psi.BorderBottom = line.End.y - line.Start.y;

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

@@ -28,6 +28,7 @@ export class SvgVexFlowBackend extends VexFlowBackend {
 
     public initialize(container: HTMLElement): void {
         this.canvas = document.createElement("div");
+        // this.canvas.id = uniqueID // TODO create unique tagName like with cursor now?
         this.inner = this.canvas;
         this.inner.style.position = "relative";
         this.canvas.style.zIndex = "0";

+ 16 - 9
src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts

@@ -8,9 +8,10 @@ import {BackendType} from "../../../OpenSheetMusicDisplay";
 
 export class VexFlowBackends {
   public static CANVAS: 0;
-  public static RAPHAEL: 1;
+  public static RAPHAEL: 1; // this is currently unused in OSMD, and outdated in Vexflow.
+  // maybe SVG should be 1? but this could be a breaking change if people use numbers (2) instead of names (.SVG).
   public static SVG: 2;
-  public static VML: 3;
+  public static VML: 3; // this is currently unused in OSMD, and outdated in Vexflow
 
 }
 
@@ -30,6 +31,18 @@ export abstract class VexFlowBackend {
     return this.canvas;
   }
 
+  public getRenderElement(): HTMLElement {
+    //console.log("backend type: " + this.getVexflowBackendType());
+    let renderingHtmlElement: HTMLElement = this.canvas; // for SVGBackend
+    if (this.getVexflowBackendType() === Vex.Flow.Renderer.Backends.CANVAS) {
+      renderingHtmlElement = this.inner;
+      // usage in removeFromContainer:
+      // for SVG, this.canvas === this.inner, but for Canvas, removing this.canvas causes an error because it's not a child of container,
+      // so we have to remove this.inner instead.
+    }
+    return renderingHtmlElement;
+  }
+
   public getRenderer(): Vex.Flow.Renderer {
     return this.renderer;
   }
@@ -42,13 +55,7 @@ export abstract class VexFlowBackend {
 
   // note: removing single children to remove all is error-prone, because sometimes a random SVG-child remains.
   public removeFromContainer(container: HTMLElement): void {
-    //console.log("backend type: " + this.getVexflowBackendType());
-    let htmlElementToRemove: HTMLElement = this.canvas; // for SVGBackend
-    if (this.getVexflowBackendType() === Vex.Flow.Renderer.Backends.CANVAS) {
-      htmlElementToRemove = this.inner;
-      // for SVG, this.canvas === this.inner, but for Canvas, removing this.canvas causes an error because it's not a child of container,
-      // so we have to remove this.inner instead.
-    }
+    const htmlElementToRemove: HTMLElement = this.getRenderElement();
 
     // only remove child if the container has this child, otherwise it will throw an error.
     for (let i: number = 0; i < container.children.length; i++) {

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

@@ -27,13 +27,14 @@ import {VexFlowVoiceEntry} from "./VexFlowVoiceEntry";
 import {Fraction} from "../../../Common/DataObjects/Fraction";
 import {Voice} from "../../VoiceData/Voice";
 import {LinkedVoice} from "../../VoiceData/LinkedVoice";
-import { EngravingRules } from "../EngravingRules";
+import {EngravingRules} from "../EngravingRules";
 import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
 import {TechnicalInstruction, TechnicalInstructionType} from "../../VoiceData/Instructions/TechnicalInstruction";
 import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
 import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
 import {AutoBeamOptions} from "../../../OpenSheetMusicDisplay/OSMDOptions";
 import {NoteType, Arpeggio} from "../../VoiceData";
+import {SkyBottomLineCalculator} from "../SkyBottomLineCalculator";
 
 // type StemmableNote = Vex.Flow.StemmableNote;
 
@@ -335,7 +336,83 @@ export class VexFlowMeasure extends GraphicalMeasure {
                 default:
                     break;
             }
-            this.stave.setVoltaType(voltaType, repetitionInstruction.endingIndices[0], 0);
+
+            const skyBottomLineCalculator: SkyBottomLineCalculator = this.ParentStaffLine.SkyBottomLineCalculator;
+            //Because of loss of accuracy when sampling (see SkyBottomLineCalculator.updateInRange), measures tend to overlap
+            //This causes getSkyLineMinInRange to return an incorrect min value (one from the previous measure, which has been modified)
+            //We need to offset the end of what we are measuring by a bit to prevent this, otherwise volta pairs step up
+            const start: number = this.PositionAndShape.AbsolutePosition.x + this.PositionAndShape.BorderMarginLeft + 0.4;
+            const end: number = this.PositionAndShape.AbsolutePosition.x + this.PositionAndShape.BorderMarginRight;
+            //2 unit gap, since volta is positioned from y center it seems.
+            //This prevents cases where the volta is rendered over another element
+            const skylineMinForMeasure: number = skyBottomLineCalculator.getSkyLineMinInRange( start, end ) - 2;
+            //-6 OSMD units is the 0 value that the volta is placed on. .1 extra so the skyline goes above the volta
+            //instead of on the line itself
+            let newSkylineValueForMeasure: number = -6.1 + this.rules.VoltaOffset;
+            let vexFlowVoltaHeight: number = this.rules.VoltaOffset;
+            //EngravingRules default offset is 2.5, can be user set.
+            //2.5 gives us a good default value to work with.
+
+            //if we calculate that the minimum skyline allowed by elements is above the default volta position, need to adjust volta up further
+            if (skylineMinForMeasure < newSkylineValueForMeasure) {
+                const skylineDifference: number = skylineMinForMeasure - newSkylineValueForMeasure;
+                vexFlowVoltaHeight += skylineDifference;
+                newSkylineValueForMeasure = skylineMinForMeasure;
+            }
+
+            /*
+                Code here that is commented out is for finding the previous measure volta height
+                and matching it or resetting it to ours.
+                Still needs work. When we get to higher measure numbers, prev measures don't seem to be available?
+
+            //if we already have a volta in the prev measure, should match it's height, or if we are higher, it should match ours
+            //find previous sibling measure that may have volta
+            let measureIndex = this.parentSourceMeasure.measureListIndex;
+            //this.parentSourceMeasure.getPreviousMeasure();
+            if(measureIndex > 0){
+                let prevMeasureIndex = measureIndex-1;
+                let prevMeasure : VexFlowMeasure = undefined;
+                // find first visible StaffLine
+                //Need to find the uppermost, where the volta would go
+                const measures: VexFlowMeasure[] = <VexFlowMeasure[]>this.ParentMusicSystem.GraphicalMeasures[prevMeasureIndex];
+                if(measures !== undefined && measures.length > 0){
+                    for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
+                        const graphicalMeasure: VexFlowMeasure = measures[idx];
+                        if (graphicalMeasure.ParentStaffLine !== undefined && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
+                            prevMeasure = <VexFlowMeasure>graphicalMeasure;
+                        break;
+                        }
+                    }
+                }
+
+                if(prevMeasure !== undefined){
+                    let prevStaveModifiers = prevMeasure.stave.getModifiers();
+                    for(let i = 0; i < prevStaveModifiers.length; i++){
+                        let nextStaveModifier = prevStaveModifiers[i];
+                        if(nextStaveModifier.hasOwnProperty("volta")){
+                            const prevskyBottomLineCalculator: SkyBottomLineCalculator = prevMeasure.ParentStaffLine.SkyBottomLineCalculator;
+                            const prevStart: number = prevMeasure.PositionAndShape.AbsolutePosition.x + prevMeasure.PositionAndShape.BorderMarginLeft + 0.4;
+                            const prevEnd: number = prevMeasure.PositionAndShape.AbsolutePosition.x + prevMeasure.PositionAndShape.BorderMarginRight;
+                            let prevMeasureSkyline: number = prevskyBottomLineCalculator.getSkyLineMinInRange(prevStart, prevEnd);
+                            //if prev skyline is higher, use it
+                            if(prevMeasureSkyline <= newSkylineValueForMeasure){
+                                let skylineDifference = prevMeasureSkyline - newSkylineValueForMeasure;
+                                vexFlowVoltaHeight += skylineDifference;
+                                newSkylineValueForMeasure = prevMeasureSkyline;
+                            } else { //otherwise, we are higher. Need to adjust prev
+                                (nextStaveModifier as any).y_shift = vexFlowVoltaHeight*10;
+                                prevMeasure.ParentStaffLine.SkyBottomLineCalculator.updateSkyLineInRange(prevStart, prevEnd, newSkylineValueForMeasure);
+                            }
+                        }
+                    }
+                }
+            }*/
+
+            //convert to VF units (pixels)
+            vexFlowVoltaHeight *= 10;
+            this.stave.setVoltaType(voltaType, repetitionInstruction.endingIndices[0], vexFlowVoltaHeight);
+            skyBottomLineCalculator.updateSkyLineInRange(start, end, newSkylineValueForMeasure);
+           //this.ParentStaffLine.SkyBottomLineCalculator.calculateLines();
         }
     }
 

+ 7 - 4
src/MusicalScore/MusicParts/MusicPartManagerIterator.ts

@@ -28,7 +28,7 @@ export class MusicPartManagerIterator {
             }
             this.activeDynamicExpressions = new Array(manager.MusicSheet.getCompleteNumberOfStaves());
             this.currentMeasure = this.manager.MusicSheet.SourceMeasures[0];
-            if (startTimestamp === undefined) { return; }
+            if (!startTimestamp) { return; }
             do {
                 this.moveToNext();
             } while ((this.currentVoiceEntries === undefined || this.currentTimeStamp.lt(startTimestamp)) && !this.endReached);
@@ -70,7 +70,7 @@ export class MusicPartManagerIterator {
     private currentRepetition: Repetition = undefined;
     private endReached: boolean = false;
     private frontReached: boolean = false;
-    private currentTimeStamp: Fraction = new Fraction(0, 1);
+    public currentTimeStamp: Fraction = new Fraction(0, 1);
     private currentEnrolledMeasureTimestamp: Fraction = new Fraction(0, 1);
     private currentVerticalContainerInMeasureTimestamp: Fraction = new Fraction(0, 1);
     private jumpResponsibleRepetition: Repetition = undefined;
@@ -132,14 +132,17 @@ export class MusicPartManagerIterator {
     /**
      * Creates a clone of this iterator which has the same actual position.
      */
-    public clone(): MusicPartManagerIterator {
-        const ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.manager);
+    public clone(startTimeStamp: Fraction = undefined): MusicPartManagerIterator {
+        // TODO this hopefully sets the cloned iterator to the given startTimeStamp. needs testing
+        const ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.manager, startTimeStamp);
         ret.currentVoiceEntryIndex = this.currentVoiceEntryIndex;
         ret.currentMappingPart = this.currentMappingPart;
         ret.currentPartIndex = this.currentPartIndex;
         ret.currentVoiceEntries = this.currentVoiceEntries;
         ret.endReached = this.endReached;
         ret.frontReached = this.frontReached;
+        // alternative method to set currentTimeStamp? may not fully affect current iterator position
+        // ret.currentTimeStamp = this.currentTimeStamp;
         return ret;
     }
 

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

@@ -690,7 +690,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             if (subtitleNodeChild !== undefined) {
                 const workNumber: string = subtitleNodeChild.value;
                 if (workNumber) {
-                    if (finalSubTitle) {
+                    if (finalSubTitle === "") {
                         finalSubTitle = workNumber;
                     } else {
                         finalSubTitle = finalSubTitle + ", " + workNumber;

+ 5 - 2
src/MusicalScore/VoiceData/NoteType.ts

@@ -8,6 +8,7 @@ export enum NoteType {
     _256th,
     _128th,
     _64th,
+    _32nd,
     _16th,
     EIGTH,
     QUARTER,
@@ -20,13 +21,15 @@ export enum NoteType {
 
 export class NoteTypeHandler {
     // tslint:disable-next-line: variable-name
-    public static NoteTypeXmlValues: string[] = ["", "1024th", "512th", "256th", "128th", "64th", "32th", "16th",
+    public static NoteTypeXmlValues: string[] = ["", "1024th", "512th", "256th", "128th", "64th", "32nd", "16th",
         "eigth", "quarter", "half", "whole", "breve", "long", "maxima"];
     // alternative to array: use switch/case
 
 
     public static NoteTypeToString(noteType: NoteType): string {
-        return this.NoteTypeXmlValues[noteType]; // assumes that the enum values are ordered from 0 to x, like NoteTypeXmlValues array members
+        return this.NoteTypeXmlValues[noteType];
+        // assumes that the enum values are ordered from 0 to x, like NoteTypeXmlValues array members
+        // see NoteType_Test.ts
     }
 
     public static StringToNoteType(noteType: string): NoteType {

+ 18 - 1
src/OpenSheetMusicDisplay/Cursor.ts

@@ -18,7 +18,19 @@ export class Cursor {
     this.container = container;
     this.openSheetMusicDisplay = openSheetMusicDisplay;
     this.rules = this.openSheetMusicDisplay.EngravingRules;
+
+    // set cursor id
+    // TODO add this for the OSMD object as well and refactor this into a util method?
+    let id: number = 0;
+    this.cursorElementId = "cursorImg-0";
+    // find unique cursor id in document
+    while (document.getElementById(this.cursorElementId)) {
+      id++;
+      this.cursorElementId = `cursorImg-${id}`;
+    }
+
     const curs: HTMLElement = document.createElement("img");
+    curs.id = this.cursorElementId;
     curs.style.position = "absolute";
     curs.style.zIndex = "-1";
     this.cursorElement = <HTMLImageElement>curs;
@@ -26,13 +38,18 @@ export class Cursor {
   }
 
   private container: HTMLElement;
+  public cursorElement: HTMLImageElement;
+  /** a unique id of the cursor's HTMLElement in the document.
+   * Should be constant between re-renders and backend changes,
+   * but different between different OSMD objects on the same page.
+   */
+  public cursorElementId: string;
   private openSheetMusicDisplay: OpenSheetMusicDisplay;
   private rules: EngravingRules;
   private manager: MusicPartManager;
   public iterator: MusicPartManagerIterator;
   private graphic: GraphicalMusicSheet;
   public hidden: boolean = true;
-  private cursorElement: HTMLImageElement;
 
   /** Initialize the cursor. Necessary before using functions like show() and next(). */
   public init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void {

+ 1 - 1
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -54,7 +54,7 @@ export interface IOSMDOptions {
     disableCursor?: boolean;
     /** Follow Cursor: Scroll the page when cursor.next() is called and the cursor moves into a new system outside of the current view frame. */
     followCursor?: boolean;
-    /** Broad Parameters like compact or preview mode. */
+    /** Broad Parameters like compact or preview mode. Also try "compacttight", which is like compact but also reduces margins. */
     drawingParameters?: string | DrawingParametersEnum;
     /** Whether to draw credits (title, subtitle, composer, lyricist) (in future: copyright etc., see <credit>). */
     drawCredits?: boolean;

+ 10 - 5
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -503,7 +503,7 @@ export class OpenSheetMusicDisplay {
             // we could remove the window EventListener here, but not necessary.
         }
         if (options.pageFormat !== undefined) { // only change this option if it was given, see above
-            this.rules.PageFormat = OpenSheetMusicDisplay.StringToPageFormat(options.pageFormat);
+            this.setPageFormat(options.pageFormat);
         }
         if (options.pageBackgroundColor !== undefined) {
             this.rules.PageBackgroundColor = options.pageBackgroundColor;
@@ -619,7 +619,7 @@ export class OpenSheetMusicDisplay {
                 //);
                 //self.container.style.width = width + "px";
 
-                // recalculate beems, are otherwise not updated and can detach from beams, see #724
+                // recalculate beams, are otherwise not updated and can detach from stems, see #724
                 if (this.graphic?.GetCalculator instanceof VexFlowMusicSheetCalculator) { // null and type check
                     (this.graphic.GetCalculator as VexFlowMusicSheetCalculator).beamsNeedUpdate = true;
                 }
@@ -686,8 +686,10 @@ export class OpenSheetMusicDisplay {
             const previousIterator: MusicPartManagerIterator = this.cursor?.Iterator;
 
             // create new cursor
-            this.cursor = new Cursor(this.drawer.Backends[0].getInnerElement(), this);
-            if (this.sheet && this.graphic) { // else init is called in load()
+            if (this.drawer?.Backends?.length >= 1 && this.drawer.Backends[0].getRenderElement()) {
+                this.cursor = new Cursor(this.drawer.Backends[0].getRenderElement(), this);
+            }
+            if (this.sheet && this.graphic && this.cursor) { // else init is called in load()
                 this.cursor.init(this.sheet.MusicPartManager, this.graphic);
             }
 
@@ -761,7 +763,7 @@ export class OpenSheetMusicDisplay {
         return pageFormat;
     }
 
-    /** Sets page format by string. Alternative to setOptions({pageFormat: PageFormatStandards.Endless}) for example. */
+    /** Sets page format by string. Used by setOptions({pageFormat: "A4_P"}) for example. */
     public setPageFormat(formatId: string): void {
         const newPageFormat: PageFormat = OpenSheetMusicDisplay.StringToPageFormat(formatId);
         this.needBackendUpdate = !(newPageFormat.Equals(this.rules.PageFormat));
@@ -823,6 +825,9 @@ export class OpenSheetMusicDisplay {
 
         // simply save the created pdf
         pdf.save(pdfName);
+
+        // note that using jspdf with svg2pdf creates unnecessary console warnings "AcroForm-Classes are not populated into global-namespace..."
+        // this will hopefully be fixed with a new jspdf release, see https://github.com/yWorks/jsPDF/pull/32
     }
 
     //#region GETTER / SETTER

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

@@ -359,8 +359,12 @@ describe("OpenSheetMusicDisplay Main Export", () => {
                     voiceEntry,
                     voiceEntry.ParentSourceStaffEntry,
                     new Fraction(1),
-                    new Pitch(11, 1, AccidentalEnum.NATURAL));
+                    new Pitch(11, 2, AccidentalEnum.NATURAL));
+                    // note: if the pitch is such that the voice entry frequencies aren't ordered correctly,
+                    // Vexflow will complain about unsorted pitches. see below
                 voiceEntry.Notes.push(newNote);
+                // we could do something like voiceEntry.sort() here to prevent the Vexflow warning about unsorted pitches,
+                // but for now sort() only exists on GraphicalVoiceEntry.
 
                 opensheetmusicdisplay.updateGraphic();
 

+ 11 - 8
test/MusicalScore/Graphical/VexFlow/VexFlowMeasure_Test.ts

@@ -8,13 +8,13 @@ import {SourceMeasure} from "../../../../src/MusicalScore/VoiceData/SourceMeasur
 import {SourceStaffEntry} from "../../../../src/MusicalScore/VoiceData/SourceStaffEntry";
 import {GraphicalMeasure} from "../../../../src/MusicalScore/Graphical/GraphicalMeasure";
 import {MusicSheetCalculator} from "../../../../src/MusicalScore/Graphical/MusicSheetCalculator";
-import { EngravingRules } from "../../../../src/MusicalScore/Graphical/EngravingRules";
+import {EngravingRules} from "../../../../src/MusicalScore/Graphical/EngravingRules";
 
 /* tslint:disable:no-unused-expression */
 describe("VexFlow Measure", () => {
 
-   it.skip("GraphicalMusicSheet", (done: MochaDone) => {
-      const path: string = "test/data/MuzioClementi_SonatinaOpus36No1_Part1.xml";
+   it("GraphicalMusicSheet", (done: MochaDone) => {
+      const path: string = "MuzioClementi_SonatinaOpus36No1_Part1.xml";
       const score: Document = TestUtils.getScore(path);
       chai.expect(score).to.not.be.undefined;
       const partwise: Element = TestUtils.getPartWiseElement(score);
@@ -23,11 +23,12 @@ describe("VexFlow Measure", () => {
       const calc: VexFlowMusicSheetCalculator = new VexFlowMusicSheetCalculator(reader.rules);
       const sheet: MusicSheet = reader.createMusicSheet(new IXmlElement(partwise), path);
       const gms: GraphicalMusicSheet = new GraphicalMusicSheet(sheet, calc);
-      console.log(gms);
+      // console.log(gms);
+      chai.expect(gms).to.not.be.undefined; // at least necessary for linter so that variable is not unused
       done();
    });
 
-   it.skip("Simple Measure", (done: MochaDone) => {
+   it("Simple Measure", (done: MochaDone) => {
       const sheet: MusicSheet = new MusicSheet();
       sheet.Rules = new EngravingRules();
       const measure: SourceMeasure = new SourceMeasure(1, sheet.Rules);
@@ -37,11 +38,12 @@ describe("VexFlow Measure", () => {
       chai.expect(gms.MeasureList.length).to.equal(1);
       chai.expect(gms.MeasureList[0].length).to.equal(1);
       const gm: GraphicalMeasure = gms.MeasureList[0][0];
-      console.log(gm);
+      // console.log(gm);
+      chai.expect(gm).to.not.be.undefined; // at least necessary for linter so that variable is not unused
       done();
    });
 
-   it.skip("Empty Measure", (done: MochaDone) => {
+   it("Empty Measure", (done: MochaDone) => {
       const sheet: MusicSheet = new MusicSheet();
       sheet.Rules = new EngravingRules();
       const measure: SourceMeasure = new SourceMeasure(1, sheet.Rules);
@@ -50,7 +52,8 @@ describe("VexFlow Measure", () => {
       const calc: MusicSheetCalculator = new VexFlowMusicSheetCalculator(sheet.Rules);
       const gms: GraphicalMusicSheet = new GraphicalMusicSheet(sheet, calc);
       chai.expect(gms.MeasureList.length).to.equal(1);
-      chai.expect(gms.MeasureList[0].length).to.equal(0);
+      chai.expect(gms.MeasureList[0].length).to.equal(1);
+      chai.expect(gms.MeasureList[0][0].staffEntries.length).to.equal(0);
       done();
    });
 

+ 21 - 22
test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer_Test.ts

@@ -1,15 +1,12 @@
 import {VexFlowMusicSheetDrawer} from "../../../../src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer";
 import {GraphicalMusicSheet} from "../../../../src/MusicalScore/Graphical/GraphicalMusicSheet";
-import {OutlineAndFillStyleEnum} from "../../../../src/MusicalScore/Graphical/DrawingEnums";
 import {MusicSheet} from "../../../../src/MusicalScore/MusicSheet";
 import {MusicSheetReader} from "../../../../src/MusicalScore/ScoreIO/MusicSheetReader";
 import {VexFlowMusicSheetCalculator} from "../../../../src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator";
 import {TestUtils} from "../../../Util/TestUtils";
 import {IXmlElement} from "../../../../src/Common/FileIO/Xml";
-import {Fraction} from "../../../../src/Common/DataObjects/Fraction";
 import {VexFlowBackend} from "../../../../src/MusicalScore/Graphical/VexFlow/VexFlowBackend";
 import {CanvasVexFlowBackend} from "../../../../src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend";
-import {DrawingParameters} from "../../../../src/MusicalScore/Graphical/DrawingParameters";
 
 /* tslint:disable:no-unused-expression */
 describe("VexFlow Music Sheet Drawer", () => {
@@ -34,24 +31,26 @@ describe("VexFlow Music Sheet Drawer", () => {
         done();
     });
 
-    it.skip("draws cursor (as rectangle)", (done: MochaDone) => {
-        const score: Document = TestUtils.getScore("MuzioClementi_SonatinaOpus36No1_Part1.xml");
-        chai.expect(score).to.not.be.undefined;
-        const partwise: Element = TestUtils.getPartWiseElement(score);
-        chai.expect(partwise).to.not.be.undefined;
-        const reader: MusicSheetReader = new MusicSheetReader();
-        const calc: VexFlowMusicSheetCalculator = new VexFlowMusicSheetCalculator(reader.rules);
-        const sheet: MusicSheet = reader.createMusicSheet(new IXmlElement(partwise), "** missing path **");
-        const gms: GraphicalMusicSheet = new GraphicalMusicSheet(sheet, calc);
-        gms.Cursors.push(gms.calculateCursorLineAtTimestamp(new Fraction(0, 4), OutlineAndFillStyleEnum.PlaybackCursor));
+    // Test ignored for now, gms.calculateCursorLineAtTimestamp returns null instead of a GraphicalLine,
+    // and in any case, this test doesn't test that the cursor is actually drawn, there are no expects for that etc.
+    // it.only("draws cursor (as rectangle)", (done: MochaDone) => {
+    //     const score: Document = TestUtils.getScore("MuzioClementi_SonatinaOpus36No1_Part1.xml");
+    //     chai.expect(score).to.not.be.undefined;
+    //     const partwise: Element = TestUtils.getPartWiseElement(score);
+    //     chai.expect(partwise).to.not.be.undefined;
+    //     const reader: MusicSheetReader = new MusicSheetReader();
+    //     const calc: VexFlowMusicSheetCalculator = new VexFlowMusicSheetCalculator(reader.rules);
+    //     const sheet: MusicSheet = reader.createMusicSheet(new IXmlElement(partwise), "** missing path **");
+    //     const gms: GraphicalMusicSheet = new GraphicalMusicSheet(sheet, calc);
+    //     gms.Cursors.push(gms.calculateCursorLineAtTimestamp(new Fraction(0, 4), OutlineAndFillStyleEnum.PlaybackCursor));
 
-        // Create the canvas in the document:
-        const canvas: HTMLCanvasElement = document.createElement("canvas");
-        const backend: VexFlowBackend = new CanvasVexFlowBackend(sheet.Rules);
-        backend.initialize(canvas);
-        const drawer: VexFlowMusicSheetDrawer = new VexFlowMusicSheetDrawer(new DrawingParameters());
-        drawer.Backends.push(backend);
-        drawer.drawSheet(gms);
-        done();
-    });
+    //     // Create the canvas in the document:
+    //     const canvas: HTMLCanvasElement = document.createElement("canvas");
+    //     const backend: VexFlowBackend = new CanvasVexFlowBackend(sheet.Rules);
+    //     backend.initialize(canvas);
+    //     const drawer: VexFlowMusicSheetDrawer = new VexFlowMusicSheetDrawer(new DrawingParameters());
+    //     drawer.Backends.push(backend);
+    //     drawer.drawSheet(gms);
+    //     done();
+    // });
 });

+ 17 - 17
test/MusicalScore/ScoreIO/Key_Test.ts

@@ -21,9 +21,9 @@ describe("MusicXML parser for element 'key'", () => {
 
   describe("for group traditional keys", () => {
 
-    xit("enforces single occurrence of element 'fifths'", (done: MochaDone) => {
+    it("enforces single occurrence of element 'fifths'", (done: MochaDone) => {
       const keyInstruction: KeyInstruction = getIllegalMusicXmlWithTwoFifthsElements().getFirstSourceMeasure().getKeyInstruction(0);
-      // TODO Make sure we detect the multiple fifths and react properly
+      // TODO Make sure we detect the multiple fifths and react properly // [it seems like we do this, test passes. ssch]
       chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.none);
       done();
     });
@@ -79,21 +79,21 @@ describe("MusicXML parser for element 'key'", () => {
         done();
       });
 
-      it("reads key signature Fis-major", (done: MochaDone) => {
+      it("reads key signature F#-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(6, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(6);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
         done();
       });
 
-      it("reads key signature Cis-major", (done: MochaDone) => {
+      it("reads key signature C#-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(7, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(7);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
         done();
       });
 
-      it("reads key signature Gis-major", (done: MochaDone) => {
+      it("reads key signature G#-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(8, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(8);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
@@ -114,35 +114,35 @@ describe("MusicXML parser for element 'key'", () => {
         done();
       });
 
-      it("reads key signature Es-major", (done: MochaDone) => {
+      it("reads key signature Eb-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(-3, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(-3);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
         done();
       });
 
-      it("reads key signature As-major", (done: MochaDone) => {
+      it("reads key signature Ab-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(-4, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(-4);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
         done();
       });
 
-      it("reads key signature Des-major", (done: MochaDone) => {
+      it("reads key signature Db-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(-5, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(-5);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
         done();
       });
 
-      it("reads key signature Ges-major", (done: MochaDone) => {
+      it("reads key signature Gb-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(-6, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(-6);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
         done();
       });
 
-      it("reads key signature Fes-major", (done: MochaDone) => {
+      it("reads key signature Fb-major", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(-8, "major").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(-8);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.major);
@@ -173,35 +173,35 @@ describe("MusicXML parser for element 'key'", () => {
         done();
       });
 
-      it("reads key signature fis-minor", (done: MochaDone) => {
+      it("reads key signature f#-minor", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(3, "minor").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(3);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.minor);
         done();
       });
 
-      it("reads key signature cis-minor", (done: MochaDone) => {
+      it("reads key signature c#-minor", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(4, "minor").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(4);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.minor);
         done();
       });
 
-      it("reads key signature gis-minor", (done: MochaDone) => {
+      it("reads key signature g#-minor", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(5, "minor").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(5);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.minor);
         done();
       });
 
-      it("reads key signature dis-minor", (done: MochaDone) => {
+      it("reads key signature d#-minor", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(6, "minor").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(6);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.minor);
         done();
       });
 
-      it("reads key signature ais-minor", (done: MochaDone) => {
+      it("reads key signature a#-minor", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(7, "minor").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(7);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.minor);
@@ -243,14 +243,14 @@ describe("MusicXML parser for element 'key'", () => {
         done();
       });
 
-      it("reads key signature es-minor", (done: MochaDone) => {
+      it("reads key signature eb-minor", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(-6, "minor").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(-6);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.minor);
         done();
       });
 
-      it("reads key signature as-minor", (done: MochaDone) => {
+      it("reads key signature ab-minor", (done: MochaDone) => {
         const keyInstruction: KeyInstruction = getMusicSheetWithKey(-7, "minor").getFirstSourceMeasure().getKeyInstruction(0);
         chai.expect(keyInstruction.Key).to.equal(-7);
         chai.expect(keyInstruction.Mode).to.equal(KeyModeEnum.minor);

+ 19 - 0
test/MusicalScore/VoiceData/NoteType_Test.ts

@@ -0,0 +1,19 @@
+import { NoteType, NoteTypeHandler } from "../../../src/MusicalScore/VoiceData";
+
+/* tslint:disable:no-unused-expression */
+describe("NoteType", () => {
+    it("parses 32nd note correctly (sample value)", (done: MochaDone) => {
+        chai.expect(NoteTypeHandler.StringToNoteType("32nd")).to.equal(NoteType._32nd);
+        done();
+    });
+
+    it("parses all NoteType values from string correctly (in XML: <note><type>)", (done: MochaDone) => {
+        const inputValues: string[] = NoteTypeHandler.NoteTypeXmlValues;
+        for (let i: number = 0; i < inputValues.length; i++) {
+            chai.expect(NoteTypeHandler.StringToNoteType(inputValues[i])).to.equal(i);
+            // assumption: i corresponds to NoteType int value: 0 = UNDEFINED, 1 = _1024th, etc.
+            // we could also iterate over the NoteType enum values, but that's a bit ugly in typescript.
+        }
+        done();
+    });
+});