|
@@ -36,6 +36,7 @@ import {SkyBottomLineCalculator} from "../SkyBottomLineCalculator";
|
|
|
import { NoteType } from "../../VoiceData/NoteType";
|
|
|
import { Arpeggio } from "../../VoiceData/Arpeggio";
|
|
|
import { GraphicalTie } from "../GraphicalTie";
|
|
|
+import { Note } from "../../VoiceData/Note";
|
|
|
|
|
|
// type StemmableNote = VF.StemmableNote;
|
|
|
|
|
@@ -981,12 +982,21 @@ export class VexFlowMeasure extends GraphicalMeasure {
|
|
|
if (!this.rules.AutoBeamTabs && this.isTabMeasure) { // could also use an option tabBeams to disable beams there completely
|
|
|
return;
|
|
|
}
|
|
|
- let notesToAutoBeam: StemmableNote[] = [];
|
|
|
- let consecutiveBeamableNotes: StemmableNote[] = [];
|
|
|
+ let autoBeamId: number = 60; // start with 60 to not collide (ids) with xml beams
|
|
|
+ /** Link between OSMD note (Note) and Vexflow note (StaveNote).
|
|
|
+ * For adding OSMD beams (note.NoteBeam), we also need the note (+ corresponding vfnote)
|
|
|
+ * This avoids needing to check (stavenote as any).beam, and registers the beam in the OSMD Note(.NoteBeam).
|
|
|
+ */
|
|
|
+ interface LinkedNote {
|
|
|
+ vfStaveNote: StaveNote;
|
|
|
+ sourceNote: Note;
|
|
|
+ }
|
|
|
+ let notesToAutoBeam: LinkedNote[] = [];
|
|
|
+ let consecutiveBeamableNotes: LinkedNote[] = [];
|
|
|
let currentTuplet: Tuplet;
|
|
|
- let tupletNotesToAutoBeam: StaveNote[] = [];
|
|
|
+ let tupletNotesToAutoBeam: LinkedNote[] = [];
|
|
|
this.autoTupletVfBeams = [];
|
|
|
- const separateAutoBeams: StemmableNote[][] = []; // a set of separate beams, each having a set of notes (StemmableNote[]).
|
|
|
+ const separateAutoBeams: LinkedNote[][] = []; // a set of separate beams, each having a set of notes (StemmableNote[]).
|
|
|
this.autoVfBeams = []; // final VF.Beams will be pushed/collected into this
|
|
|
let timeSignature: Fraction = this.parentSourceMeasure.ActiveTimeSignature;
|
|
|
if (!timeSignature) { // this doesn't happen in OSMD, but maybe in a SourceGenerator
|
|
@@ -999,11 +1009,14 @@ export class VexFlowMeasure extends GraphicalMeasure {
|
|
|
}
|
|
|
}
|
|
|
}*/
|
|
|
-
|
|
|
for (const staffEntry of this.staffEntries) {
|
|
|
for (const gve of staffEntry.graphicalVoiceEntries) {
|
|
|
const vfStaveNote: StaveNote = <StaveNote> (gve as VexFlowVoiceEntry).vfStaveNote;
|
|
|
const gNote: GraphicalNote = gve.notes[0]; // TODO check for all notes within the graphical voice entry
|
|
|
+ const linkedNote: LinkedNote = {
|
|
|
+ vfStaveNote: vfStaveNote,
|
|
|
+ sourceNote: gNote.sourceNote
|
|
|
+ };
|
|
|
const isOnBeat: boolean = staffEntry.relInMeasureTimestamp.isOnBeat(timeSignature);
|
|
|
const haveTwoOrMoreNotesToBeamAlready: boolean = consecutiveBeamableNotes.length >= 2;
|
|
|
//const noteIsQuarterOrLonger: boolean = gNote.sourceNote.Length.CompareTo(new Fraction(1, 4)) >= 0; // trusting Fraction class, no float check
|
|
@@ -1061,37 +1074,57 @@ export class VexFlowMeasure extends GraphicalMeasure {
|
|
|
} else {
|
|
|
if (currentTuplet !== noteTuplet) { // new tuplet, finish old one
|
|
|
if (tupletNotesToAutoBeam.length > 1) {
|
|
|
- const vfBeam: VF.Beam = new VF.Beam(tupletNotesToAutoBeam, true);
|
|
|
+ const beamVFNotes: StaveNote[] = [];
|
|
|
+ for (const tupletNote of tupletNotesToAutoBeam) {
|
|
|
+ beamVFNotes.push(tupletNote.vfStaveNote);
|
|
|
+ }
|
|
|
+ const vfBeam: VF.Beam = new VF.Beam(beamVFNotes, true);
|
|
|
if (this.rules.FlatBeams) {
|
|
|
(<any>vfBeam).render_options.flat_beams = true;
|
|
|
(<any>vfBeam).render_options.flat_beam_offset = this.rules.FlatBeamOffset;
|
|
|
(<any>vfBeam).render_options.flat_beam_offset_per_beam = this.rules.FlatBeamOffsetPerBeam;
|
|
|
}
|
|
|
this.autoTupletVfBeams.push(vfBeam);
|
|
|
+
|
|
|
+ const osmdBeam: Beam = new Beam(autoBeamId++);
|
|
|
+ osmdBeam.AutoGenerated = true;
|
|
|
+ for (const tupletNote of tupletNotesToAutoBeam) {
|
|
|
+ osmdBeam.addNoteToBeam(tupletNote.sourceNote);
|
|
|
+ }
|
|
|
}
|
|
|
tupletNotesToAutoBeam = [];
|
|
|
currentTuplet = noteTuplet;
|
|
|
}
|
|
|
}
|
|
|
if (!tupletContainsUnbeamableNote) {
|
|
|
- tupletNotesToAutoBeam.push(vfStaveNote);
|
|
|
+ tupletNotesToAutoBeam.push(linkedNote);
|
|
|
}
|
|
|
continue;
|
|
|
} else {
|
|
|
currentTuplet = undefined;
|
|
|
}
|
|
|
|
|
|
- consecutiveBeamableNotes.push(vfStaveNote); // also happens on new beat
|
|
|
+ consecutiveBeamableNotes.push(linkedNote); // also happens on new beat
|
|
|
}
|
|
|
}
|
|
|
if (tupletNotesToAutoBeam.length >= 2) {
|
|
|
- const vfBeam: VF.Beam = new VF.Beam(tupletNotesToAutoBeam, true);
|
|
|
+ const beamVFNotes: StaveNote[] = [];
|
|
|
+ for (const tupletNote of tupletNotesToAutoBeam) {
|
|
|
+ beamVFNotes.push(tupletNote.vfStaveNote);
|
|
|
+ }
|
|
|
+ const vfBeam: VF.Beam = new VF.Beam(beamVFNotes, true);
|
|
|
if (this.rules.FlatBeams) {
|
|
|
(<any>vfBeam).render_options.flat_beams = true;
|
|
|
(<any>vfBeam).render_options.flat_beam_offset = this.rules.FlatBeamOffset;
|
|
|
(<any>vfBeam).render_options.flat_beam_offset_per_beam = this.rules.FlatBeamOffsetPerBeam;
|
|
|
}
|
|
|
this.autoTupletVfBeams.push(vfBeam);
|
|
|
+
|
|
|
+ const osmdBeam: Beam = new Beam(autoBeamId++);
|
|
|
+ osmdBeam.AutoGenerated = true;
|
|
|
+ for (const tupletNote of tupletNotesToAutoBeam) {
|
|
|
+ osmdBeam.addNoteToBeam(tupletNote.sourceNote);
|
|
|
+ }
|
|
|
}
|
|
|
if (consecutiveBeamableNotes.length >= 2) {
|
|
|
for (const note of consecutiveBeamableNotes) {
|
|
@@ -1116,7 +1149,11 @@ export class VexFlowMeasure extends GraphicalMeasure {
|
|
|
}
|
|
|
|
|
|
for (const notesForSeparateAutoBeam of separateAutoBeams) {
|
|
|
- const newBeams: VF.Beam[] = VF.Beam.generateBeams(notesForSeparateAutoBeam, generateBeamOptions);
|
|
|
+ const beamVFNotes: StaveNote[] = [];
|
|
|
+ for (const linkedNote of notesForSeparateAutoBeam) {
|
|
|
+ beamVFNotes.push(linkedNote.vfStaveNote);
|
|
|
+ }
|
|
|
+ const newBeams: VF.Beam[] = VF.Beam.generateBeams(beamVFNotes, generateBeamOptions);
|
|
|
for (const vfBeam of newBeams) {
|
|
|
if (this.rules.FlatBeams) {
|
|
|
(<any>vfBeam).render_options.flat_beams = true;
|
|
@@ -1151,9 +1188,11 @@ export class VexFlowMeasure extends GraphicalMeasure {
|
|
|
if (tupletStaveNotes.length > 1) {
|
|
|
const tuplet: Tuplet = tupletBuilder[0];
|
|
|
const notesOccupied: number = tuplet.Notes[0][0].NormalNotes;
|
|
|
- const bracketed: boolean = tuplet.Bracket ||
|
|
|
- (tuplet.TupletLabelNumber === 3 && this.rules.TripletsBracketed) ||
|
|
|
- (tuplet.TupletLabelNumber !== 3 && this.rules.TupletsBracketed);
|
|
|
+ const bracketed: boolean = tuplet.shouldBeBracketed(
|
|
|
+ this.rules.TupletsBracketedUseXMLValue,
|
|
|
+ this.rules.TupletsBracketed,
|
|
|
+ this.rules.TripletsBracketed
|
|
|
+ );
|
|
|
let location: number = VF.Tuplet.LOCATION_TOP;
|
|
|
if (tuplet.tupletLabelNumberPlacement === PlacementEnum.Below) {
|
|
|
location = VF.Tuplet.LOCATION_BOTTOM;
|