VexFlowMusicSheetCalculator.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. import {MusicSheetCalculator} from "../MusicSheetCalculator";
  2. import {VexFlowGraphicalSymbolFactory} from "./VexFlowGraphicalSymbolFactory";
  3. import {GraphicalMeasure} from "../GraphicalMeasure";
  4. import {StaffLine} from "../StaffLine";
  5. import {VoiceEntry} from "../../VoiceData/VoiceEntry";
  6. import {GraphicalNote} from "../GraphicalNote";
  7. import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
  8. import {GraphicalTie} from "../GraphicalTie";
  9. import {Tie} from "../../VoiceData/Tie";
  10. import {SourceMeasure} from "../../VoiceData/SourceMeasure";
  11. import {MultiExpression} from "../../VoiceData/Expressions/MultiExpression";
  12. import {RepetitionInstruction} from "../../VoiceData/Instructions/RepetitionInstruction";
  13. import {Beam} from "../../VoiceData/Beam";
  14. import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
  15. import {OctaveEnum, OctaveShift} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
  16. import {Fraction} from "../../../Common/DataObjects/Fraction";
  17. import {LyricWord} from "../../VoiceData/Lyrics/LyricsWord";
  18. import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
  19. import {ArticulationEnum} from "../../VoiceData/VoiceEntry";
  20. import {Tuplet} from "../../VoiceData/Tuplet";
  21. import {VexFlowMeasure} from "./VexFlowMeasure";
  22. import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
  23. import Vex = require("vexflow");
  24. import * as log from "loglevel";
  25. import {unitInPixels} from "./VexFlowMusicSheetDrawer";
  26. import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
  27. import {TechnicalInstruction} from "../../VoiceData/Instructions/TechnicalInstruction";
  28. import {GraphicalLyricEntry} from "../GraphicalLyricEntry";
  29. import {GraphicalLabel} from "../GraphicalLabel";
  30. import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
  31. import {GraphicalLyricWord} from "../GraphicalLyricWord";
  32. import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
  33. import { VexFlowOctaveShift } from "./VexFlowOctaveShift";
  34. import { VexFlowInstantaniousDynamicExpression } from "./VexFlowInstantaniousDynamicExpression";
  35. import {BoundingBox} from "../BoundingBox";
  36. import { EngravingRules } from "../EngravingRules";
  37. export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
  38. constructor() {
  39. super();
  40. MusicSheetCalculator.symbolFactory = new VexFlowGraphicalSymbolFactory();
  41. MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer();
  42. }
  43. protected clearRecreatedObjects(): void {
  44. super.clearRecreatedObjects();
  45. for (const graphicalMeasures of this.graphicalMusicSheet.MeasureList) {
  46. for (const graphicalMeasure of graphicalMeasures) {
  47. (<VexFlowMeasure>graphicalMeasure).clean();
  48. }
  49. }
  50. }
  51. protected formatMeasures(): void {
  52. for (const verticalMeasureList of this.graphicalMusicSheet.MeasureList) {
  53. const firstMeasure: VexFlowMeasure = verticalMeasureList[0] as VexFlowMeasure;
  54. // first measure has formatting method as lambda function object, but formats all measures. TODO this could be refactored
  55. firstMeasure.format();
  56. for (const measure of verticalMeasureList) {
  57. for (const staffEntry of measure.staffEntries) {
  58. (<VexFlowStaffEntry>staffEntry).calculateXPosition();
  59. }
  60. }
  61. }
  62. }
  63. //protected clearSystemsAndMeasures(): void {
  64. // for (let measure of measures) {
  65. //
  66. // }
  67. //}
  68. /**
  69. * Calculates the x layout of the staff entries within the staff measures belonging to one source measure.
  70. * All staff entries are x-aligned throughout all vertically aligned staff measures.
  71. * This method is called within calculateXLayout.
  72. * The staff entries are aligned with minimum needed x distances.
  73. * The MinimumStaffEntriesWidth of every measure will be set - needed for system building.
  74. * Prepares the VexFlow formatter for later formatting
  75. * Does not calculate measure width from lyrics (which is called from MusicSheetCalculator)
  76. * @param measures
  77. * @returns the minimum required x width of the source measure (=list of staff measures)
  78. */
  79. protected calculateMeasureXLayout(measures: GraphicalMeasure[]): number {
  80. // Finalize beams
  81. /*for (let measure of measures) {
  82. (measure as VexFlowMeasure).finalizeBeams();
  83. (measure as VexFlowMeasure).finalizeTuplets();
  84. }*/
  85. // Format the voices
  86. const allVoices: Vex.Flow.Voice[] = [];
  87. const formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter();
  88. for (const measure of measures) {
  89. const mvoices: { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
  90. const voices: Vex.Flow.Voice[] = [];
  91. for (const voiceID in mvoices) {
  92. if (mvoices.hasOwnProperty(voiceID)) {
  93. voices.push(mvoices[voiceID]);
  94. allVoices.push(mvoices[voiceID]);
  95. }
  96. }
  97. if (voices.length === 0) {
  98. log.warn("Found a measure with no voices... Continuing anyway.", mvoices);
  99. continue;
  100. }
  101. // all voices that belong to one stave are collectively added to create a common context in VexFlow.
  102. formatter.joinVoices(voices);
  103. }
  104. let minStaffEntriesWidth: number = 200;
  105. if (allVoices.length > 0) {
  106. // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
  107. // FIXME: a more relaxed formatting of voices
  108. minStaffEntriesWidth = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels + 5.0;
  109. // firstMeasure.formatVoices = (w: number) => {
  110. // formatter.format(allVoices, w);
  111. // };
  112. MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minStaffEntriesWidth);
  113. for (const measure of measures) {
  114. if (measure === measures[0]) {
  115. const vexflowMeasure: VexFlowMeasure = (measure as VexFlowMeasure);
  116. // prepare format function for voices, will be called later for formatting measure again
  117. vexflowMeasure.formatVoices = (w: number) => {
  118. formatter.format(allVoices, w, {
  119. align_rests: true,
  120. });
  121. };
  122. // format now for minimum width
  123. vexflowMeasure.formatVoices(minStaffEntriesWidth * unitInPixels);
  124. } else {
  125. (measure as VexFlowMeasure).formatVoices = undefined;
  126. }
  127. }
  128. }
  129. for (const graphicalMeasure of measures) {
  130. for (const staffEntry of graphicalMeasure.staffEntries) {
  131. // here the measure modifiers are not yet set, therefore the begin instruction width will be empty
  132. (<VexFlowStaffEntry>staffEntry).calculateXPosition();
  133. }
  134. }
  135. // calculateMeasureWidthFromLyrics() will be called from MusicSheetCalculator after this
  136. return minStaffEntriesWidth;
  137. }
  138. public calculateMeasureWidthFromLyrics(measuresVertical: GraphicalMeasure[], oldMinimumStaffEntriesWidth: number): number {
  139. let elongationFactorMeasureWidth: number = 1;
  140. // information we need for the previous lyricsEntries to space the current one
  141. interface LyricEntryInfo {
  142. extend: boolean;
  143. labelHalfWidth: number;
  144. staffEntryXPosition: number;
  145. text: string;
  146. measureNumber: number;
  147. }
  148. // holds lyrics entries for verses i
  149. interface LyricEntryDict {
  150. [i: number]: LyricEntryInfo;
  151. }
  152. for (const measure of measuresVertical) {
  153. const lastLyricEntryDict: LyricEntryDict = {}; // holds info about last lyrics entries for all verses
  154. for (let i: number = 0; i < measure.staffEntries.length; i++) {
  155. const staffEntry: GraphicalStaffEntry = measure.staffEntries[i];
  156. if (staffEntry.LyricsEntries.length === 0) {
  157. continue;
  158. }
  159. // for all verses
  160. for (let j: number = 0; j < staffEntry.LyricsEntries.length; j++) {
  161. const lyricsEntry: GraphicalLyricEntry = staffEntry.LyricsEntries[j];
  162. // const lyricsEntryText = lyricsEntry.GetLyricsEntry.Text; // for easier debugging
  163. let minLyricsSpacing: number = EngravingRules.Rules.HorizontalBetweenLyricsDistance;
  164. // spacing for multi-syllable words
  165. if (lyricsEntry.ParentLyricWord) {
  166. if (lyricsEntry.GetLyricsEntry.SyllableIndex > 0) { // syllables after first
  167. // give a little more spacing for dash between syllables
  168. minLyricsSpacing = EngravingRules.Rules.BetweenSyllabelMinimumDistance;
  169. }
  170. }
  171. const lyricsBbox: BoundingBox = lyricsEntry.GraphicalLabel.PositionAndShape;
  172. const lyricsLabelHalfWidth: number = lyricsBbox.Size.width / 2;
  173. const staffEntryXPosition: number = (staffEntry as VexFlowStaffEntry).PositionAndShape.RelativePosition.x;
  174. // if we don't have a previous lyricEntry, skip spacing, just save lastLyricEntry information
  175. if (lastLyricEntryDict[j] !== undefined) {
  176. if (lastLyricEntryDict[j].extend) {
  177. // TODO handle extend of last entry (extend is stored in lyrics entry of preceding syllable)
  178. }
  179. const spaceNeededByLyrics: number =
  180. lastLyricEntryDict[j].labelHalfWidth + lyricsLabelHalfWidth + minLyricsSpacing;
  181. const staffEntrySpacing: number = staffEntryXPosition - lastLyricEntryDict[j].staffEntryXPosition;
  182. // get factor of how much we need to stretch the measure to space the current lyric with the last one
  183. const elongationFactorMeasureWidthForCurrentLabels: number = spaceNeededByLyrics / staffEntrySpacing;
  184. elongationFactorMeasureWidth = Math.max(elongationFactorMeasureWidth, elongationFactorMeasureWidthForCurrentLabels);
  185. }
  186. // TODO for spacing between last lyric of a measure and first lyric of the next measure,
  187. // we need to look ahead into the next measure, because first note position is not affected
  188. // by measure elongation. or return this elongation and let MusicSheetCalculator apply it to prev. measure
  189. // e.g. for Austrian national hymn:
  190. // if (lyricsEntry.GetLyricsEntry.Text === "kunfts") {
  191. // elongationFactorMeasureWidth *= 1.5;
  192. // }
  193. // set up last lyric entry information for next measure
  194. lastLyricEntryDict[j] = {
  195. extend: lyricsEntry.GetLyricsEntry.extend,
  196. labelHalfWidth: lyricsLabelHalfWidth,
  197. measureNumber: measure.MeasureNumber,
  198. staffEntryXPosition: staffEntryXPosition,
  199. text: lyricsEntry.GetLyricsEntry.Text,
  200. // lyricExtend: lyricExtend
  201. };
  202. }
  203. }
  204. }
  205. return oldMinimumStaffEntriesWidth * elongationFactorMeasureWidth;
  206. // calculateMeasureWidthFromLyrics is called afterwards from MusicSheetCalculator
  207. }
  208. protected createGraphicalTie(tie: Tie, startGse: GraphicalStaffEntry, endGse: GraphicalStaffEntry,
  209. startNote: GraphicalNote, endNote: GraphicalNote): GraphicalTie {
  210. return new GraphicalTie(tie, startNote, endNote);
  211. }
  212. protected updateStaffLineBorders(staffLine: StaffLine): void {
  213. staffLine.SkyBottomLineCalculator.updateStaffLineBorders();
  214. }
  215. protected graphicalMeasureCreatedCalculations(measure: GraphicalMeasure): void {
  216. (measure as VexFlowMeasure).graphicalMeasureCreatedCalculations();
  217. }
  218. /**
  219. * Can be used to calculate articulations, stem directions, helper(ledger) lines, and overlapping note x-displacement.
  220. * Is Excecuted per voice entry of a staff entry.
  221. * After that layoutStaffEntry is called.
  222. * @param voiceEntry
  223. * @param graphicalNotes
  224. * @param graphicalStaffEntry
  225. * @param hasPitchedNote
  226. */
  227. protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[], graphicalStaffEntry: GraphicalStaffEntry,
  228. hasPitchedNote: boolean): void {
  229. return;
  230. }
  231. /**
  232. * Do all layout calculations that have to be done per staff entry, like dots, ornaments, arpeggios....
  233. * This method is called after the voice entries are handled by layoutVoiceEntry().
  234. * @param graphicalStaffEntry
  235. */
  236. protected layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
  237. (graphicalStaffEntry.parentMeasure as VexFlowMeasure).layoutStaffEntry(graphicalStaffEntry);
  238. }
  239. /**
  240. * calculates the y positions of the staff lines within a system and
  241. * furthermore the y positions of the systems themselves.
  242. */
  243. protected calculateSystemYLayout(): void {
  244. for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
  245. for (const musicSystem of graphicalMusicPage.MusicSystems) {
  246. this.optimizeDistanceBetweenStaffLines(musicSystem);
  247. }
  248. // set y positions of systems using the previous system and a fixed distance.
  249. this.calculateMusicSystemsRelativePositions(graphicalMusicPage);
  250. }
  251. }
  252. /**
  253. * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
  254. */
  255. protected initGraphicalMeasuresCreation(): void {
  256. return;
  257. }
  258. /**
  259. * add here all given articulations to the VexFlowGraphicalStaffEntry and prepare them for rendering.
  260. * @param articulations
  261. * @param voiceEntry
  262. * @param graphicalStaffEntry
  263. */
  264. protected layoutArticulationMarks(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
  265. // uncomment this when implementing:
  266. // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
  267. return;
  268. }
  269. /**
  270. * Calculate the shape (Bezier curve) for this tie.
  271. * @param tie
  272. * @param tieIsAtSystemBreak
  273. */
  274. protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
  275. const startNote: VexFlowGraphicalNote = (tie.StartNote as VexFlowGraphicalNote);
  276. const endNote: VexFlowGraphicalNote = (tie.EndNote as VexFlowGraphicalNote);
  277. let vfStartNote: Vex.Flow.StaveNote = undefined;
  278. let startNoteIndexInTie: number = 0;
  279. if (startNote !== undefined) {
  280. vfStartNote = startNote.vfnote[0];
  281. startNoteIndexInTie = startNote.vfnote[1];
  282. }
  283. let vfEndNote: Vex.Flow.StaveNote = undefined;
  284. let endNoteIndexInTie: number = 0;
  285. if (endNote !== undefined) {
  286. vfEndNote = endNote.vfnote[0];
  287. endNoteIndexInTie = endNote.vfnote[1];
  288. }
  289. if (tieIsAtSystemBreak) {
  290. // split tie into two ties:
  291. const vfTie1: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
  292. first_indices: [startNoteIndexInTie],
  293. first_note: vfStartNote
  294. });
  295. const measure1: VexFlowMeasure = (startNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
  296. measure1.vfTies.push(vfTie1);
  297. const vfTie2: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
  298. last_indices: [endNoteIndexInTie],
  299. last_note: vfEndNote
  300. });
  301. const measure2: VexFlowMeasure = (endNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
  302. measure2.vfTies.push(vfTie2);
  303. } else {
  304. // normal case
  305. const vfTie: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
  306. first_indices: [startNoteIndexInTie],
  307. first_note: vfStartNote,
  308. last_indices: [endNoteIndexInTie],
  309. last_note: vfEndNote
  310. });
  311. const measure: VexFlowMeasure = (endNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure);
  312. measure.vfTies.push(vfTie);
  313. }
  314. }
  315. protected calculateDynamicExpressionsForSingleMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
  316. if (multiExpression.InstantaniousDynamic) {
  317. const timeStamp: Fraction = multiExpression.Timestamp;
  318. const measure: GraphicalMeasure = this.graphicalMusicSheet.MeasureList[measureIndex][staffIndex];
  319. const startStaffEntry: GraphicalStaffEntry = measure.findGraphicalStaffEntryFromTimestamp(timeStamp);
  320. const idx: VexFlowInstantaniousDynamicExpression = new VexFlowInstantaniousDynamicExpression(multiExpression.InstantaniousDynamic, startStaffEntry);
  321. // idx.calculcateBottomLine(measure);
  322. (measure as VexFlowMeasure).instantaniousDynamics.push(idx);
  323. }
  324. }
  325. /**
  326. * Calculate a single OctaveShift for a [[MultiExpression]].
  327. * @param sourceMeasure
  328. * @param multiExpression
  329. * @param measureIndex
  330. * @param staffIndex
  331. */
  332. protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
  333. // calculate absolute Timestamp and startStaffLine (and EndStaffLine if needed)
  334. const octaveShift: OctaveShift = multiExpression.OctaveShiftStart;
  335. const startTimeStamp: Fraction = octaveShift.ParentStartMultiExpression.Timestamp;
  336. const endTimeStamp: Fraction = octaveShift.ParentEndMultiExpression.Timestamp;
  337. const startStaffLine: StaffLine = this.graphicalMusicSheet.MeasureList[measureIndex][staffIndex].ParentStaffLine;
  338. let endMeasure: GraphicalMeasure = undefined;
  339. if (octaveShift.ParentEndMultiExpression !== undefined) {
  340. endMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(octaveShift.ParentEndMultiExpression.SourceMeasureParent,
  341. staffIndex);
  342. }
  343. let startMeasure: GraphicalMeasure = undefined;
  344. if (octaveShift.ParentEndMultiExpression !== undefined) {
  345. startMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(octaveShift.ParentStartMultiExpression.SourceMeasureParent,
  346. staffIndex);
  347. }
  348. if (endMeasure !== undefined) {
  349. // calculate GraphicalOctaveShift and RelativePositions
  350. const graphicalOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, startStaffLine.PositionAndShape);
  351. startStaffLine.OctaveShifts.push(graphicalOctaveShift);
  352. // calculate RelativePosition and Dashes
  353. const startStaffEntry: GraphicalStaffEntry = startMeasure.findGraphicalStaffEntryFromTimestamp(startTimeStamp);
  354. const endStaffEntry: GraphicalStaffEntry = endMeasure.findGraphicalStaffEntryFromTimestamp(endTimeStamp);
  355. graphicalOctaveShift.setStartNote(startStaffEntry);
  356. if (endMeasure.ParentStaffLine !== startMeasure.ParentStaffLine) {
  357. graphicalOctaveShift.endsOnDifferentStaffLine = true;
  358. const lastMeasure: GraphicalMeasure = startMeasure.ParentStaffLine.Measures[startMeasure.ParentStaffLine.Measures.length - 1];
  359. const lastNote: GraphicalStaffEntry = lastMeasure.staffEntries[lastMeasure.staffEntries.length - 1];
  360. graphicalOctaveShift.setEndNote(lastNote);
  361. // Now finish the shift on the next line
  362. const remainingOctaveShift: VexFlowOctaveShift = new VexFlowOctaveShift(octaveShift, endMeasure.PositionAndShape);
  363. endMeasure.ParentStaffLine.OctaveShifts.push(remainingOctaveShift);
  364. const firstMeasure: GraphicalMeasure = endMeasure.ParentStaffLine.Measures[0];
  365. const firstNote: GraphicalStaffEntry = firstMeasure.staffEntries[0];
  366. remainingOctaveShift.setStartNote(firstNote);
  367. remainingOctaveShift.setEndNote(endStaffEntry);
  368. } else {
  369. graphicalOctaveShift.setEndNote(endStaffEntry);
  370. }
  371. } else {
  372. log.warn("End measure for octave shift is undefined! This should not happen!");
  373. }
  374. }
  375. /**
  376. * Calculate all the textual and symbolic [[RepetitionInstruction]]s (e.g. dal segno) for a single [[SourceMeasure]].
  377. * @param repetitionInstruction
  378. * @param measureIndex
  379. */
  380. protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction, measureIndex: number): void {
  381. // find first visible StaffLine
  382. let uppermostMeasure: VexFlowMeasure = undefined;
  383. const measures: VexFlowMeasure[] = <VexFlowMeasure[]>this.graphicalMusicSheet.MeasureList[measureIndex];
  384. for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
  385. const graphicalMeasure: VexFlowMeasure = measures[idx];
  386. if (graphicalMeasure.ParentStaffLine !== undefined && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
  387. uppermostMeasure = <VexFlowMeasure>graphicalMeasure;
  388. break;
  389. }
  390. }
  391. // ToDo: feature/Repetitions
  392. // now create corresponding graphical symbol or Text in VexFlow:
  393. // use top measure and staffline for positioning.
  394. if (uppermostMeasure !== undefined) {
  395. uppermostMeasure.addWordRepetition(repetitionInstruction);
  396. }
  397. }
  398. protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
  399. return;
  400. }
  401. /**
  402. * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.
  403. * @param tiedGraphicalNote
  404. * @param beams
  405. * @param activeClef
  406. * @param octaveShiftValue
  407. * @param graphicalStaffEntry
  408. * @param duration
  409. * @param openTie
  410. * @param isLastTieNote
  411. */
  412. protected handleTiedGraphicalNote(tiedGraphicalNote: GraphicalNote, beams: Beam[], activeClef: ClefInstruction,
  413. octaveShiftValue: OctaveEnum, graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction,
  414. openTie: Tie, isLastTieNote: boolean): void {
  415. return;
  416. }
  417. /**
  418. * Is called if a note is part of a beam.
  419. * @param graphicalNote
  420. * @param beam
  421. * @param openBeams a list of all currently open beams
  422. */
  423. protected handleBeam(graphicalNote: GraphicalNote, beam: Beam, openBeams: Beam[]): void {
  424. (graphicalNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure).handleBeam(graphicalNote, beam);
  425. }
  426. protected handleVoiceEntryLyrics(voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry, lyricWords: LyricWord[]): void {
  427. voiceEntry.LyricsEntries.forEach((key: number, lyricsEntry: LyricsEntry) => {
  428. const graphicalLyricEntry: GraphicalLyricEntry = new GraphicalLyricEntry(lyricsEntry,
  429. graphicalStaffEntry,
  430. this.rules.LyricsHeight,
  431. this.rules.StaffHeight);
  432. graphicalStaffEntry.LyricsEntries.push(graphicalLyricEntry);
  433. // create corresponding GraphicalLabel
  434. const graphicalLabel: GraphicalLabel = graphicalLyricEntry.GraphicalLabel;
  435. graphicalLabel.setLabelPositionAndShapeBorders();
  436. if (lyricsEntry.Word !== undefined) {
  437. const lyricsEntryIndex: number = lyricsEntry.Word.Syllables.indexOf(lyricsEntry);
  438. let index: number = lyricWords.indexOf(lyricsEntry.Word);
  439. if (index === -1) {
  440. lyricWords.push(lyricsEntry.Word);
  441. index = lyricWords.indexOf(lyricsEntry.Word);
  442. }
  443. if (this.graphicalLyricWords.length === 0 || index > this.graphicalLyricWords.length - 1) {
  444. const graphicalLyricWord: GraphicalLyricWord = new GraphicalLyricWord(lyricsEntry.Word);
  445. graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
  446. graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
  447. this.graphicalLyricWords.push(graphicalLyricWord);
  448. } else {
  449. const graphicalLyricWord: GraphicalLyricWord = this.graphicalLyricWords[index];
  450. graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
  451. graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
  452. if (graphicalLyricWord.isFilled()) {
  453. lyricWords.splice(index, 1);
  454. this.graphicalLyricWords.splice(this.graphicalLyricWords.indexOf(graphicalLyricWord), 1);
  455. }
  456. }
  457. }
  458. });
  459. }
  460. protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
  461. return;
  462. }
  463. /**
  464. * Add articulations to the given vexflow staff entry.
  465. * @param articulations
  466. * @param voiceEntry
  467. * @param graphicalStaffEntry
  468. */
  469. protected handleVoiceEntryArticulations(articulations: ArticulationEnum[],
  470. voiceEntry: VoiceEntry, staffEntry: GraphicalStaffEntry): void {
  471. // uncomment this when implementing:
  472. // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
  473. return;
  474. }
  475. /**
  476. * Add technical instructions to the given vexflow staff entry.
  477. * @param technicalInstructions
  478. * @param voiceEntry
  479. * @param staffEntry
  480. */
  481. protected handleVoiceEntryTechnicalInstructions(technicalInstructions: TechnicalInstruction[],
  482. voiceEntry: VoiceEntry, staffEntry: GraphicalStaffEntry): void {
  483. // uncomment this when implementing:
  484. // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
  485. return;
  486. }
  487. /**
  488. * Is called if a note is part of a tuplet.
  489. * @param graphicalNote
  490. * @param tuplet
  491. * @param openTuplets a list of all currently open tuplets
  492. */
  493. protected handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet, openTuplets: Tuplet[]): void {
  494. (graphicalNote.parentVoiceEntry.parentStaffEntry.parentMeasure as VexFlowMeasure).handleTuplet(graphicalNote, tuplet);
  495. }
  496. }