VexFlowMeasure.ts 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  1. import Vex from "vexflow";
  2. import {GraphicalMeasure} from "../GraphicalMeasure";
  3. import {SourceMeasure} from "../../VoiceData/SourceMeasure";
  4. import {Staff} from "../../VoiceData/Staff";
  5. import {StaffLine} from "../StaffLine";
  6. import {SystemLinesEnum} from "../SystemLinesEnum";
  7. import {ClefInstruction, ClefEnum} from "../../VoiceData/Instructions/ClefInstruction";
  8. import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
  9. import {RhythmInstruction} from "../../VoiceData/Instructions/RhythmInstruction";
  10. import {VexFlowConverter} from "./VexFlowConverter";
  11. import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
  12. import {Beam} from "../../VoiceData/Beam";
  13. import {GraphicalNote} from "../GraphicalNote";
  14. import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
  15. import StaveConnector = Vex.Flow.StaveConnector;
  16. import StaveNote = Vex.Flow.StaveNote;
  17. import StemmableNote = Vex.Flow.StemmableNote;
  18. import NoteSubGroup = Vex.Flow.NoteSubGroup;
  19. import log from "loglevel";
  20. import {unitInPixels} from "./VexFlowMusicSheetDrawer";
  21. import {Tuplet} from "../../VoiceData/Tuplet";
  22. import {RepetitionInstructionEnum, RepetitionInstruction, AlignmentType} from "../../VoiceData/Instructions/RepetitionInstruction";
  23. import {SystemLinePosition} from "../SystemLinePosition";
  24. import {StemDirectionType} from "../../VoiceData/VoiceEntry";
  25. import {GraphicalVoiceEntry} from "../GraphicalVoiceEntry";
  26. import {VexFlowVoiceEntry} from "./VexFlowVoiceEntry";
  27. import {Fraction} from "../../../Common/DataObjects/Fraction";
  28. import {Voice} from "../../VoiceData/Voice";
  29. import {LinkedVoice} from "../../VoiceData/LinkedVoice";
  30. import { EngravingRules } from "../EngravingRules";
  31. import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
  32. import {TechnicalInstruction, TechnicalInstructionType} from "../../VoiceData/Instructions/TechnicalInstruction";
  33. import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
  34. import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
  35. import {AutoBeamOptions} from "../../../OpenSheetMusicDisplay/OSMDOptions";
  36. import {NoteType, Arpeggio} from "../../VoiceData";
  37. import { SkyBottomLineCalculator } from "../SkyBottomLineCalculator";
  38. // type StemmableNote = Vex.Flow.StemmableNote;
  39. export class VexFlowMeasure extends GraphicalMeasure {
  40. constructor(staff: Staff, sourceMeasure: SourceMeasure = undefined, staffLine: StaffLine = undefined) {
  41. super(staff, sourceMeasure, staffLine);
  42. this.minimumStaffEntriesWidth = -1;
  43. /*
  44. * There is no case in which `staffLine === undefined && sourceMeasure === undefined` holds.
  45. * Hence, it is not necessary to specify an `else` case.
  46. * One can verify this through a usage search for this constructor.
  47. */
  48. if (staffLine) {
  49. this.rules = staffLine.ParentMusicSystem.rules;
  50. } else if (sourceMeasure) {
  51. this.rules = sourceMeasure.Rules;
  52. }
  53. this.resetLayout();
  54. }
  55. /** octaveOffset according to active clef */
  56. public octaveOffset: number = 3;
  57. /** The VexFlow Voices in the measure */
  58. public vfVoices: { [voiceID: number]: Vex.Flow.Voice; } = {};
  59. /** Call this function (if present) to x-format all the voices in the measure */
  60. public formatVoices: (width: number) => void;
  61. /** The VexFlow Ties in the measure */
  62. public vfTies: Vex.Flow.StaveTie[] = [];
  63. /** The repetition instructions given as words or symbols (coda, dal segno..) */
  64. public vfRepetitionWords: Vex.Flow.Repetition[] = [];
  65. /** The VexFlow Stave (= one measure in a staffline) */
  66. protected stave: Vex.Flow.Stave;
  67. /** VexFlow StaveConnectors (vertical lines) */
  68. protected connectors: Vex.Flow.StaveConnector[] = [];
  69. /** Intermediate object to construct beams */
  70. private beams: { [voiceID: number]: [Beam, VexFlowVoiceEntry[]][]; } = {};
  71. /** Beams created by (optional) autoBeam function. */
  72. private autoVfBeams: Vex.Flow.Beam[];
  73. /** Beams of tuplet notes created by (optional) autoBeam function. */
  74. private autoTupletVfBeams: Vex.Flow.Beam[];
  75. /** VexFlow Beams */
  76. private vfbeams: { [voiceID: number]: Vex.Flow.Beam[]; };
  77. /** Intermediate object to construct tuplets */
  78. protected tuplets: { [voiceID: number]: [Tuplet, VexFlowVoiceEntry[]][]; } = {};
  79. /** VexFlow Tuplets */
  80. private vftuplets: { [voiceID: number]: Vex.Flow.Tuplet[]; } = {};
  81. // The engraving rules of OSMD.
  82. public rules: EngravingRules;
  83. // Sets the absolute coordinates of the VFStave on the canvas
  84. public setAbsoluteCoordinates(x: number, y: number): void {
  85. this.stave.setX(x).setY(y);
  86. }
  87. /**
  88. * Reset all the geometric values and parameters of this measure and put it in an initialized state.
  89. * This is needed to evaluate a measure a second time by system builder.
  90. */
  91. public resetLayout(): void {
  92. // Take into account some space for the begin and end lines of the stave
  93. // Will be changed when repetitions will be implemented
  94. //this.beginInstructionsWidth = 20 / UnitInPixels;
  95. //this.endInstructionsWidth = 20 / UnitInPixels;
  96. // TODO save beginning and end bar type, set these again after new stave.
  97. this.stave = new Vex.Flow.Stave(0, 0, 0, {
  98. space_above_staff_ln: 0,
  99. space_below_staff_ln: 0,
  100. });
  101. // constructor sets beginning and end bar type to standard
  102. this.stave.setBegBarType(Vex.Flow.Barline.type.NONE); // technically not correct, but we'd need to set the next measure's beginning bar type
  103. if (this.parentSourceMeasure && this.parentSourceMeasure.endingBarStyleEnum === SystemLinesEnum.None) {
  104. // fix for vexflow ignoring ending barline style after new stave, apparently
  105. this.stave.setEndBarType(Vex.Flow.Barline.type.NONE);
  106. }
  107. // the correct bar types seem to be set later
  108. this.updateInstructionWidth();
  109. }
  110. public clean(): void {
  111. this.vfTies.length = 0;
  112. this.connectors = [];
  113. // Clean up instructions
  114. this.resetLayout();
  115. }
  116. /**
  117. * returns the x-width (in units) of a given measure line {SystemLinesEnum}.
  118. * @param line
  119. * @returns the x-width in osmd units
  120. */
  121. public getLineWidth(line: SystemLinesEnum): number {
  122. switch (line) {
  123. // return 0 for the normal lines, as the line width will be considered at the updateInstructionWidth() method using the stavemodifiers.
  124. // case SystemLinesEnum.SingleThin:
  125. // return 5.0 / unitInPixels;
  126. // case SystemLinesEnum.DoubleThin:
  127. // return 5.0 / unitInPixels;
  128. // case SystemLinesEnum.ThinBold:
  129. // return 5.0 / unitInPixels;
  130. // but just add a little extra space for repetitions (cosmetics):
  131. case SystemLinesEnum.BoldThinDots:
  132. case SystemLinesEnum.DotsThinBold:
  133. return 10.0 / unitInPixels;
  134. case SystemLinesEnum.DotsBoldBoldDots:
  135. return 10.0 / unitInPixels;
  136. default:
  137. return 0;
  138. }
  139. }
  140. /**
  141. * adds the given clef to the begin of the measure.
  142. * This has to update/increase BeginInstructionsWidth.
  143. * @param clef
  144. */
  145. public addClefAtBegin(clef: ClefInstruction): void {
  146. this.octaveOffset = clef.OctaveOffset;
  147. if (clef.ClefType === ClefEnum.TAB) {
  148. this.stave.addClef("tab", undefined, undefined, undefined);
  149. } else {
  150. const vfclef: { type: string, size: string, annotation: string } = VexFlowConverter.Clef(clef, "default");
  151. this.stave.addClef(vfclef.type, vfclef.size, vfclef.annotation, Vex.Flow.StaveModifier.Position.BEGIN);
  152. }
  153. this.updateInstructionWidth();
  154. }
  155. /**
  156. * adds the given key to the begin of the measure.
  157. * This has to update/increase BeginInstructionsWidth.
  158. * @param currentKey the new valid key.
  159. * @param previousKey the old cancelled key. Needed to show which accidentals are not valid any more.
  160. * @param currentClef the valid clef. Needed to put the accidentals on the right y-positions.
  161. */
  162. public addKeyAtBegin(currentKey: KeyInstruction, previousKey: KeyInstruction, currentClef: ClefInstruction): void {
  163. this.stave.setKeySignature(
  164. VexFlowConverter.keySignature(currentKey),
  165. VexFlowConverter.keySignature(previousKey),
  166. undefined
  167. );
  168. this.updateInstructionWidth();
  169. }
  170. /**
  171. * adds the given rhythm to the begin of the measure.
  172. * This has to update/increase BeginInstructionsWidth.
  173. * @param rhythm
  174. */
  175. public addRhythmAtBegin(rhythm: RhythmInstruction): void {
  176. const timeSig: Vex.Flow.TimeSignature = VexFlowConverter.TimeSignature(rhythm);
  177. this.stave.addModifier(
  178. timeSig,
  179. Vex.Flow.StaveModifier.Position.BEGIN
  180. );
  181. this.updateInstructionWidth();
  182. }
  183. /**
  184. * adds the given clef to the end of the measure.
  185. * This has to update/increase EndInstructionsWidth.
  186. * @param clef
  187. */
  188. public addClefAtEnd(clef: ClefInstruction): void {
  189. const vfclef: { type: string, size: string, annotation: string } = VexFlowConverter.Clef(clef, "small");
  190. this.stave.setEndClef(vfclef.type, vfclef.size, vfclef.annotation);
  191. this.updateInstructionWidth();
  192. }
  193. public addMeasureLine(lineType: SystemLinesEnum, linePosition: SystemLinePosition): void {
  194. switch (linePosition) {
  195. case SystemLinePosition.MeasureBegin:
  196. switch (lineType) {
  197. case SystemLinesEnum.BoldThinDots:
  198. this.stave.setBegBarType(Vex.Flow.Barline.type.REPEAT_BEGIN);
  199. break;
  200. default:
  201. //this.stave.setBegBarType(Vex.Flow.Barline.type.NONE); // not necessary, it seems
  202. break;
  203. }
  204. break;
  205. case SystemLinePosition.MeasureEnd:
  206. switch (lineType) {
  207. case SystemLinesEnum.DotsBoldBoldDots:
  208. this.stave.setEndBarType(Vex.Flow.Barline.type.REPEAT_BOTH);
  209. break;
  210. case SystemLinesEnum.DotsThinBold:
  211. this.stave.setEndBarType(Vex.Flow.Barline.type.REPEAT_END);
  212. break;
  213. case SystemLinesEnum.DoubleThin:
  214. this.stave.setEndBarType(Vex.Flow.Barline.type.DOUBLE);
  215. break;
  216. case SystemLinesEnum.ThinBold:
  217. this.stave.setEndBarType(Vex.Flow.Barline.type.END);
  218. break;
  219. case SystemLinesEnum.None:
  220. this.stave.setEndBarType(Vex.Flow.Barline.type.NONE);
  221. break;
  222. // TODO: Add support for additional Barline types when VexFlow supports them
  223. default:
  224. break;
  225. }
  226. break;
  227. default:
  228. break;
  229. }
  230. }
  231. /**
  232. * Adds a measure number to the top left corner of the measure
  233. * This method is not used currently in favor of the calculateMeasureNumberPlacement
  234. * method in the MusicSheetCalculator.ts
  235. */
  236. public addMeasureNumber(): void {
  237. const text: string = this.MeasureNumber.toString();
  238. const position: number = StavePositionEnum.ABOVE; //Vex.Flow.StaveModifier.Position.ABOVE;
  239. const options: any = {
  240. justification: 1,
  241. shift_x: 0,
  242. shift_y: 0,
  243. };
  244. this.stave.setText(text, position, options);
  245. }
  246. public addWordRepetition(repetitionInstruction: RepetitionInstruction): void {
  247. let instruction: Vex.Flow.Repetition.type = undefined;
  248. let position: any = Vex.Flow.StaveModifier.Position.END;
  249. switch (repetitionInstruction.type) {
  250. case RepetitionInstructionEnum.Segno:
  251. // create Segno Symbol:
  252. instruction = Vex.Flow.Repetition.type.SEGNO_LEFT;
  253. position = Vex.Flow.StaveModifier.Position.BEGIN;
  254. break;
  255. case RepetitionInstructionEnum.Coda:
  256. // create Coda Symbol:
  257. instruction = Vex.Flow.Repetition.type.CODA_LEFT;
  258. position = Vex.Flow.StaveModifier.Position.BEGIN;
  259. break;
  260. case RepetitionInstructionEnum.DaCapo:
  261. instruction = Vex.Flow.Repetition.type.DC;
  262. break;
  263. case RepetitionInstructionEnum.DalSegno:
  264. instruction = Vex.Flow.Repetition.type.DS;
  265. break;
  266. case RepetitionInstructionEnum.Fine:
  267. instruction = Vex.Flow.Repetition.type.FINE;
  268. break;
  269. case RepetitionInstructionEnum.ToCoda:
  270. //instruction = "To Coda";
  271. break;
  272. case RepetitionInstructionEnum.DaCapoAlFine:
  273. instruction = Vex.Flow.Repetition.type.DC_AL_FINE;
  274. break;
  275. case RepetitionInstructionEnum.DaCapoAlCoda:
  276. instruction = Vex.Flow.Repetition.type.DC_AL_CODA;
  277. break;
  278. case RepetitionInstructionEnum.DalSegnoAlFine:
  279. instruction = Vex.Flow.Repetition.type.DS_AL_FINE;
  280. break;
  281. case RepetitionInstructionEnum.DalSegnoAlCoda:
  282. instruction = Vex.Flow.Repetition.type.DS_AL_CODA;
  283. break;
  284. default:
  285. break;
  286. }
  287. if (instruction !== undefined) {
  288. this.stave.addModifier(new Vex.Flow.Repetition(instruction, 0, 0), position);
  289. return;
  290. }
  291. this.addVolta(repetitionInstruction);
  292. }
  293. private addVolta(repetitionInstruction: RepetitionInstruction): void {
  294. let voltaType: number = Vex.Flow.Volta.type.BEGIN;
  295. if (repetitionInstruction.type === RepetitionInstructionEnum.Ending) {
  296. switch (repetitionInstruction.alignment) {
  297. case AlignmentType.Begin:
  298. if (this.parentSourceMeasure.endsRepetitionEnding()) {
  299. voltaType = Vex.Flow.Volta.type.BEGIN_END;
  300. } else {
  301. voltaType = Vex.Flow.Volta.type.BEGIN;
  302. }
  303. break;
  304. case AlignmentType.End:
  305. if (this.parentSourceMeasure.beginsRepetitionEnding()) {
  306. //voltaType = Vex.Flow.Volta.type.BEGIN_END;
  307. // don't add BEGIN_END volta a second time:
  308. return;
  309. } else {
  310. voltaType = Vex.Flow.Volta.type.END;
  311. }
  312. break;
  313. default:
  314. break;
  315. }
  316. const skyBottomLineCalculator: SkyBottomLineCalculator = this.ParentStaffLine.SkyBottomLineCalculator;
  317. //Because of loss of accuracy when sampling (see SkyBottomLineCalculator.updateInRange), measures tend to overlap
  318. //This causes getSkyLineMinInRange to return an incorrect min value (one from the previous measure, which has been modified)
  319. //We need to offset the end of what we are measuring by a bit to prevent this, otherwise volta pairs step up
  320. const start: number = this.PositionAndShape.AbsolutePosition.x + this.PositionAndShape.BorderMarginLeft + 0.4;
  321. const end: number = this.PositionAndShape.AbsolutePosition.x + this.PositionAndShape.BorderMarginRight;
  322. //2 unit gap, since volta is positioned from y center it seems.
  323. //This prevents cases where the volta is rendered over another element
  324. const skylineMinForMeasure: number = skyBottomLineCalculator.getSkyLineMinInRange( start, end ) - 2;
  325. //-6 OSMD units is the 0 value that the volta is placed on. .1 extra so the skyline goes above the volta
  326. //instead of on the line itself
  327. let newSkylineValueForMeasure: number = -6.1 + this.rules.VoltaOffset;
  328. let vexFlowVoltaHeight: number = this.rules.VoltaOffset;
  329. //EngravingRules default offset is 2.5, can be user set.
  330. //2.5 gives us a good default value to work with.
  331. //if we calculate that the minimum skyline allowed by elements is above the default volta position, need to adjust volta up further
  332. if (skylineMinForMeasure < newSkylineValueForMeasure) {
  333. const skylineDifference: number = skylineMinForMeasure - newSkylineValueForMeasure;
  334. vexFlowVoltaHeight += skylineDifference;
  335. newSkylineValueForMeasure = skylineMinForMeasure;
  336. }
  337. /*
  338. Code here that is commented out is for finding the previous measure volta height
  339. and matching it or resetting it to ours.
  340. Still needs work. When we get to higher measure numbers, prev measures don't seem to be available?
  341. //if we already have a volta in the prev measure, should match it's height, or if we are higher, it should match ours
  342. //find previous sibling measure that may have volta
  343. let measureIndex = this.parentSourceMeasure.measureListIndex;
  344. //this.parentSourceMeasure.getPreviousMeasure();
  345. if(measureIndex > 0){
  346. let prevMeasureIndex = measureIndex-1;
  347. let prevMeasure : VexFlowMeasure = undefined;
  348. // find first visible StaffLine
  349. //Need to find the uppermost, where the volta would go
  350. const measures: VexFlowMeasure[] = <VexFlowMeasure[]>this.ParentMusicSystem.GraphicalMeasures[prevMeasureIndex];
  351. if(measures !== undefined && measures.length > 0){
  352. for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
  353. const graphicalMeasure: VexFlowMeasure = measures[idx];
  354. if (graphicalMeasure.ParentStaffLine !== undefined && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
  355. prevMeasure = <VexFlowMeasure>graphicalMeasure;
  356. break;
  357. }
  358. }
  359. }
  360. if(prevMeasure !== undefined){
  361. let prevStaveModifiers = prevMeasure.stave.getModifiers();
  362. for(let i = 0; i < prevStaveModifiers.length; i++){
  363. let nextStaveModifier = prevStaveModifiers[i];
  364. if(nextStaveModifier.hasOwnProperty("volta")){
  365. const prevskyBottomLineCalculator: SkyBottomLineCalculator = prevMeasure.ParentStaffLine.SkyBottomLineCalculator;
  366. const prevStart: number = prevMeasure.PositionAndShape.AbsolutePosition.x + prevMeasure.PositionAndShape.BorderMarginLeft + 0.4;
  367. const prevEnd: number = prevMeasure.PositionAndShape.AbsolutePosition.x + prevMeasure.PositionAndShape.BorderMarginRight;
  368. let prevMeasureSkyline: number = prevskyBottomLineCalculator.getSkyLineMinInRange(prevStart, prevEnd);
  369. //if prev skyline is higher, use it
  370. if(prevMeasureSkyline <= newSkylineValueForMeasure){
  371. let skylineDifference = prevMeasureSkyline - newSkylineValueForMeasure;
  372. vexFlowVoltaHeight += skylineDifference;
  373. newSkylineValueForMeasure = prevMeasureSkyline;
  374. } else { //otherwise, we are higher. Need to adjust prev
  375. (nextStaveModifier as any).y_shift = vexFlowVoltaHeight*10;
  376. prevMeasure.ParentStaffLine.SkyBottomLineCalculator.updateSkyLineInRange(prevStart, prevEnd, newSkylineValueForMeasure);
  377. }
  378. }
  379. }
  380. }
  381. }*/
  382. //convert to VF units (pixels)
  383. vexFlowVoltaHeight *= 10;
  384. this.stave.setVoltaType(voltaType, repetitionInstruction.endingIndices[0], vexFlowVoltaHeight);
  385. skyBottomLineCalculator.updateSkyLineInRange(start, end, newSkylineValueForMeasure);
  386. //this.ParentStaffLine.SkyBottomLineCalculator.calculateLines();
  387. }
  388. }
  389. /**
  390. * Sets the overall x-width of the measure.
  391. * @param width
  392. */
  393. public setWidth(width: number): void {
  394. super.setWidth(width);
  395. // Set the width of the Vex.Flow.Stave
  396. this.stave.setWidth(width * unitInPixels);
  397. // Force the width of the Begin Instructions
  398. //this.stave.setNoteStartX(this.beginInstructionsWidth * UnitInPixels);
  399. }
  400. /**
  401. * This method is called after the StaffEntriesScaleFactor has been set.
  402. * Here the final x-positions of the staff entries have to be set.
  403. * (multiply the minimal positions with the scaling factor, considering the BeginInstructionsWidth)
  404. */
  405. public layoutSymbols(): void {
  406. // vexflow does the x-layout
  407. }
  408. /**
  409. * Draw this measure on a VexFlow CanvasContext
  410. * @param ctx
  411. */
  412. public draw(ctx: Vex.IRenderContext): void {
  413. // Draw stave lines
  414. this.stave.setContext(ctx).draw();
  415. // Draw all voices
  416. for (const voiceID in this.vfVoices) {
  417. if (this.vfVoices.hasOwnProperty(voiceID)) {
  418. ctx.save();
  419. this.vfVoices[voiceID].draw(ctx, this.stave);
  420. ctx.restore();
  421. // this.vfVoices[voiceID].tickables.forEach(t => t.getBoundingBox().draw(ctx));
  422. // this.vfVoices[voiceID].tickables.forEach(t => t.getBoundingBox().draw(ctx));
  423. }
  424. }
  425. // Draw beams
  426. for (const voiceID in this.vfbeams) {
  427. if (this.vfbeams.hasOwnProperty(voiceID)) {
  428. for (const beam of this.vfbeams[voiceID]) {
  429. beam.setContext(ctx).draw();
  430. }
  431. }
  432. }
  433. // Draw auto-generated beams from Beam.generateBeams()
  434. if (this.autoVfBeams) {
  435. for (const beam of this.autoVfBeams) {
  436. beam.setContext(ctx).draw();
  437. }
  438. }
  439. if (this.autoTupletVfBeams) {
  440. for (const beam of this.autoTupletVfBeams) {
  441. beam.setContext(ctx).draw();
  442. }
  443. }
  444. // Draw tuplets
  445. for (const voiceID in this.vftuplets) {
  446. if (this.vftuplets.hasOwnProperty(voiceID)) {
  447. for (const tuplet of this.vftuplets[voiceID]) {
  448. tuplet.setContext(ctx).draw();
  449. }
  450. }
  451. }
  452. // Draw ties
  453. for (const tie of this.vfTies) {
  454. tie.setContext(ctx).draw();
  455. }
  456. // Draw vertical lines
  457. for (const connector of this.connectors) {
  458. connector.setContext(ctx).draw();
  459. }
  460. }
  461. // this currently formats multiple measures, see VexFlowMusicSheetCalculator.formatMeasures()
  462. public format(): void {
  463. // If this is the first stave in the vertical measure, call the format
  464. // method to set the width of all the voices
  465. if (this.formatVoices) {
  466. // set the width of the voices to the current measure width:
  467. // (The width of the voices does not include the instructions (StaveModifiers))
  468. this.formatVoices((this.PositionAndShape.Size.width - this.beginInstructionsWidth - this.endInstructionsWidth) * unitInPixels);
  469. }
  470. }
  471. /**
  472. * Returns all the voices that are present in this measure
  473. */
  474. public getVoicesWithinMeasure(): Voice[] {
  475. const voices: Voice[] = [];
  476. for (const gse of this.staffEntries) {
  477. for (const gve of gse.graphicalVoiceEntries) {
  478. if (voices.indexOf(gve.parentVoiceEntry.ParentVoice) === -1) {
  479. voices.push(gve.parentVoiceEntry.ParentVoice);
  480. }
  481. }
  482. }
  483. return voices;
  484. }
  485. /**
  486. * Returns all the graphicalVoiceEntries of a given Voice.
  487. * @param voice the voice for which the graphicalVoiceEntries shall be returned.
  488. */
  489. public getGraphicalVoiceEntriesPerVoice(voice: Voice): GraphicalVoiceEntry[] {
  490. const voiceEntries: GraphicalVoiceEntry[] = [];
  491. for (const gse of this.staffEntries) {
  492. for (const gve of gse.graphicalVoiceEntries) {
  493. if (gve.parentVoiceEntry.ParentVoice === voice) {
  494. voiceEntries.push(gve);
  495. }
  496. }
  497. }
  498. return voiceEntries;
  499. }
  500. /**
  501. * Finds the gaps between the existing notes within a measure.
  502. * Problem here is, that the graphicalVoiceEntry does not exist yet and
  503. * that Tied notes are not present in the normal voiceEntries.
  504. * To handle this, calculation with absolute timestamps is needed.
  505. * And the graphical notes have to be analysed directly (and not the voiceEntries, as it actually should be -> needs refactoring)
  506. * @param voice the voice for which the ghost notes shall be searched.
  507. */
  508. protected getRestFilledVexFlowStaveNotesPerVoice(voice: Voice): GraphicalVoiceEntry[] {
  509. let latestVoiceTimestamp: Fraction = undefined;
  510. const gvEntries: GraphicalVoiceEntry[] = this.getGraphicalVoiceEntriesPerVoice(voice);
  511. for (let idx: number = 0, len: number = gvEntries.length; idx < len; ++idx) {
  512. const gve: GraphicalVoiceEntry = gvEntries[idx];
  513. const gNotesStartTimestamp: Fraction = gve.notes[0].sourceNote.getAbsoluteTimestamp();
  514. // find the voiceEntry end timestamp:
  515. let gNotesEndTimestamp: Fraction = new Fraction();
  516. for (const graphicalNote of gve.notes) {
  517. const noteEnd: Fraction = Fraction.plus(graphicalNote.sourceNote.getAbsoluteTimestamp(), graphicalNote.sourceNote.Length);
  518. if (gNotesEndTimestamp.lt(noteEnd)) {
  519. gNotesEndTimestamp = noteEnd;
  520. }
  521. }
  522. // check if this voice has just been found the first time:
  523. if (latestVoiceTimestamp === undefined) {
  524. // if this voice is new, check for a gap from measure start to the start of the current voice entry:
  525. const gapFromMeasureStart: Fraction = Fraction.minus(gNotesStartTimestamp, this.parentSourceMeasure.AbsoluteTimestamp);
  526. if (gapFromMeasureStart.RealValue > 0) {
  527. log.trace(`Ghost Found at start (measure ${this.MeasureNumber})`); // happens too often for valid measures to be logged to debug
  528. const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(gapFromMeasureStart);
  529. const ghostGve: VexFlowVoiceEntry = new VexFlowVoiceEntry(undefined, undefined);
  530. ghostGve.vfStaveNote = vfghost;
  531. gvEntries.splice(0, 0, ghostGve);
  532. idx++;
  533. }
  534. } else {
  535. // get the length of the empty space between notes:
  536. const inBetweenLength: Fraction = Fraction.minus(gNotesStartTimestamp, latestVoiceTimestamp);
  537. if (inBetweenLength.RealValue > 0) {
  538. log.trace(`Ghost Found in between (measure ${this.MeasureNumber})`); // happens too often for valid measures to be logged to debug
  539. const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(inBetweenLength);
  540. const ghostGve: VexFlowVoiceEntry = new VexFlowVoiceEntry(undefined, undefined);
  541. ghostGve.vfStaveNote = vfghost;
  542. // add element before current element:
  543. gvEntries.splice(idx, 0, ghostGve);
  544. // and increase index, as we added an element:
  545. idx++;
  546. }
  547. }
  548. // finally set the latest timestamp of this voice to the end timestamp of the longest note in the current voiceEntry:
  549. latestVoiceTimestamp = gNotesEndTimestamp;
  550. }
  551. const measureEndTimestamp: Fraction = Fraction.plus(this.parentSourceMeasure.AbsoluteTimestamp, this.parentSourceMeasure.Duration);
  552. const restLength: Fraction = Fraction.minus(measureEndTimestamp, latestVoiceTimestamp);
  553. if (restLength.RealValue > 0) {
  554. // fill the gap with a rest ghost note
  555. // starting from lastFraction
  556. // with length restLength:
  557. log.trace(`Ghost Found at end (measure ${this.MeasureNumber})`); // happens too often for valid measures to be logged to debug
  558. const vfghost: Vex.Flow.GhostNote = VexFlowConverter.GhostNote(restLength);
  559. const ghostGve: VexFlowVoiceEntry = new VexFlowVoiceEntry(undefined, undefined);
  560. ghostGve.vfStaveNote = vfghost;
  561. gvEntries.push(ghostGve);
  562. }
  563. return gvEntries;
  564. }
  565. /**
  566. * Add a note to a beam
  567. * @param graphicalNote
  568. * @param beam
  569. */
  570. public handleBeam(graphicalNote: GraphicalNote, beam: Beam): void {
  571. const voiceID: number = graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
  572. let beams: [Beam, VexFlowVoiceEntry[]][] = this.beams[voiceID];
  573. if (beams === undefined) {
  574. beams = this.beams[voiceID] = [];
  575. }
  576. let data: [Beam, VexFlowVoiceEntry[]];
  577. for (const mybeam of beams) {
  578. if (mybeam[0] === beam) {
  579. data = mybeam;
  580. }
  581. }
  582. if (data === undefined) {
  583. data = [beam, []];
  584. beams.push(data);
  585. }
  586. const parent: VexFlowVoiceEntry = graphicalNote.parentVoiceEntry as VexFlowVoiceEntry;
  587. if (data[1].indexOf(parent) < 0) {
  588. data[1].push(parent);
  589. }
  590. }
  591. public handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet): void {
  592. const voiceID: number = graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
  593. tuplet = graphicalNote.sourceNote.NoteTuplet;
  594. let tuplets: [Tuplet, VexFlowVoiceEntry[]][] = this.tuplets[voiceID];
  595. if (tuplets === undefined) {
  596. tuplets = this.tuplets[voiceID] = [];
  597. }
  598. let currentTupletBuilder: [Tuplet, VexFlowVoiceEntry[]];
  599. for (const t of tuplets) {
  600. if (t[0] === tuplet) {
  601. currentTupletBuilder = t;
  602. }
  603. }
  604. if (currentTupletBuilder === undefined) {
  605. currentTupletBuilder = [tuplet, []];
  606. tuplets.push(currentTupletBuilder);
  607. }
  608. const parent: VexFlowVoiceEntry = graphicalNote.parentVoiceEntry as VexFlowVoiceEntry;
  609. if (currentTupletBuilder[1].indexOf(parent) < 0) {
  610. currentTupletBuilder[1].push(parent);
  611. }
  612. }
  613. /**
  614. * Complete the creation of VexFlow Beams in this measure
  615. */
  616. public finalizeBeams(): void {
  617. // The following line resets the created Vex.Flow Beams and
  618. // created them brand new. Is this needed? And more importantly,
  619. // should the old beams be removed manually by the notes?
  620. this.vfbeams = {};
  621. const beamedNotes: StaveNote[] = []; // already beamed notes, will be ignored by this.autoBeamNotes()
  622. for (const voiceID in this.beams) {
  623. if (this.beams.hasOwnProperty(voiceID)) {
  624. let vfbeams: Vex.Flow.Beam[] = this.vfbeams[voiceID];
  625. if (vfbeams === undefined) {
  626. vfbeams = this.vfbeams[voiceID] = [];
  627. }
  628. for (const beam of this.beams[voiceID]) {
  629. let beamHasQuarterNoteOrLonger: boolean = false;
  630. for (const note of beam[0].Notes) {
  631. if (note.Length.RealValue >= new Fraction(1, 4).RealValue
  632. // check whether the note has a TypeLength that's also not suitable for a beam (bigger than an eigth)
  633. && (note.TypeLength === undefined || note.TypeLength.RealValue > 0.125)) {
  634. beamHasQuarterNoteOrLonger = true;
  635. break;
  636. }
  637. }
  638. if (beamHasQuarterNoteOrLonger) {
  639. log.debug("Beam between note >= quarter, likely tremolo, currently unsupported. continuing.");
  640. continue;
  641. }
  642. const notes: Vex.Flow.StaveNote[] = [];
  643. const psBeam: Beam = beam[0];
  644. const voiceEntries: VexFlowVoiceEntry[] = beam[1];
  645. let autoStemBeam: boolean = true;
  646. for (const gve of voiceEntries) {
  647. if (gve.parentVoiceEntry.ParentVoice === psBeam.Notes[0].ParentVoiceEntry.ParentVoice) {
  648. autoStemBeam = gve.parentVoiceEntry.WantedStemDirection === StemDirectionType.Undefined;
  649. }
  650. }
  651. let isGraceBeam: boolean = false;
  652. let beamColor: string;
  653. const stemColors: string[] = [];
  654. for (const entry of voiceEntries) {
  655. const note: Vex.Flow.StaveNote = ((<VexFlowVoiceEntry>entry).vfStaveNote as StaveNote);
  656. if (note !== undefined) {
  657. notes.push(note);
  658. beamedNotes.push(note);
  659. }
  660. if (entry.parentVoiceEntry.IsGrace) {
  661. isGraceBeam = true;
  662. }
  663. if (entry.parentVoiceEntry.StemColor && this.rules.ColoringEnabled) {
  664. stemColors.push(entry.parentVoiceEntry.StemColor);
  665. }
  666. }
  667. if (notes.length > 1) {
  668. const vfBeam: Vex.Flow.Beam = new Vex.Flow.Beam(notes, autoStemBeam);
  669. if (isGraceBeam) {
  670. // smaller beam, as in Vexflow.GraceNoteGroup.beamNotes()
  671. (<any>vfBeam).render_options.beam_width = 3;
  672. (<any>vfBeam).render_options.partial_beam_length = 4;
  673. }
  674. if (stemColors.length >= 2 && this.rules.ColorBeams) {
  675. beamColor = stemColors[0];
  676. for (const stemColor of stemColors) {
  677. if (stemColor !== beamColor) {
  678. beamColor = undefined;
  679. break;
  680. }
  681. }
  682. vfBeam.setStyle({ fillStyle: beamColor, strokeStyle: beamColor });
  683. }
  684. vfbeams.push(vfBeam);
  685. } else {
  686. log.debug("Warning! Beam with no notes!");
  687. }
  688. }
  689. }
  690. }
  691. if (this.rules.AutoBeamNotes) {
  692. this.autoBeamNotes(beamedNotes); // try to autobeam notes except those that are already beamed (beamedNotes).
  693. }
  694. }
  695. /** Automatically creates beams for notes except beamedNotes, using Vexflow's Beam.generateBeams().
  696. * Takes options from this.rules.AutoBeamOptions.
  697. * @param beamedNotes notes that will not be autobeamed (usually because they are already beamed)
  698. */
  699. private autoBeamNotes(beamedNotes: StemmableNote[]): void {
  700. let notesToAutoBeam: StemmableNote[] = [];
  701. let consecutiveBeamableNotes: StemmableNote[] = [];
  702. let currentTuplet: Tuplet;
  703. let tupletNotesToAutoBeam: StaveNote[] = [];
  704. this.autoTupletVfBeams = [];
  705. const separateAutoBeams: StemmableNote[][] = []; // a set of separate beams, each having a set of notes (StemmableNote[]).
  706. this.autoVfBeams = []; // final Vex.Flow.Beams will be pushed/collected into this
  707. let timeSignature: Fraction = this.parentSourceMeasure.ActiveTimeSignature;
  708. if (timeSignature === undefined) { // this doesn't happen in OSMD, but maybe in a SourceGenerator
  709. timeSignature = this.parentSourceMeasure.Duration; // suboptimal, can be 1/1 in a 4/4 time signature
  710. }
  711. /*if (this.parentSourceMeasure.FirstInstructionsStaffEntries[0]) {
  712. for (const instruction of this.parentSourceMeasure.FirstInstructionsStaffEntries[0].Instructions) {
  713. if (instruction instanceof RhythmInstruction) { // there is not always a RhythmInstruction, but this could be useful some time.
  714. timeSignature = (instruction as RhythmInstruction).Rhythm;
  715. }
  716. }
  717. }*/
  718. for (const staffEntry of this.staffEntries) {
  719. for (const gve of staffEntry.graphicalVoiceEntries) {
  720. const vfStaveNote: StaveNote = <StaveNote> (gve as VexFlowVoiceEntry).vfStaveNote;
  721. const gNote: GraphicalNote = gve.notes[0]; // TODO check for all notes within the graphical voice entry
  722. const isOnBeat: boolean = staffEntry.relInMeasureTimestamp.isOnBeat(timeSignature);
  723. const haveTwoOrMoreNotesToBeamAlready: boolean = consecutiveBeamableNotes.length >= 2;
  724. //const noteIsQuarterOrLonger: boolean = gNote.sourceNote.Length.CompareTo(new Fraction(1, 4)) >= 0; // trusting Fraction class, no float check
  725. const noteIsQuarterOrLonger: boolean = gNote.sourceNote.Length.RealValue - new Fraction(1, 4).RealValue > (-Fraction.FloatInaccuracyTolerance);
  726. const unbeamableNote: boolean =
  727. gve.parentVoiceEntry.IsGrace || // don't beam grace notes
  728. noteIsQuarterOrLonger || // don't beam quarter or longer notes
  729. beamedNotes.contains(vfStaveNote);
  730. if (unbeamableNote || isOnBeat) { // end beam
  731. if (haveTwoOrMoreNotesToBeamAlready) {
  732. // if we already have at least 2 notes to beam, beam them. don't beam notes surrounded by quarter notes etc.
  733. for (const note of consecutiveBeamableNotes) {
  734. notesToAutoBeam.push(note); // "flush" already beamed notes
  735. }
  736. separateAutoBeams.push(notesToAutoBeam.slice()); // copy array, otherwise this beam gets the next notes of next beam later
  737. notesToAutoBeam = []; // reset notesToAutoBeam, otherwise the next beam includes the previous beam's notes too
  738. }
  739. consecutiveBeamableNotes = []; // reset notes to beam
  740. if (unbeamableNote) {
  741. continue;
  742. }
  743. // else, note will be pushed to consecutiveBeamableNotes after tuplet check, also for note on new beat
  744. }
  745. // create beams for tuplets separately
  746. const noteTuplet: Tuplet = gve.notes[0].sourceNote.NoteTuplet;
  747. if (noteTuplet) {
  748. // check if there are quarter notes or longer in the tuplet, then don't beam.
  749. // (TODO: check for consecutiveBeamableNotes inside tuplets like for non-tuplet notes above
  750. // e.g quarter eigth eighth -> beam the two eigth notes)
  751. let tupletContainsUnbeamableNote: boolean = false;
  752. for (const notes of noteTuplet.Notes) {
  753. for (const note of notes) {
  754. //const stavenote: StemmableNote = (gve as VexFlowVoiceEntry).vfStaveNote;
  755. //console.log("note " + note.ToString() + ", stavenote type: " + stavenote.getNoteType());
  756. if (note.NoteTypeXml >= NoteType.QUARTER || // quarter note or longer: don't beam
  757. // TODO: don't take Note (head) type from XML, but from current model,
  758. // so that rendering can react dynamically to changes compared to the XML.
  759. // however, taking the note length as fraction is tricky because of tuplets.
  760. // a quarter in a triplet has length < quarter, but quarter note head, which Vexflow can't beam.
  761. note.ParentVoiceEntry.IsGrace ||
  762. note.isRest() && !this.rules.AutoBeamOptions.beam_rests) {
  763. tupletContainsUnbeamableNote = true;
  764. break;
  765. }
  766. }
  767. if (tupletContainsUnbeamableNote) {
  768. break;
  769. }
  770. }
  771. if (currentTuplet === undefined) {
  772. currentTuplet = noteTuplet;
  773. } else {
  774. if (currentTuplet !== noteTuplet) { // new tuplet, finish old one
  775. if (tupletNotesToAutoBeam.length > 1) {
  776. this.autoTupletVfBeams.push(new Vex.Flow.Beam(tupletNotesToAutoBeam, true));
  777. }
  778. tupletNotesToAutoBeam = [];
  779. currentTuplet = noteTuplet;
  780. }
  781. }
  782. if (!tupletContainsUnbeamableNote) {
  783. tupletNotesToAutoBeam.push(vfStaveNote);
  784. }
  785. continue;
  786. } else {
  787. currentTuplet = undefined;
  788. }
  789. consecutiveBeamableNotes.push(vfStaveNote); // also happens on new beat
  790. }
  791. }
  792. if (tupletNotesToAutoBeam.length >= 2) {
  793. this.autoTupletVfBeams.push(new Vex.Flow.Beam(tupletNotesToAutoBeam, true));
  794. }
  795. if (consecutiveBeamableNotes.length >= 2) {
  796. for (const note of consecutiveBeamableNotes) {
  797. notesToAutoBeam.push(note);
  798. }
  799. separateAutoBeams.push(notesToAutoBeam);
  800. }
  801. // create options for generateBeams
  802. const autoBeamOptions: AutoBeamOptions = this.rules.AutoBeamOptions;
  803. const generateBeamOptions: any = {
  804. beam_middle_only: autoBeamOptions.beam_middle_rests_only,
  805. beam_rests: autoBeamOptions.beam_rests,
  806. maintain_stem_directions: autoBeamOptions.maintain_stem_directions,
  807. };
  808. if (autoBeamOptions.groups && autoBeamOptions.groups.length) {
  809. const groups: Vex.Flow.Fraction[] = [];
  810. for (const fraction of autoBeamOptions.groups) {
  811. groups.push(new Vex.Flow.Fraction(fraction[0], fraction[1]));
  812. }
  813. generateBeamOptions.groups = groups;
  814. }
  815. for (const notesForSeparateAutoBeam of separateAutoBeams) {
  816. const newBeams: Vex.Flow.Beam[] = Vex.Flow.Beam.generateBeams(notesForSeparateAutoBeam, generateBeamOptions);
  817. for (const beam of newBeams) {
  818. this.autoVfBeams.push(beam);
  819. }
  820. }
  821. }
  822. /**
  823. * Complete the creation of VexFlow Tuplets in this measure
  824. */
  825. public finalizeTuplets(): void {
  826. // The following line resets the created Vex.Flow Tuplets and
  827. // created them brand new. Is this needed? And more importantly,
  828. // should the old tuplets be removed manually from the notes?
  829. this.vftuplets = {};
  830. for (const voiceID in this.tuplets) {
  831. if (this.tuplets.hasOwnProperty(voiceID)) {
  832. let vftuplets: Vex.Flow.Tuplet[] = this.vftuplets[voiceID];
  833. if (vftuplets === undefined) {
  834. vftuplets = this.vftuplets[voiceID] = [];
  835. }
  836. for (const tupletBuilder of this.tuplets[voiceID]) {
  837. const tupletStaveNotes: Vex.Flow.StaveNote[] = [];
  838. const tupletVoiceEntries: VexFlowVoiceEntry[] = tupletBuilder[1];
  839. for (const tupletVoiceEntry of tupletVoiceEntries) {
  840. tupletStaveNotes.push(((tupletVoiceEntry).vfStaveNote as StaveNote));
  841. }
  842. if (tupletStaveNotes.length > 1) {
  843. const tuplet: Tuplet = tupletBuilder[0];
  844. const notesOccupied: number = tuplet.Notes[0][0].NormalNotes;
  845. const bracketed: boolean = tuplet.Bracket ||
  846. (tuplet.TupletLabelNumber === 3 && this.rules.TripletsBracketed) ||
  847. (tuplet.TupletLabelNumber !== 3 && this.rules.TupletsBracketed);
  848. vftuplets.push(new Vex.Flow.Tuplet( tupletStaveNotes,
  849. {
  850. bracketed: bracketed,
  851. notes_occupied: notesOccupied,
  852. num_notes: tuplet.TupletLabelNumber, //, location: -1, ratioed: true
  853. ratioed: this.rules.TupletsRatioed,
  854. }));
  855. } else {
  856. log.debug("Warning! Tuplet with no notes! Trying to ignore, but this is a serious problem.");
  857. }
  858. }
  859. }
  860. }
  861. }
  862. public layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
  863. return;
  864. }
  865. public graphicalMeasureCreatedCalculations(): void {
  866. let graceSlur: boolean;
  867. let graceGVoiceEntriesBefore: GraphicalVoiceEntry[] = [];
  868. for (const graphicalStaffEntry of this.staffEntries as VexFlowStaffEntry[]) {
  869. graceSlur = false;
  870. graceGVoiceEntriesBefore = [];
  871. // create vex flow Stave Notes:
  872. for (const gve of graphicalStaffEntry.graphicalVoiceEntries) {
  873. if (gve.parentVoiceEntry.IsGrace) {
  874. // save grace notes for the next non-grace note
  875. graceGVoiceEntriesBefore.push(gve);
  876. if (!graceSlur) {
  877. graceSlur = gve.parentVoiceEntry.GraceSlur;
  878. }
  879. continue;
  880. }
  881. if (gve.notes[0].sourceNote.PrintObject) {
  882. (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve);
  883. } else {
  884. // note can now also be added as StaveNote instead of GhostNote, because we set it to transparent
  885. (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve);
  886. // previous method: add as GhostNote instead of StaveNote. Can cause formatting issues if critical notes are missing in the measure
  887. // don't render note. add ghost note, otherwise Vexflow can have issues with layouting when voices not complete.
  888. //(gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.GhostNote(gve.notes[0].sourceNote.Length);
  889. //graceGVoiceEntriesBefore = []; // if note is not rendered, its grace notes shouldn't be rendered, might need to be removed
  890. //continue;
  891. }
  892. if (graceGVoiceEntriesBefore.length > 0) {
  893. // add grace notes that came before this main note to a GraceNoteGroup in Vexflow, attached to the main note
  894. const graceNotes: Vex.Flow.GraceNote[] = [];
  895. for (let i: number = 0; i < graceGVoiceEntriesBefore.length; i++) {
  896. const gveGrace: VexFlowVoiceEntry = <VexFlowVoiceEntry>graceGVoiceEntriesBefore[i];
  897. //if (gveGrace.notes[0].sourceNote.PrintObject) {
  898. // grace notes should generally be rendered independently of main note instead of skipped if main note is invisible
  899. // could be an option to make grace notes transparent if main note is transparent. set grace notes' PrintObject to false then.
  900. const vfStaveNote: StaveNote = VexFlowConverter.StaveNote(gveGrace);
  901. gveGrace.vfStaveNote = vfStaveNote;
  902. graceNotes.push(vfStaveNote);
  903. }
  904. const graceNoteGroup: Vex.Flow.GraceNoteGroup = new Vex.Flow.GraceNoteGroup(graceNotes, graceSlur);
  905. ((gve as VexFlowVoiceEntry).vfStaveNote as StaveNote).addModifier(0, graceNoteGroup);
  906. graceGVoiceEntriesBefore = [];
  907. }
  908. }
  909. }
  910. // remaining grace notes at end of measure, turned into stand-alone grace notes:
  911. if (graceGVoiceEntriesBefore.length > 0) {
  912. for (const graceGve of graceGVoiceEntriesBefore) {
  913. (graceGve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(graceGve);
  914. graceGve.parentVoiceEntry.GraceAfterMainNote = true;
  915. }
  916. }
  917. // const t0: number = performance.now();
  918. this.finalizeBeams();
  919. // const t1: number = performance.now();
  920. // console.log("Call to finalizeBeams in VexFlowMeasure took " + (t1 - t0) + " milliseconds.");
  921. this.finalizeTuplets();
  922. const voices: Voice[] = this.getVoicesWithinMeasure();
  923. for (const voice of voices) {
  924. if (voice === undefined) {
  925. continue;
  926. }
  927. const isMainVoice: boolean = !(voice instanceof LinkedVoice);
  928. // add a vexFlow voice for this voice:
  929. this.vfVoices[voice.VoiceId] = new Vex.Flow.Voice({
  930. beat_value: this.parentSourceMeasure.Duration.Denominator,
  931. num_beats: this.parentSourceMeasure.Duration.Numerator,
  932. resolution: Vex.Flow.RESOLUTION,
  933. }).setMode(Vex.Flow.Voice.Mode.SOFT);
  934. const restFilledEntries: GraphicalVoiceEntry[] = this.getRestFilledVexFlowStaveNotesPerVoice(voice);
  935. // create vex flow voices and add tickables to it:
  936. for (const voiceEntry of restFilledEntries) {
  937. if (voiceEntry.parentVoiceEntry) {
  938. if (voiceEntry.parentVoiceEntry.IsGrace && !voiceEntry.parentVoiceEntry.GraceAfterMainNote) {
  939. continue;
  940. }
  941. }
  942. const vexFlowVoiceEntry: VexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry;
  943. if (voiceEntry.notes.length === 0 || !voiceEntry.notes[0] || !voiceEntry.notes[0].sourceNote.PrintObject) {
  944. // GhostNote, don't add modifiers like in-measure clefs
  945. this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
  946. continue;
  947. }
  948. // check for in-measure clefs:
  949. // only add clefs in main voice (to not add them twice)
  950. if (isMainVoice) {
  951. const vfse: VexFlowStaffEntry = vexFlowVoiceEntry.parentStaffEntry as VexFlowStaffEntry;
  952. if (vfse && vfse.vfClefBefore !== undefined) {
  953. // add clef as NoteSubGroup so that we get modifier layouting
  954. const clefModifier: NoteSubGroup = new NoteSubGroup( [vfse.vfClefBefore] );
  955. // The cast is necesary because...vexflow -> see types
  956. if (vexFlowVoiceEntry.vfStaveNote.getCategory && vexFlowVoiceEntry.vfStaveNote.getCategory() === "stavenotes") {
  957. // GhostNotes and other StemmableNotes don't have this function
  958. (vexFlowVoiceEntry.vfStaveNote as Vex.Flow.StaveNote).addModifier(0, clefModifier);
  959. }
  960. }
  961. }
  962. // add fingering
  963. if (voiceEntry.parentVoiceEntry && this.rules.RenderFingerings) {
  964. this.createFingerings(voiceEntry);
  965. }
  966. // add Arpeggio
  967. if (voiceEntry.parentVoiceEntry && voiceEntry.parentVoiceEntry.Arpeggio !== undefined) {
  968. const arpeggio: Arpeggio = voiceEntry.parentVoiceEntry.Arpeggio;
  969. // TODO right now our arpeggio object has all arpeggio notes from arpeggios across all voices.
  970. // see VoiceGenerator. Doesn't matter for Vexflow for now though
  971. if (voiceEntry.notes && voiceEntry.notes.length > 1) {
  972. const type: Vex.Flow.Stroke.Type = VexFlowConverter.StrokeTypeFromArpeggioType(arpeggio.type);
  973. const stroke: Vex.Flow.Stroke = new Vex.Flow.Stroke(type, {
  974. all_voices: this.rules.ArpeggiosGoAcrossVoices
  975. // default: false. This causes arpeggios to always go across all voices, which is often unwanted.
  976. // also, this can cause infinite height of stroke, see #546
  977. });
  978. //if (arpeggio.notes.length === vexFlowVoiceEntry.notes.length) { // different workaround for endless y bug
  979. if (this.rules.RenderArpeggios) {
  980. vexFlowVoiceEntry.vfStaveNote.addStroke(0, stroke);
  981. }
  982. } else {
  983. log.debug(`[OSMD] arpeggio in measure ${this.MeasureNumber} could not be drawn.
  984. voice entry had less than two notes, arpeggio is likely between voice entries, not currently supported in Vexflow.`);
  985. // TODO: create new arpeggio with all the arpeggio's notes (arpeggio.notes), perhaps with GhostNotes in a new vfStaveNote. not easy.
  986. }
  987. }
  988. this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
  989. }
  990. }
  991. this.createArticulations();
  992. this.createOrnaments();
  993. this.setStemDirectionFromVexFlow();
  994. }
  995. /**
  996. * Copy the stem directions chosen by VexFlow to the StemDirection variable of the graphical notes
  997. */
  998. private setStemDirectionFromVexFlow(): void {
  999. //if StemDirection was not set then read out what VexFlow has chosen
  1000. for ( const vfStaffEntry of this.staffEntries ) {
  1001. for ( const gVoiceEntry of vfStaffEntry.graphicalVoiceEntries) {
  1002. for ( const gnote of gVoiceEntry.notes) {
  1003. const vfnote: [StaveNote, number] = (gnote as VexFlowGraphicalNote).vfnote;
  1004. if (vfnote === undefined || vfnote[0] === undefined) {
  1005. continue;
  1006. }
  1007. const vfStemDir: number = vfnote[0].getStemDirection();
  1008. switch (vfStemDir) {
  1009. case (Vex.Flow.Stem.UP):
  1010. gVoiceEntry.parentVoiceEntry.StemDirection = StemDirectionType.Up;
  1011. break;
  1012. case (Vex.Flow.Stem.DOWN):
  1013. gVoiceEntry.parentVoiceEntry.StemDirection = StemDirectionType.Down;
  1014. break;
  1015. default:
  1016. }
  1017. }
  1018. }
  1019. }
  1020. }
  1021. /**
  1022. * Create the articulations for all notes of the current staff entry
  1023. */
  1024. protected createArticulations(): void {
  1025. for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
  1026. const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
  1027. // create vex flow articulation:
  1028. const graphicalVoiceEntries: GraphicalVoiceEntry[] = graphicalStaffEntry.graphicalVoiceEntries;
  1029. for (const gve of graphicalVoiceEntries) {
  1030. const vfStaveNote: StemmableNote = (gve as VexFlowVoiceEntry).vfStaveNote;
  1031. VexFlowConverter.generateArticulations(vfStaveNote, gve.notes[0].sourceNote.ParentVoiceEntry.Articulations);
  1032. }
  1033. }
  1034. }
  1035. /**
  1036. * Create the ornaments for all notes of the current staff entry
  1037. */
  1038. protected createOrnaments(): void {
  1039. for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
  1040. const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
  1041. const gvoices: { [voiceID: number]: GraphicalVoiceEntry; } = graphicalStaffEntry.graphicalVoiceEntries;
  1042. for (const voiceID in gvoices) {
  1043. if (gvoices.hasOwnProperty(voiceID)) {
  1044. const vfStaveNote: StemmableNote = (gvoices[voiceID] as VexFlowVoiceEntry).vfStaveNote;
  1045. const ornamentContainer: OrnamentContainer = gvoices[voiceID].notes[0].sourceNote.ParentVoiceEntry.OrnamentContainer;
  1046. if (ornamentContainer !== undefined) {
  1047. VexFlowConverter.generateOrnaments(vfStaveNote, ornamentContainer);
  1048. }
  1049. }
  1050. }
  1051. }
  1052. }
  1053. protected createFingerings(voiceEntry: GraphicalVoiceEntry): void {
  1054. const vexFlowVoiceEntry: VexFlowVoiceEntry = voiceEntry as VexFlowVoiceEntry;
  1055. const technicalInstructions: TechnicalInstruction[] = voiceEntry.parentVoiceEntry.TechnicalInstructions;
  1056. let fingeringsCount: number = 0;
  1057. for (const instruction of technicalInstructions) {
  1058. if (instruction.type === TechnicalInstructionType.Fingering) {
  1059. fingeringsCount++;
  1060. }
  1061. }
  1062. let fingeringIndex: number = -1;
  1063. for (const fingeringInstruction of technicalInstructions) {
  1064. if (fingeringInstruction.type !== TechnicalInstructionType.Fingering) {
  1065. continue;
  1066. }
  1067. fingeringIndex++; // 0 for first fingering
  1068. let fingeringPosition: PlacementEnum = this.rules.FingeringPosition;
  1069. if (fingeringInstruction.placement !== PlacementEnum.NotYetDefined) {
  1070. fingeringPosition = fingeringInstruction.placement;
  1071. }
  1072. let modifierPosition: any; // Vex.Flow.Stavemodifier.Position
  1073. switch (fingeringPosition) {
  1074. default:
  1075. case PlacementEnum.Left:
  1076. modifierPosition = Vex.Flow.StaveModifier.Position.LEFT;
  1077. break;
  1078. case PlacementEnum.Right:
  1079. modifierPosition = Vex.Flow.StaveModifier.Position.RIGHT;
  1080. break;
  1081. case PlacementEnum.Above:
  1082. modifierPosition = Vex.Flow.StaveModifier.Position.ABOVE;
  1083. break;
  1084. case PlacementEnum.Below:
  1085. modifierPosition = Vex.Flow.StaveModifier.Position.BELOW;
  1086. break;
  1087. case PlacementEnum.NotYetDefined: // automatic fingering placement, could be more complex/customizable
  1088. const sourceStaff: Staff = voiceEntry.parentStaffEntry.sourceStaffEntry.ParentStaff;
  1089. if (voiceEntry.notes.length > 1 || voiceEntry.parentStaffEntry.graphicalVoiceEntries.length > 1) {
  1090. modifierPosition = Vex.Flow.StaveModifier.Position.LEFT;
  1091. } else if (sourceStaff.idInMusicSheet === 0) {
  1092. modifierPosition = Vex.Flow.StaveModifier.Position.ABOVE;
  1093. fingeringPosition = PlacementEnum.Above;
  1094. } else {
  1095. modifierPosition = Vex.Flow.StaveModifier.Position.BELOW;
  1096. fingeringPosition = PlacementEnum.Below;
  1097. }
  1098. }
  1099. const fretFinger: Vex.Flow.FretHandFinger = new Vex.Flow.FretHandFinger(fingeringInstruction.value);
  1100. fretFinger.setPosition(modifierPosition);
  1101. if (fingeringPosition === PlacementEnum.Above || fingeringPosition === PlacementEnum.Below) {
  1102. const offsetYSign: number = fingeringPosition === PlacementEnum.Above ? -1 : 1; // minus y is up
  1103. const ordering: number = fingeringPosition === PlacementEnum.Above ? fingeringIndex :
  1104. fingeringsCount - 1 - fingeringIndex; // reverse order for fingerings below staff
  1105. if (this.rules.FingeringInsideStafflines && fingeringsCount > 1) { // y-shift for single fingering is ok
  1106. // experimental, bounding boxes wrong for fretFinger above/below, better would be creating Labels
  1107. // set y-shift. vexflow fretfinger simply places directly above/below note
  1108. const perFingeringShift: number = fretFinger.getWidth() / 2;
  1109. const shiftCount: number = fingeringsCount * 2.5;
  1110. (<any>fretFinger).setOffsetY(offsetYSign * (ordering + shiftCount) * perFingeringShift);
  1111. } else if (!this.rules.FingeringInsideStafflines) { // use StringNumber for placement above/below stafflines
  1112. const stringNumber: Vex.Flow.StringNumber = new Vex.Flow.StringNumber(fingeringInstruction.value);
  1113. (<any>stringNumber).radius = 0; // hack to remove the circle around the number
  1114. stringNumber.setPosition(modifierPosition);
  1115. stringNumber.setOffsetY(offsetYSign * ordering * stringNumber.getWidth() * 2 / 3);
  1116. // Vexflow made a mess with the addModifier signature that changes through each class so we just cast to any :(
  1117. vexFlowVoiceEntry.vfStaveNote.addModifier((fingeringIndex as any), (stringNumber as any));
  1118. continue;
  1119. }
  1120. }
  1121. if (vexFlowVoiceEntry.vfStaveNote.getCategory() === "tabnotes") {
  1122. // TODO this doesn't work yet. don't add fingering for tabs for now.
  1123. // vexFlowVoiceEntry.vfStaveNote.addModifier(fretFinger, fingeringIndex);
  1124. } else {
  1125. // Vexflow made a mess with the addModifier signature that changes through each class so we just cast to any :(
  1126. vexFlowVoiceEntry.vfStaveNote.addModifier((fingeringIndex as any), (fretFinger as any));
  1127. }
  1128. }
  1129. }
  1130. /**
  1131. * Creates a line from 'top' to this measure, of type 'lineType'
  1132. * @param top
  1133. * @param lineType
  1134. */
  1135. public lineTo(top: VexFlowMeasure, lineType: any): void {
  1136. const connector: StaveConnector = new Vex.Flow.StaveConnector(top.getVFStave(), this.stave);
  1137. connector.setType(lineType);
  1138. this.connectors.push(connector);
  1139. }
  1140. /**
  1141. * Return the VexFlow Stave corresponding to this graphicalMeasure
  1142. * @returns {Vex.Flow.Stave}
  1143. */
  1144. public getVFStave(): Vex.Flow.Stave {
  1145. return this.stave;
  1146. }
  1147. /**
  1148. * After re-running the formatting on the VexFlow Stave, update the
  1149. * space needed by Instructions (in VexFlow: StaveModifiers)
  1150. */
  1151. protected updateInstructionWidth(): void {
  1152. let vfBeginInstructionsWidth: number = 0;
  1153. let vfEndInstructionsWidth: number = 0;
  1154. const modifiers: Vex.Flow.StaveModifier[] = this.stave.getModifiers();
  1155. for (const mod of modifiers) {
  1156. if (mod.getPosition() === StavePositionEnum.BEGIN) { //Vex.Flow.StaveModifier.Position.BEGIN) {
  1157. vfBeginInstructionsWidth += mod.getWidth() + mod.getPadding(undefined);
  1158. } else if (mod.getPosition() === StavePositionEnum.END) { //Vex.Flow.StaveModifier.Position.END) {
  1159. vfEndInstructionsWidth += mod.getWidth() + mod.getPadding(undefined);
  1160. }
  1161. }
  1162. this.beginInstructionsWidth = vfBeginInstructionsWidth / unitInPixels;
  1163. this.endInstructionsWidth = vfEndInstructionsWidth / unitInPixels;
  1164. }
  1165. }
  1166. // Gives the position of the Stave - replaces the function get Position() in the description of class StaveModifier in vexflow.d.ts
  1167. // The latter gave an error because function cannot be defined in the class descriptions in vexflow.d.ts
  1168. export enum StavePositionEnum {
  1169. LEFT = 1,
  1170. RIGHT = 2,
  1171. ABOVE = 3,
  1172. BELOW = 4,
  1173. BEGIN = 5,
  1174. END = 6
  1175. }