|
@@ -20,6 +20,7 @@ import {Tuplet} from "../../VoiceData/Tuplet";
|
|
|
import { RepetitionInstructionEnum } from "../../VoiceData/Instructions/RepetitionInstruction";
|
|
|
import { SystemLinePosition } from "../SystemLinePosition";
|
|
|
import { StemDirectionType } from "../../VoiceData/VoiceEntry";
|
|
|
+import { Fraction } from "../../../Common/DataObjects/Fraction";
|
|
|
|
|
|
export class VexFlowMeasure extends StaffMeasure {
|
|
|
constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
|
|
@@ -342,6 +343,91 @@ export class VexFlowMeasure extends StaffMeasure {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 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 fillMissingRests(): void {
|
|
|
+ const latestVoiceTimestampDict: { [voiceID: number]: Fraction; } = {};
|
|
|
+
|
|
|
+ // 1) find front- and in-measure-gaps:
|
|
|
+ for (const staffEntry of this.staffEntries as VexFlowStaffEntry[]) {
|
|
|
+ for (const gNotesPerVoice of staffEntry.notes) {
|
|
|
+ // get voice id:
|
|
|
+ const voiceId: number = gNotesPerVoice[0].sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
|
|
|
+ const gNotesStartTimestamp: Fraction = gNotesPerVoice[0].sourceNote.getAbsoluteTimestamp();
|
|
|
+
|
|
|
+ // These 3 lines one render all "regular" notes:
|
|
|
+ const gnotes: { [voiceID: number]: GraphicalNote[]; } = staffEntry.graphicalNotes;
|
|
|
+ const vfnote: StaveNote = VexFlowConverter.StaveNote(gnotes[voiceId]);
|
|
|
+ staffEntry.vfNotes[voiceId] = vfnote;
|
|
|
+
|
|
|
+ // 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", this)
|
|
|
+ const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(gapFromMeasureStart);
|
|
|
+ // staffEntry.vfNotes[voiceId] = (vfghost as any);
|
|
|
+ // 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", 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)
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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
|
|
@@ -471,18 +557,18 @@ export class VexFlowMeasure extends StaffMeasure {
|
|
|
}
|
|
|
|
|
|
public staffMeasureCreatedCalculations(): void {
|
|
|
- for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
|
|
|
- const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
|
|
|
-
|
|
|
- // create vex flow Notes:
|
|
|
- const gnotes: { [voiceID: number]: GraphicalNote[]; } = graphicalStaffEntry.graphicalNotes;
|
|
|
- for (const voiceID in gnotes) {
|
|
|
- if (gnotes.hasOwnProperty(voiceID)) {
|
|
|
- const vfnote: StaveNote = VexFlowConverter.StaveNote(gnotes[voiceID]);
|
|
|
- (graphicalStaffEntry as VexFlowStaffEntry).vfNotes[voiceID] = vfnote;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ this.fillMissingRests();
|
|
|
+ // for (const graphicalStaffEntry of this.staffEntries as VexFlowStaffEntry[]) {
|
|
|
+ // // create vex flow Notes:
|
|
|
+ // const gnotes: { [voiceID: number]: GraphicalNote[]; } = graphicalStaffEntry.graphicalNotes;
|
|
|
+ // for (const voiceID in gnotes) {
|
|
|
+ // if (gnotes.hasOwnProperty(voiceID)) {
|
|
|
+ // // console.log(gnotes[voiceID][0]);
|
|
|
+ // // const vfnote: StaveNote = VexFlowConverter.StaveNote(gnotes[voiceID]);
|
|
|
+ // // (graphicalStaffEntry as VexFlowStaffEntry).vfNotes[voiceID] = vfnote;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
|
|
|
this.finalizeBeams();
|
|
|
this.finalizeTuplets();
|