VexFlowMusicSheetCalculator.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. import {MusicSheetCalculator} from "../MusicSheetCalculator";
  2. import {VexFlowGraphicalSymbolFactory} from "./VexFlowGraphicalSymbolFactory";
  3. import {StaffMeasure} from "../StaffMeasure";
  4. import {StaffLine} from "../StaffLine";
  5. import {VoiceEntry} from "../../VoiceData/VoiceEntry";
  6. import {MusicSystem} from "../MusicSystem";
  7. import {GraphicalNote} from "../GraphicalNote";
  8. import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
  9. import {GraphicalMusicPage} from "../GraphicalMusicPage";
  10. import {GraphicalTie} from "../GraphicalTie";
  11. import {Tie} from "../../VoiceData/Tie";
  12. import {SourceMeasure} from "../../VoiceData/SourceMeasure";
  13. import {MultiExpression} from "../../VoiceData/Expressions/MultiExpression";
  14. import {RepetitionInstruction} from "../../VoiceData/Instructions/RepetitionInstruction";
  15. import {Beam} from "../../VoiceData/Beam";
  16. import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
  17. import {OctaveEnum} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
  18. import {Fraction} from "../../../Common/DataObjects/Fraction";
  19. import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
  20. import {LyricWord} from "../../VoiceData/Lyrics/LyricsWord";
  21. import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
  22. import {ArticulationEnum} from "../../VoiceData/VoiceEntry";
  23. import {Tuplet} from "../../VoiceData/Tuplet";
  24. import Dictionary from "typescript-collections/dist/lib/Dictionary";
  25. import {VexFlowMeasure} from "./VexFlowMeasure";
  26. import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
  27. import Vex = require("vexflow");
  28. import {Logging} from "../../../Common/Logging";
  29. import {unitInPixels} from "./VexFlowMusicSheetDrawer";
  30. import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
  31. export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
  32. constructor() {
  33. super(new VexFlowGraphicalSymbolFactory());
  34. MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer();
  35. }
  36. protected clearRecreatedObjects(): void {
  37. super.clearRecreatedObjects();
  38. for (let staffMeasures of this.graphicalMusicSheet.MeasureList) {
  39. for (let staffMeasure of staffMeasures) {
  40. (<VexFlowMeasure>staffMeasure).clean();
  41. }
  42. }
  43. }
  44. //protected clearSystemsAndMeasures(): void {
  45. // for (let measure of measures) {
  46. //
  47. // }
  48. //}
  49. /**
  50. * Calculates the x layout of the staff entries within the staff measures belonging to one source measure.
  51. * All staff entries are x-aligned throughout all vertically aligned staff measures.
  52. * This method is called within calculateXLayout.
  53. * The staff entries are aligned with minimum needed x distances.
  54. * The MinimumStaffEntriesWidth of every measure will be set - needed for system building.
  55. * @param measures
  56. * @returns the minimum required x width of the source measure (=list of staff measures)
  57. */
  58. protected calculateMeasureXLayout(measures: StaffMeasure[]): number {
  59. // Finalize beams
  60. /*for (let measure of measures) {
  61. (measure as VexFlowMeasure).finalizeBeams();
  62. (measure as VexFlowMeasure).finalizeTuplets();
  63. }*/
  64. // Format the voices
  65. let allVoices: Vex.Flow.Voice[] = [];
  66. let formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter({
  67. align_rests: true,
  68. });
  69. for (let measure of measures) {
  70. let mvoices: { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
  71. let voices: Vex.Flow.Voice[] = [];
  72. for (let voiceID in mvoices) {
  73. if (mvoices.hasOwnProperty(voiceID)) {
  74. voices.push(mvoices[voiceID]);
  75. allVoices.push(mvoices[voiceID]);
  76. }
  77. }
  78. if (voices.length === 0) {
  79. Logging.warn("Found a measure with no voices... Continuing anyway.", mvoices);
  80. continue;
  81. }
  82. formatter.joinVoices(voices);
  83. }
  84. let width: number = 200;
  85. if (allVoices.length > 0) {
  86. let firstMeasure: VexFlowMeasure = measures[0] as VexFlowMeasure;
  87. // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
  88. // FIXME: a more relaxed formatting of voices
  89. width = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels + 5.0;
  90. for (let measure of measures) {
  91. measure.minimumStaffEntriesWidth = width;
  92. (measure as VexFlowMeasure).formatVoices = undefined;
  93. }
  94. firstMeasure.formatVoices = (w: number) => {
  95. formatter.format(allVoices, w);
  96. };
  97. }
  98. return width;
  99. }
  100. protected createGraphicalTie(tie: Tie, startGse: GraphicalStaffEntry, endGse: GraphicalStaffEntry,
  101. startNote: GraphicalNote, endNote: GraphicalNote): GraphicalTie {
  102. return new GraphicalTie(tie, startNote, endNote);
  103. }
  104. protected updateStaffLineBorders(staffLine: StaffLine): void {
  105. return;
  106. }
  107. protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
  108. return;
  109. }
  110. protected staffMeasureCreatedCalculations(measure: StaffMeasure): void {
  111. (measure as VexFlowMeasure).staffMeasureCreatedCalculations();
  112. }
  113. /**
  114. * Can be used to calculate articulations, stem directions, helper(ledger) lines, and overlapping note x-displacement.
  115. * Is Excecuted per voice entry of a staff entry.
  116. * After that layoutStaffEntry is called.
  117. * @param voiceEntry
  118. * @param graphicalNotes
  119. * @param graphicalStaffEntry
  120. * @param hasPitchedNote
  121. * @param isGraceStaffEntry
  122. */
  123. protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[], graphicalStaffEntry: GraphicalStaffEntry,
  124. hasPitchedNote: boolean, isGraceStaffEntry: boolean): void {
  125. return;
  126. }
  127. /**
  128. * Do all layout calculations that have to be done per staff entry, like dots, ornaments, arpeggios....
  129. * This method is called after the voice entries are handled by layoutVoiceEntry().
  130. * @param graphicalStaffEntry
  131. */
  132. protected layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
  133. (graphicalStaffEntry.parentMeasure as VexFlowMeasure).layoutStaffEntry(graphicalStaffEntry);
  134. }
  135. /**
  136. * calculates the y positions of the staff lines within a system and
  137. * furthermore the y positions of the systems themselves.
  138. */
  139. protected calculateSystemYLayout(): void {
  140. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  141. let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  142. if (!this.leadSheet) {
  143. let globalY: number = this.rules.PageTopMargin + this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
  144. this.rules.TitleBottomDistance;
  145. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  146. let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  147. // calculate y positions of stafflines within system
  148. let y: number = 0;
  149. for (let line of musicSystem.StaffLines) {
  150. line.PositionAndShape.RelativePosition.y = y;
  151. y += 10;
  152. }
  153. // set y positions of systems using the previous system and a fixed distance.
  154. musicSystem.PositionAndShape.BorderBottom = y + 0;
  155. musicSystem.PositionAndShape.RelativePosition.x = this.rules.PageLeftMargin + this.rules.SystemLeftMargin;
  156. musicSystem.PositionAndShape.RelativePosition.y = globalY;
  157. globalY += y + 5;
  158. }
  159. }
  160. }
  161. }
  162. /**
  163. * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
  164. */
  165. protected initStaffMeasuresCreation(): void {
  166. return;
  167. }
  168. protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
  169. let startNote: VexFlowGraphicalNote = (tie.StartNote as VexFlowGraphicalNote);
  170. let vfStartNote: Vex.Flow.StaveNote = undefined;
  171. if (startNote !== undefined) {
  172. vfStartNote = startNote.vfnote[0];
  173. }
  174. let endNote: VexFlowGraphicalNote = (tie.EndNote as VexFlowGraphicalNote);
  175. let vfEndNote: Vex.Flow.StaveNote = undefined;
  176. if (endNote !== undefined) {
  177. vfEndNote = endNote.vfnote[0];
  178. }
  179. if (tieIsAtSystemBreak) {
  180. // split tie into two ties:
  181. let vfTie1: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
  182. first_note: vfStartNote,
  183. });
  184. let measure1: VexFlowMeasure = (startNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
  185. measure1.vfTies.push(vfTie1);
  186. let vfTie2: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
  187. last_note : vfEndNote,
  188. });
  189. let measure2: VexFlowMeasure = (endNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
  190. measure2.vfTies.push(vfTie2);
  191. } else {
  192. let vfTie: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
  193. first_note: vfStartNote,
  194. last_note : vfEndNote,
  195. });
  196. let measure: VexFlowMeasure = (endNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
  197. measure.vfTies.push(vfTie);
  198. }
  199. }
  200. protected calculateSingleStaffLineLyricsPosition(staffLine: StaffLine, lyricVersesNumber: number[]): void {
  201. return;
  202. }
  203. protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
  204. return;
  205. }
  206. protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction, measureIndex: number): void {
  207. return;
  208. }
  209. protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
  210. return;
  211. }
  212. protected handleTiedGraphicalNote( tiedGraphicalNote: GraphicalNote, beams: Beam[], activeClef: ClefInstruction,
  213. octaveShiftValue: OctaveEnum, graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction,
  214. openTie: Tie, isLastTieNote: boolean): void {
  215. return;
  216. }
  217. /**
  218. * Is called if a note is part of a beam.
  219. * @param graphicalNote
  220. * @param beam
  221. * @param openBeams a list of all currently open beams
  222. */
  223. protected handleBeam(graphicalNote: GraphicalNote, beam: Beam, openBeams: Beam[]): void {
  224. (graphicalNote.parentStaffEntry.parentMeasure as VexFlowMeasure).handleBeam(graphicalNote, beam);
  225. }
  226. protected handleVoiceEntryLyrics(lyricsEntries: Dictionary<number, LyricsEntry>, voiceEntry: VoiceEntry,
  227. graphicalStaffEntry: GraphicalStaffEntry, openLyricWords: LyricWord[]): void {
  228. return;
  229. }
  230. protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
  231. return;
  232. }
  233. protected handleVoiceEntryArticulations(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
  234. return;
  235. }
  236. /**
  237. * Is called if a note is part of a tuplet.
  238. * @param graphicalNote
  239. * @param tuplet
  240. * @param openTuplets a list of all currently open tuplets
  241. */
  242. protected handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet, openTuplets: Tuplet[]): void {
  243. (graphicalNote.parentStaffEntry.parentMeasure as VexFlowMeasure).handleTuplet(graphicalNote, tuplet);
  244. }
  245. }