|
@@ -17,11 +17,13 @@ import StaveNote = Vex.Flow.StaveNote;
|
|
|
import {Logging} from "../../../Common/Logging";
|
|
|
import {unitInPixels} from "./VexFlowMusicSheetDrawer";
|
|
|
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 {
|
|
|
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
|
|
|
* @param graphicalNote
|
|
|
* @param beam
|
|
@@ -411,7 +595,7 @@ export class VexFlowMeasure extends StaffMeasure {
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
notes.push(note);
|
|
|
}
|
|
@@ -449,7 +633,7 @@ export class VexFlowMeasure extends StaffMeasure {
|
|
|
const tupletStaveNotes: Vex.Flow.StaveNote[] = [];
|
|
|
const tupletVoiceEntries: VexFlowVoiceEntry[] = tupletBuilder[1];
|
|
|
for (const tupletVoiceEntry of tupletVoiceEntries) {
|
|
|
- tupletStaveNotes.push((tupletVoiceEntry).vfStaveNote);
|
|
|
+ tupletStaveNotes.push(((tupletVoiceEntry).vfStaveNote as StaveNote));
|
|
|
}
|
|
|
if (tupletStaveNotes.length > 1) {
|
|
|
const notesOccupied: number = 2;
|
|
@@ -471,31 +655,30 @@ export class VexFlowMeasure extends StaffMeasure {
|
|
|
}
|
|
|
|
|
|
public staffMeasureCreatedCalculations(): void {
|
|
|
- for (const graphicalStaffEntry of this.staffEntries) {
|
|
|
+ for (const graphicalStaffEntry of this.staffEntries as VexFlowStaffEntry[]) {
|
|
|
// create vex flow Stave Notes:
|
|
|
for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
|
|
|
- (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve.notes);
|
|
|
+ (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this.finalizeBeams();
|
|
|
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,
|
|
|
num_beats: this.parentSourceMeasure.Duration.Numerator,
|
|
|
resolution: Vex.Flow.RESOLUTION,
|
|
|
}).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();
|
|
@@ -508,7 +691,7 @@ export class VexFlowMeasure extends StaffMeasure {
|
|
|
// create vex flow articulation:
|
|
|
const graphicalVoiceEntries: GraphicalVoiceEntry[] = graphicalStaffEntry.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);
|
|
|
}
|
|
|
}
|