VexFlowTabMeasure.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import Vex from "vexflow";
  2. import VF = Vex.Flow;
  3. import { Staff } from "../../VoiceData/Staff";
  4. import { SourceMeasure } from "../../VoiceData/SourceMeasure";
  5. import { VexFlowMeasure } from "./VexFlowMeasure";
  6. import { VexFlowStaffEntry } from "./VexFlowStaffEntry";
  7. import { VexFlowConverter } from "./VexFlowConverter";
  8. import { StaffLine } from "../StaffLine";
  9. import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
  10. import { VexFlowVoiceEntry } from "./VexFlowVoiceEntry";
  11. import { Arpeggio } from "../../VoiceData/Arpeggio";
  12. import { Voice } from "../../VoiceData/Voice";
  13. import log from "loglevel";
  14. export class VexFlowTabMeasure extends VexFlowMeasure {
  15. constructor(staff: Staff, sourceMeasure: SourceMeasure = undefined, staffLine: StaffLine = undefined) {
  16. super(staff, sourceMeasure, staffLine);
  17. this.isTabMeasure = true;
  18. }
  19. /**
  20. * Reset all the geometric values and parameters of this measure and put it in an initialized state.
  21. * This is needed to evaluate a measure a second time by system builder.
  22. */
  23. public resetLayout(): void {
  24. // Take into account some space for the begin and end lines of the stave
  25. // Will be changed when repetitions will be implemented
  26. //this.beginInstructionsWidth = 20 / UnitInPixels;
  27. //this.endInstructionsWidth = 20 / UnitInPixels;
  28. const stafflineCount: number = this.ParentStaff.StafflineCount ?? 6; // if undefined, 6 by default (same as Vexflow default)
  29. this.stave = new VF.TabStave(0, 0, 0, {
  30. space_above_staff_ln: 0,
  31. space_below_staff_ln: 0,
  32. num_lines: stafflineCount
  33. });
  34. // also see VexFlowMusicSheetDrawer.drawSheet() for some other vexflow default value settings (like default font scale)
  35. this.updateInstructionWidth();
  36. }
  37. public graphicalMeasureCreatedCalculations(): void {
  38. for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
  39. const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
  40. // create vex flow Notes:
  41. for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
  42. if (gve.notes[0].sourceNote.isRest()) {
  43. const ghostNotes: VF.GhostNote[] = VexFlowConverter.GhostNotes(gve.notes[0].sourceNote.Length);
  44. (gve as VexFlowVoiceEntry).vfStaveNote = ghostNotes[0];
  45. (gve as VexFlowVoiceEntry).vfGhostNotes = ghostNotes; // we actually need multiple ghost notes sometimes, see #1062 Sep. 23 2021 comment
  46. } else {
  47. (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.CreateTabNote(gve);
  48. }
  49. }
  50. }
  51. if (this.rules.TupletNumbersInTabs) { // default false, don't show tuplets in tab measures
  52. this.finalizeTuplets();
  53. }
  54. const voices: Voice[] = this.getVoicesWithinMeasure();
  55. for (const voice of voices) {
  56. if (!voice) {
  57. continue;
  58. }
  59. // add a vexFlow voice for this voice:
  60. this.vfVoices[voice.VoiceId] = new VF.Voice({
  61. beat_value: this.parentSourceMeasure.Duration.Denominator,
  62. num_beats: this.parentSourceMeasure.Duration.Numerator,
  63. resolution: VF.RESOLUTION,
  64. }).setMode(VF.Voice.Mode.SOFT);
  65. const restFilledEntries: GraphicalVoiceEntry[] = this.getRestFilledVexFlowStaveNotesPerVoice(voice);
  66. // create vex flow voices and add tickables to it:
  67. for (const voiceEntry of restFilledEntries) {
  68. if (voiceEntry.parentVoiceEntry) {
  69. if (voiceEntry.parentVoiceEntry.IsGrace && !voiceEntry.parentVoiceEntry.GraceAfterMainNote) {
  70. continue;
  71. }
  72. }
  73. const vexFlowVoiceEntry: VexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry;
  74. if (voiceEntry.notes.length === 0 || !voiceEntry.notes[0] || !voiceEntry.notes[0].sourceNote.PrintObject) {
  75. // GhostNote, don't add modifiers like in-measure clefs
  76. if (vexFlowVoiceEntry.vfGhostNotes) {
  77. for (const ghostNote of vexFlowVoiceEntry.vfGhostNotes) {
  78. this.vfVoices[voice.VoiceId].addTickable(ghostNote);
  79. }
  80. } else {
  81. this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
  82. }
  83. continue;
  84. }
  85. // don't add non-tab fingerings for tab measures (doesn't work yet for tabnotes in vexflow, see VexFlowMeasure.createFingerings())
  86. // if (voiceEntry.parentVoiceEntry && this.rules.RenderFingerings) {
  87. // this.createFingerings(voiceEntry);
  88. // }
  89. // add Arpeggio
  90. if (voiceEntry.parentVoiceEntry && voiceEntry.parentVoiceEntry.Arpeggio) {
  91. const arpeggio: Arpeggio = voiceEntry.parentVoiceEntry.Arpeggio;
  92. // TODO right now our arpeggio object has all arpeggio notes from arpeggios across all voices.
  93. // see VoiceGenerator. Doesn't matter for Vexflow for now though
  94. if (voiceEntry.notes && voiceEntry.notes.length > 1) {
  95. const type: VF.Stroke.Type = VexFlowConverter.StrokeTypeFromArpeggioType(arpeggio.type);
  96. const stroke: VF.Stroke = new VF.Stroke(type, {
  97. all_voices: this.rules.ArpeggiosGoAcrossVoices
  98. // default: false. This causes arpeggios to always go across all voices, which is often unwanted.
  99. // also, this can cause infinite height of stroke, see #546
  100. });
  101. //if (arpeggio.notes.length === vexFlowVoiceEntry.notes.length) { // different workaround for endless y bug
  102. if (this.rules.RenderArpeggios) {
  103. vexFlowVoiceEntry.vfStaveNote.addStroke(0, stroke);
  104. }
  105. } else {
  106. log.debug(`[OSMD] arpeggio in measure ${this.MeasureNumber} could not be drawn.
  107. voice entry had less than two notes, arpeggio is likely between voice entries, not currently supported in Vexflow.`);
  108. // TODO: create new arpeggio with all the arpeggio's notes (arpeggio.notes), perhaps with GhostNotes in a new vfStaveNote. not easy.
  109. }
  110. }
  111. if (vexFlowVoiceEntry.vfGhostNotes) {
  112. for (const ghostNote of vexFlowVoiceEntry.vfGhostNotes) {
  113. this.vfVoices[voice.VoiceId].addTickable(ghostNote);
  114. }
  115. } else {
  116. this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
  117. }
  118. }
  119. }
  120. //this.createArticulations();
  121. //this.createOrnaments();
  122. }
  123. }