VexFlowTabMeasure.ts 6.1 KB

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