Browse Source

Merge branch 'improvement/ghostnotes' into develop

Matthias Uiberacker 7 years ago
parent
commit
8227ae60e0

+ 11 - 2
external/vexflow/vexflow.d.ts

@@ -40,6 +40,8 @@ declare namespace Vex {
             public setStave(stave: Stave);
             public setStave(stave: Stave);
 
 
             public getBoundingBox(): BoundingBox;
             public getBoundingBox(): BoundingBox;
+
+            public getAttribute(arg: string): string;
         }
         }
 
 
         export class Voice {
         export class Voice {
@@ -55,9 +57,9 @@ declare namespace Vex {
 
 
             public setStave(stave: Stave): Voice;
             public setStave(stave: Stave): Voice;
 
 
-            public addTickables(notes: StaveNote[]): Voice;
+            public addTickables(tickables: Tickable[]): Voice;
 
 
-            public addTickable(note: StaveNote): Voice;
+            public addTickable(tickable: Tickable): Voice;
 
 
             public setMode(mode: any): Voice;
             public setMode(mode: any): Voice;
 
 
@@ -74,6 +76,13 @@ declare namespace Vex {
         export class StemmableNote extends Note {
         export class StemmableNote extends Note {
             public getStemDirection(): number;
             public getStemDirection(): number;
             public setStemDirection(direction: number): StemmableNote;
             public setStemDirection(direction: number): StemmableNote;
+            public x_shift: number;
+            public getAbsoluteX(): number;
+        }
+
+        export class GhostNote extends StemmableNote {
+            constructor(note_struct: any);
+            public setStave(stave): void;
         }
         }
 
 
         export class StaveNote extends StemmableNote {
         export class StaveNote extends StemmableNote {

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

@@ -12,7 +12,7 @@ export class GraphicalVoiceEntry extends GraphicalObject {
         super();
         super();
         this.parentVoiceEntry = parentVoiceEntry;
         this.parentVoiceEntry = parentVoiceEntry;
         this.parentStaffEntry = parentStaffEntry;
         this.parentStaffEntry = parentStaffEntry;
-        this.PositionAndShape = new BoundingBox(this, parentStaffEntry.PositionAndShape);
+        this.PositionAndShape = new BoundingBox(this, parentStaffEntry ? parentStaffEntry.PositionAndShape : undefined);
         this.notes = [];
         this.notes = [];
     }
     }
 
 

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

@@ -55,6 +55,7 @@ import {GraphicalLyricWord} from "./GraphicalLyricWord";
 import {GraphicalLine} from "./GraphicalLine";
 import {GraphicalLine} from "./GraphicalLine";
 import {Label} from "../Label";
 import {Label} from "../Label";
 import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry";
 import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry";
+import { VerticalSourceStaffEntryContainer } from "../VoiceData/VerticalSourceStaffEntryContainer";
 
 
 /**
 /**
  * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
  * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
@@ -1134,7 +1135,7 @@ export abstract class MusicSheetCalculator {
                         const measure: StaffMeasure = staffLine.Measures[idx4];
                         const measure: StaffMeasure = staffLine.Measures[idx4];
                         if (measure.staffEntries.length === 1) {
                         if (measure.staffEntries.length === 1) {
                             const gse: GraphicalStaffEntry = measure.staffEntries[0];
                             const gse: GraphicalStaffEntry = measure.staffEntries[0];
-                            if (gse.graphicalVoiceEntries.length > 0 && gse.graphicalVoiceEntries[0].notes.length > 0) {
+                            if (gse.graphicalVoiceEntries.length > 0 && gse.graphicalVoiceEntries[0].notes.length === 1) {
                                 const graphicalNote: GraphicalNote = gse.graphicalVoiceEntries[0].notes[0];
                                 const graphicalNote: GraphicalNote = gse.graphicalVoiceEntries[0].notes[0];
                                 if (graphicalNote.sourceNote.Pitch === undefined && (new Fraction(1, 2)).lt(graphicalNote.sourceNote.Length)) {
                                 if (graphicalNote.sourceNote.Pitch === undefined && (new Fraction(1, 2)).lt(graphicalNote.sourceNote.Length)) {
                                     this.layoutMeasureWithWholeRest(graphicalNote, gse, measure);
                                     this.layoutMeasureWithWholeRest(graphicalNote, gse, measure);
@@ -1542,7 +1543,11 @@ export abstract class MusicSheetCalculator {
         }
         }
         // if there are no staffEntries in this measure, create a rest for the whole measure:
         // if there are no staffEntries in this measure, create a rest for the whole measure:
         if (measure.staffEntries.length === 0) {
         if (measure.staffEntries.length === 0) {
-            const sourceStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, staff);
+            const sourceStaffEntry: SourceStaffEntry = new SourceStaffEntry(
+                new VerticalSourceStaffEntryContainer(  measure.parentSourceMeasure,
+                                                        measure.parentSourceMeasure.AbsoluteTimestamp,
+                                                        measure.parentSourceMeasure.CompleteNumberOfStaves),
+                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);
             voiceEntry.Notes.push(note);
             voiceEntry.Notes.push(note);

+ 39 - 16
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -18,6 +18,7 @@ import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEn
 import {Logging} from "../../../Common/Logging";
 import {Logging} from "../../../Common/Logging";
 import { ArticulationEnum, StemDirectionType } from "../../VoiceData/VoiceEntry";
 import { ArticulationEnum, StemDirectionType } from "../../VoiceData/VoiceEntry";
 import { SystemLinePosition } from "../SystemLinePosition";
 import { SystemLinePosition } from "../SystemLinePosition";
+import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 
 
 /**
 /**
  * Helper class, which contains static methods which actually convert
  * Helper class, which contains static methods which actually convert
@@ -137,36 +138,58 @@ export class VexFlowConverter {
         return acc;
         return acc;
     }
     }
 
 
+    public static GhostNote(frac: Fraction): Vex.Flow.GhostNote {
+        // const frac: Fraction = notes[0].graphicalNoteLength;
+        return new Vex.Flow.GhostNote({
+            duration: VexFlowConverter.duration(frac, false),
+        });
+    }
+
     /**
     /**
-     * Convert a set of GraphicalNotes to a VexFlow StaveNote
-     * @param notes form a chord on the staff
+     * Convert a GraphicalVoiceEntry to a VexFlow StaveNote
+     * @param gve the GraphicalVoiceEntry which can hold a note or a chord on the staff belonging to one voice
      * @returns {Vex.Flow.StaveNote}
      * @returns {Vex.Flow.StaveNote}
      */
      */
-    public static StaveNote(notes: GraphicalNote[]): Vex.Flow.StaveNote {
+    public static StaveNote(gve: GraphicalVoiceEntry): Vex.Flow.StaveNote {
+        // VexFlow needs the notes ordered vertically in the other direction:
+        const notes: GraphicalNote[] = gve.notes.reverse();
+        const baseNote: GraphicalNote = notes[0];
+        if (baseNote.sourceNote.Pitch === undefined &&
+            new Fraction(1, 2).lt(baseNote.sourceNote.Length)) {
+                // test
+            }
         let keys: string[] = [];
         let keys: string[] = [];
         const accidentals: string[] = [];
         const accidentals: string[] = [];
-        const frac: Fraction = notes[0].graphicalNoteLength;
-        const isTuplet: boolean = notes[0].sourceNote.NoteTuplet !== undefined;
+        const frac: Fraction = baseNote.graphicalNoteLength;
+        const isTuplet: boolean = baseNote.sourceNote.NoteTuplet !== undefined;
         let duration: string = VexFlowConverter.duration(frac, isTuplet);
         let duration: string = VexFlowConverter.duration(frac, isTuplet);
         let vfClefType: string = undefined;
         let vfClefType: string = undefined;
-        let numDots: number = 0;
+        let numDots: number = baseNote.numberOfDots;
         for (const note of notes) {
         for (const note of notes) {
-            const pitch: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
-            if (pitch === undefined) { // if it is a rest:
-              keys = ["b/4"];
-              duration += "r";
-              break;
+            if (numDots < note.numberOfDots) {
+                numDots = note.numberOfDots;
+            }
+            // if it is a rest:
+            if (note.sourceNote.isRest()) {
+                // if it is a full measure rest:
+                if (note.parentVoiceEntry.parentStaffEntry.parentMeasure.parentSourceMeasure.Duration.RealValue <= frac.RealValue) {
+                    duration = "w";
+                    numDots = 0;
+                }
+                keys = ["b/4"];
+                duration += "r";
+                break;
             }
             }
+
+            const pitch: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
             keys.push(pitch[0]);
             keys.push(pitch[0]);
             accidentals.push(pitch[1]);
             accidentals.push(pitch[1]);
             if (!vfClefType) {
             if (!vfClefType) {
                 const vfClef: {type: string, annotation: string} = VexFlowConverter.Clef(pitch[2]);
                 const vfClef: {type: string, annotation: string} = VexFlowConverter.Clef(pitch[2]);
                 vfClefType = vfClef.type;
                 vfClefType = vfClef.type;
             }
             }
-            if (numDots < note.numberOfDots) {
-                numDots = note.numberOfDots;
-            }
         }
         }
+
         for (let i: number = 0, len: number = numDots; i < len; ++i) {
         for (let i: number = 0, len: number = numDots; i < len; ++i) {
             duration += "d";
             duration += "d";
         }
         }
@@ -178,8 +201,8 @@ export class VexFlowConverter {
             keys: keys,
             keys: keys,
         });
         });
 
 
-        if (notes[0].sourceNote.ParentVoiceEntry !== undefined) {
-            const wantedStemDirection: StemDirectionType = notes[0].sourceNote.ParentVoiceEntry.StemDirection;
+        if (gve.parentVoiceEntry !== undefined) {
+            const wantedStemDirection: StemDirectionType = gve.parentVoiceEntry.StemDirection;
             switch (wantedStemDirection) {
             switch (wantedStemDirection) {
                 case(StemDirectionType.Up):
                 case(StemDirectionType.Up):
                     vfnote.setStemDirection(Vex.Flow.Stem.UP);
                     vfnote.setStemDirection(Vex.Flow.Stem.UP);

+ 203 - 20
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -17,11 +17,13 @@ import StaveNote = Vex.Flow.StaveNote;
 import {Logging} from "../../../Common/Logging";
 import {Logging} from "../../../Common/Logging";
 import {unitInPixels} from "./VexFlowMusicSheetDrawer";
 import {unitInPixels} from "./VexFlowMusicSheetDrawer";
 import {Tuplet} from "../../VoiceData/Tuplet";
 import {Tuplet} from "../../VoiceData/Tuplet";
-import { RepetitionInstructionEnum } from "../../VoiceData/Instructions/RepetitionInstruction";
-import { SystemLinePosition } from "../SystemLinePosition";
-import { StemDirectionType } from "../../VoiceData/VoiceEntry";
-import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
-import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
+import {RepetitionInstructionEnum} from "../../VoiceData/Instructions/RepetitionInstruction";
+import {SystemLinePosition} from "../SystemLinePosition";
+import {StemDirectionType} from "../../VoiceData/VoiceEntry";
+import {GraphicalVoiceEntry} from "../GraphicalVoiceEntry";
+import {VexFlowVoiceEntry} from "./VexFlowVoiceEntry";
+import {Fraction} from "../../../Common/DataObjects/Fraction";
+import { Voice } from "../../VoiceData/Voice";
 
 
 export class VexFlowMeasure extends StaffMeasure {
 export class VexFlowMeasure extends StaffMeasure {
     constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
     constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
@@ -335,6 +337,188 @@ export class VexFlowMeasure extends StaffMeasure {
     }
     }
 
 
     /**
     /**
+     * Returns all the voices that are present in this measure
+     */
+    public getVoicesWithinMeasure(): Voice[] {
+        const voices: Voice[] = [];
+        for (const gse of this.staffEntries) {
+           for (const gve of gse.graphicalVoiceEntries) {
+                if (voices.indexOf(gve.parentVoiceEntry.ParentVoice) === -1) {
+                    voices.push(gve.parentVoiceEntry.ParentVoice);
+                }
+            }
+        }
+        return voices;
+    }
+
+    /**
+     * Returns all the graphicalVoiceEntries of a given Voice.
+     * @param voice the voice for which the graphicalVoiceEntries shall be returned.
+     */
+    public getGraphicalVoiceEntriesPerVoice(voice: Voice): GraphicalVoiceEntry[] {
+        const voiceEntries: GraphicalVoiceEntry[] = [];
+        for (const gse of this.staffEntries) {
+           for (const gve of gse.graphicalVoiceEntries) {
+                if (gve.parentVoiceEntry.ParentVoice === voice) {
+                    voiceEntries.push(gve);
+                }
+            }
+        }
+        return voiceEntries;
+    }
+
+    /**
+     * Finds the gaps between the existing notes within a measure.
+     * Problem here is, that the graphicalVoiceEntry does not exist yet and
+     * that Tied notes are not present in the normal voiceEntries.
+     * To handle this, calculation with absolute timestamps is needed.
+     * And the graphical notes have to be analysed directly (and not the voiceEntries, as it actually should be -> needs refactoring)
+     */
+    private getRestFilledVexFlowStaveNotesPerVoice(voice: Voice): GraphicalVoiceEntry[] {
+        let latestVoiceTimestamp: Fraction = undefined;
+        const gvEntries: GraphicalVoiceEntry[] = this.getGraphicalVoiceEntriesPerVoice(voice);
+        for (let idx: number = 0, len: number = gvEntries.length; idx < len; ++idx) {
+            const gve: GraphicalVoiceEntry = gvEntries[idx];
+            const gNotesStartTimestamp: Fraction = gve.notes[0].sourceNote.getAbsoluteTimestamp();
+            // find the voiceEntry end timestamp:
+            let gNotesEndTimestamp: Fraction = new Fraction();
+            for (const graphicalNote of gve.notes) {
+                // console.log(graphicalNote);
+                const noteEnd: Fraction  = Fraction.plus(graphicalNote.sourceNote.getAbsoluteTimestamp(), graphicalNote.sourceNote.Length);
+                if (gNotesEndTimestamp < noteEnd) {
+                    gNotesEndTimestamp = noteEnd;
+                }
+            }
+
+            // check if this voice has just been found the first time:
+            if (latestVoiceTimestamp === undefined) {
+
+                // if this voice is new, check for a gap from measure start to the start of the current voice entry:
+                const gapFromMeasureStart: Fraction = Fraction.minus(gNotesStartTimestamp, this.parentSourceMeasure.AbsoluteTimestamp);
+                if (gapFromMeasureStart.RealValue > 0) {
+                    console.log("Ghost Found at start", this);
+                    /* tslint:disable-next-line:no-unused-variable */
+                    // FIXME: Add to graphicalVoiceEntry
+                    // ToDo: fill the gap with a rest ghost note
+                    // from this.parentSourceMeasure.AbsoluteTimestamp
+                    // with length gapFromMeasureStart:
+                    // (maybe remember in a list and add later in a second loop)
+                    const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(gapFromMeasureStart);
+                    const ghostGve: VexFlowVoiceEntry = new VexFlowVoiceEntry(undefined, undefined);
+                    ghostGve.vfStaveNote = vfghost;
+                    gvEntries.splice(0, 0, ghostGve);
+                    idx++;
+                }
+            } else {
+                // get the length of the empty space between notes:
+                const inBetweenLength: Fraction = Fraction.minus(gNotesStartTimestamp, latestVoiceTimestamp);
+
+                if (inBetweenLength.RealValue > 0) {
+                    console.log("Ghost Found in between", this);
+                    // ToDo: fill the gap with a rest ghost note
+                    // starting from latestVoiceTimestamp
+                    // with length restLength:
+                    // (maybe remember in a list and add later in a second loop)
+                    const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(inBetweenLength);
+                    const ghostGve: VexFlowVoiceEntry = new VexFlowVoiceEntry(undefined, undefined);
+                    ghostGve.vfStaveNote = vfghost;
+                    // add element before current element:
+                    gvEntries.splice(idx, 0, ghostGve);
+                    // and increase index, as we added an element:
+                    idx++;
+                }
+            }
+
+            // finally set the latest timestamp of this voice to the end timestamp of the longest note in the current voiceEntry:
+            latestVoiceTimestamp = gNotesEndTimestamp;
+        }
+
+        const measureEndTimestamp: Fraction = Fraction.plus(this.parentSourceMeasure.AbsoluteTimestamp, this.parentSourceMeasure.Duration);
+        const restLength: Fraction = Fraction.minus(measureEndTimestamp, latestVoiceTimestamp);
+        if (restLength.RealValue > 0) {
+            // fill the gap with a rest ghost note
+            // starting from lastFraction
+            // with length restLength:
+            console.log("Ghost Found at end",  this);
+            const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(restLength);
+            const ghostGve: VexFlowVoiceEntry = new VexFlowVoiceEntry(undefined, undefined);
+            ghostGve.vfStaveNote = vfghost;
+            gvEntries.push(ghostGve);
+        }
+        return gvEntries;
+
+        // // 1) find front- and in-measure-gaps:
+        // for (const staffEntry of this.staffEntries as VexFlowStaffEntry[]) {
+        //     for (const gNotesPerVoice of staffEntry.graphicalVoiceEntries.map(gve => gve.notes)) {
+        //         // get voice id:
+        //         const voiceId: number = gNotesPerVoice[0].sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
+        //         const gNotesStartTimestamp: Fraction = gNotesPerVoice[0].sourceNote.getAbsoluteTimestamp();
+
+        //         // find the voiceEntry end timestamp:
+        //         let gNotesEndTimestamp: Fraction = new Fraction();
+        //         for (const graphicalNote of gNotesPerVoice) {
+        //             // console.log(graphicalNote);
+        //             const noteEnd: Fraction  = Fraction.plus(graphicalNote.sourceNote.getAbsoluteTimestamp(), graphicalNote.sourceNote.Length);
+        //             if (gNotesEndTimestamp < noteEnd) {
+        //                 gNotesEndTimestamp = noteEnd;
+        //             }
+        //         }
+
+        //         // ToDo: maybe check needed if this throws an exception when not in dict:
+        //         const latestVoiceTimestamp: Fraction = latestVoiceTimestampDict[voiceId];
+
+        //         // check if this voice has just been found the first time:
+        //         if (latestVoiceTimestamp === undefined) {
+
+        //             // if this voice is new, check for a gap from measure start to the start of the current voice entry:
+        //             const gapFromMeasureStart: Fraction = Fraction.minus(gNotesStartTimestamp, this.parentSourceMeasure.AbsoluteTimestamp);
+        //             if (gapFromMeasureStart.RealValue > 0) {
+        //                 console.log("Ghost Found at start");
+        //                 /* tslint:disable-next-line:no-unused-variable */
+        //                 const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(gapFromMeasureStart);
+        //                 // FIXME: Add to graphicalVoiceEntry
+        //                 // ToDo: fill the gap with a rest ghost note
+        //                 // from this.parentSourceMeasure.AbsoluteTimestamp
+        //                 // with length gapFromMeasureStart:
+        //                 // (maybe remember in a list and add later in a second loop)
+
+        //             }
+        //         } else {
+        //             // get the length of the empty space between notes:
+        //             const restLength: Fraction = Fraction.minus(gNotesStartTimestamp, latestVoiceTimestamp);
+
+        //             if (restLength.RealValue > 0) {
+        //                 console.log("Ghost Found in between");
+        //                 // ToDo: fill the gap with a rest ghost note
+        //                 // starting from latestVoiceTimestamp
+        //                 // with length restLength:
+        //                 // (maybe remember in a list and add later in a second loop)
+
+        //             }
+        //         }
+
+        //         // finally set the latest timestamp of this voice to the end timestamp of the longest note in the current voiceEntry:
+        //         latestVoiceTimestampDict[voiceId] = gNotesEndTimestamp;
+        //     }
+        // }
+
+        // // 2) find gaps from last notes to end of this measure:
+        // for (const voiceId in latestVoiceTimestampDict) {
+        //     if (voiceId !== undefined) {
+        //         const lastFraction: Fraction = latestVoiceTimestampDict[voiceId];
+        //         const measureEndTimestamp: Fraction = Fraction.plus(this.parentSourceMeasure.AbsoluteTimestamp, this.parentSourceMeasure.Duration);
+        //         const restLength: Fraction  = Fraction.minus(measureEndTimestamp, lastFraction);
+        //         if (restLength.RealValue > 0) {
+        //             // fill the gap with a rest ghost note
+        //             // starting from lastFraction
+        //             // with length restLength:
+        //             console.log("Ghost Found at end",  this);
+        //         }
+        //     }
+        // }
+    }
+
+    /**
      * Add a note to a beam
      * Add a note to a beam
      * @param graphicalNote
      * @param graphicalNote
      * @param beam
      * @param beam
@@ -411,7 +595,7 @@ export class VexFlowMeasure extends StaffMeasure {
                     }
                     }
 
 
                     for (const entry of voiceEntries) {
                     for (const entry of voiceEntries) {
-                        const note: Vex.Flow.StaveNote = (<VexFlowVoiceEntry>entry).vfStaveNote;
+                        const note: Vex.Flow.StaveNote = ((<VexFlowVoiceEntry>entry).vfStaveNote as StaveNote);
                         if (note !== undefined) {
                         if (note !== undefined) {
                           notes.push(note);
                           notes.push(note);
                         }
                         }
@@ -449,7 +633,7 @@ export class VexFlowMeasure extends StaffMeasure {
                     const tupletStaveNotes: Vex.Flow.StaveNote[] = [];
                     const tupletStaveNotes: Vex.Flow.StaveNote[] = [];
                     const tupletVoiceEntries: VexFlowVoiceEntry[] = tupletBuilder[1];
                     const tupletVoiceEntries: VexFlowVoiceEntry[] = tupletBuilder[1];
                     for (const tupletVoiceEntry of tupletVoiceEntries) {
                     for (const tupletVoiceEntry of tupletVoiceEntries) {
-                      tupletStaveNotes.push((tupletVoiceEntry).vfStaveNote);
+                      tupletStaveNotes.push(((tupletVoiceEntry).vfStaveNote as StaveNote));
                     }
                     }
                     if (tupletStaveNotes.length > 1) {
                     if (tupletStaveNotes.length > 1) {
                       const notesOccupied: number = 2;
                       const notesOccupied: number = 2;
@@ -471,31 +655,30 @@ export class VexFlowMeasure extends StaffMeasure {
     }
     }
 
 
     public staffMeasureCreatedCalculations(): void {
     public staffMeasureCreatedCalculations(): void {
-        for (const graphicalStaffEntry of this.staffEntries) {
+        for (const graphicalStaffEntry of this.staffEntries as VexFlowStaffEntry[]) {
             // create vex flow Stave Notes:
             // create vex flow Stave Notes:
             for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
             for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
-                (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve.notes);
+                (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve);
             }
             }
         }
         }
 
 
         this.finalizeBeams();
         this.finalizeBeams();
         this.finalizeTuplets();
         this.finalizeTuplets();
 
 
-        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
-            const graphicalVoiceEntries: GraphicalVoiceEntry[] = graphicalStaffEntry.graphicalVoiceEntries;
-            // create vex flow voices and add tickables to it:
-            for (const gve of graphicalVoiceEntries) {
-                const voiceID: number = gve.parentVoiceEntry.ParentVoice.VoiceId;
-                if (!(voiceID in this.vfVoices)) {
-                    this.vfVoices[voiceID] = new Vex.Flow.Voice({
+        const voices: Voice[] = this.getVoicesWithinMeasure();
+
+        for (const voice of voices) {
+            // add a vexFlow voice for this voice:
+            this.vfVoices[voice.VoiceId] = new Vex.Flow.Voice({
                         beat_value: this.parentSourceMeasure.Duration.Denominator,
                         beat_value: this.parentSourceMeasure.Duration.Denominator,
                         num_beats: this.parentSourceMeasure.Duration.Numerator,
                         num_beats: this.parentSourceMeasure.Duration.Numerator,
                         resolution: Vex.Flow.RESOLUTION,
                         resolution: Vex.Flow.RESOLUTION,
                     }).setMode(Vex.Flow.Voice.Mode.SOFT);
                     }).setMode(Vex.Flow.Voice.Mode.SOFT);
-                }
 
 
-                this.vfVoices[voiceID].addTickable((gve as VexFlowVoiceEntry).vfStaveNote);
+            const restFilledEntries: GraphicalVoiceEntry[] =  this.getRestFilledVexFlowStaveNotesPerVoice(voice);
+            // create vex flow voices and add tickables to it:
+            for (const voiceEntry of restFilledEntries) {
+                this.vfVoices[voice.VoiceId].addTickable((voiceEntry as VexFlowVoiceEntry).vfStaveNote);
             }
             }
         }
         }
         this.createArticulations();
         this.createArticulations();
@@ -508,7 +691,7 @@ export class VexFlowMeasure extends StaffMeasure {
             // create vex flow articulation:
             // create vex flow articulation:
             const graphicalVoiceEntries: GraphicalVoiceEntry[] = graphicalStaffEntry.graphicalVoiceEntries;
             const graphicalVoiceEntries: GraphicalVoiceEntry[] = graphicalStaffEntry.graphicalVoiceEntries;
             for (const gve of graphicalVoiceEntries) {
             for (const gve of graphicalVoiceEntries) {
-                const vfStaveNote: StaveNote = (gve as VexFlowVoiceEntry).vfStaveNote;
+                const vfStaveNote: StaveNote = ((gve as VexFlowVoiceEntry).vfStaveNote as StaveNote);
                 VexFlowConverter.generateArticulations(vfStaveNote, gve.notes[0].sourceNote.ParentVoiceEntry.Articulations);
                 VexFlowConverter.generateArticulations(vfStaveNote, gve.notes[0].sourceNote.ParentVoiceEntry.Articulations);
             }
             }
         }
         }

+ 11 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts

@@ -19,12 +19,20 @@ export class VexFlowStaffEntry extends GraphicalStaffEntry {
         let tickablePosition: number = 0;
         let tickablePosition: number = 0;
         let numberOfValidTickables: number = 0;
         let numberOfValidTickables: number = 0;
         for (const gve of this.graphicalVoiceEntries) {
         for (const gve of this.graphicalVoiceEntries) {
-            const tickable: Vex.Flow.StaveNote = (gve as VexFlowVoiceEntry).vfStaveNote;
+            const tickable: Vex.Flow.StemmableNote = (gve as VexFlowVoiceEntry).vfStaveNote;
             // This will let the tickable know how to calculate it's bounding box
             // This will let the tickable know how to calculate it's bounding box
             tickable.setStave(stave);
             tickable.setStave(stave);
             // The middle of the tickable is also the OSMD BoundingBox center
             // The middle of the tickable is also the OSMD BoundingBox center
-            const staveNote: Vex.Flow.StaveNote = (<Vex.Flow.StaveNote>tickable);
-            tickablePosition += staveNote.getNoteHeadEndX() - staveNote.getGlyphWidth() / 2;
+            if (tickable.getAttribute("type") === "StaveNote") {
+                // The middle of the tickable is also the OSMD BoundingBox center
+                const staveNote: Vex.Flow.StaveNote = tickable as Vex.Flow.StaveNote;
+                tickablePosition += staveNote.getNoteHeadEndX() - staveNote.getGlyphWidth() / 2;
+            } else {
+                console.log(tickable);
+                const ghostNote: Vex.Flow.GhostNote = tickable;
+                // That's basically the same as the StaveNote does.
+                tickablePosition = ghostNote.getAbsoluteX() + ghostNote.x_shift;
+            }
             numberOfValidTickables++;
             numberOfValidTickables++;
         }
         }
         tickablePosition = tickablePosition / numberOfValidTickables;
         tickablePosition = tickablePosition / numberOfValidTickables;

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

@@ -7,5 +7,5 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
         super(parentVoiceEntry, parentStaffEntry);
         super(parentVoiceEntry, parentStaffEntry);
     }
     }
 
 
-    public vfStaveNote: Vex.Flow.StaveNote;
+    public vfStaveNote: Vex.Flow.StemmableNote;
 }
 }

+ 1 - 1
webpack.dev.js

@@ -2,6 +2,6 @@ var merge = require('webpack-merge')
 var common = require('./webpack.common.js')
 var common = require('./webpack.common.js')
 
 
 module.exports = merge(common, {
 module.exports = merge(common, {
-    devtool: 'source-map',
+    devtool: 'inline-source-map',
     mode: 'development'
     mode: 'development'
 })
 })