VexFlowConverter.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. import Vex = require("vexflow");
  2. import {ClefEnum} from "../../VoiceData/Instructions/ClefInstruction";
  3. import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
  4. import {Pitch} from "../../../Common/DataObjects/Pitch";
  5. import {Fraction} from "../../../Common/DataObjects/Fraction";
  6. import {RhythmInstruction} from "../../VoiceData/Instructions/RhythmInstruction";
  7. import {RhythmSymbolEnum} from "../../VoiceData/Instructions/RhythmInstruction";
  8. import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
  9. import {KeyEnum} from "../../VoiceData/Instructions/KeyInstruction";
  10. import {AccidentalEnum} from "../../../Common/DataObjects/Pitch";
  11. import {NoteEnum} from "../../../Common/DataObjects/Pitch";
  12. import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
  13. import {GraphicalNote} from "../GraphicalNote";
  14. import {SystemLinesEnum} from "../SystemLinesEnum";
  15. import {FontStyles} from "../../../Common/Enums/FontStyles";
  16. import {Fonts} from "../../../Common/Enums/Fonts";
  17. import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEnums";
  18. import * as log from "loglevel";
  19. import { ArticulationEnum, StemDirectionType } from "../../VoiceData/VoiceEntry";
  20. import { SystemLinePosition } from "../SystemLinePosition";
  21. import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
  22. import { OrnamentEnum, OrnamentContainer } from "../../VoiceData/OrnamentContainer";
  23. import { Notehead, NoteHeadShape } from "../../VoiceData/Notehead";
  24. import { unitInPixels } from "./VexFlowMusicSheetDrawer";
  25. import { EngravingRules } from "../EngravingRules";
  26. /**
  27. * Helper class, which contains static methods which actually convert
  28. * from OSMD objects to VexFlow objects.
  29. */
  30. export class VexFlowConverter {
  31. /**
  32. * Mapping from numbers of alterations on the key signature to major keys
  33. * @type {[alterationsNo: number]: string; }
  34. */
  35. private static majorMap: {[_: number]: string; } = {
  36. "-1": "F", "-2": "Bb", "-3": "Eb", "-4": "Ab", "-5": "Db", "-6": "Gb", "-7": "Cb", "-8": "Fb",
  37. "0": "C", "1": "G", "2": "D", "3": "A", "4": "E", "5": "B", "6": "F#", "7": "C#", "8": "G#"
  38. };
  39. /**
  40. * Mapping from numbers of alterations on the key signature to minor keys
  41. * @type {[alterationsNo: number]: string; }
  42. */
  43. private static minorMap: {[_: number]: string; } = {
  44. "-1": "D", "-2": "G", "-3": "C", "-4": "F", "-5": "Bb", "-6": "Eb", "-7": "Ab", "-8": "Db",
  45. "0": "A", "1": "E", "2": "B", "3": "F#", "4": "C#", "5": "G#", "6": "D#", "7": "A#", "8": "E#"
  46. };
  47. /**
  48. * Convert a fraction to a string which represents a duration in VexFlow
  49. * @param fraction a fraction representing the duration of a note
  50. * @returns {string}
  51. */
  52. public static duration(fraction: Fraction, isTuplet: boolean): string {
  53. const dur: number = fraction.RealValue;
  54. if (dur >= 1) {
  55. return "w";
  56. } else if (dur < 1 && dur >= 0.5) {
  57. // change to the next higher straight note to get the correct note display type
  58. if (isTuplet) {
  59. return "w";
  60. }
  61. return "h";
  62. } else if (dur < 0.5 && dur >= 0.25) {
  63. // change to the next higher straight note to get the correct note display type
  64. if (isTuplet && dur > 0.25) {
  65. return "h";
  66. }
  67. return "q";
  68. } else if (dur < 0.25 && dur >= 0.125) {
  69. // change to the next higher straight note to get the correct note display type
  70. if (isTuplet && dur > 0.125) {
  71. return "q";
  72. }
  73. return "8";
  74. } else if (dur < 0.125 && dur >= 0.0625) {
  75. // change to the next higher straight note to get the correct note display type
  76. if (isTuplet && dur > 0.0625) {
  77. return "8";
  78. }
  79. return "16";
  80. } else if (dur < 0.0625 && dur >= 0.03125) {
  81. // change to the next higher straight note to get the correct note display type
  82. if (isTuplet && dur > 0.03125) {
  83. return "16";
  84. }
  85. return "32";
  86. } else if (dur < 0.03125 && dur >= 0.015625) {
  87. // change to the next higher straight note to get the correct note display type
  88. if (isTuplet && dur > 0.015625) {
  89. return "32";
  90. }
  91. return "64";
  92. }
  93. if (isTuplet) {
  94. return "64";
  95. }
  96. return "128";
  97. }
  98. /**
  99. * Takes a Pitch and returns a string representing a VexFlow pitch,
  100. * which has the form "b/4", plus its alteration (accidental)
  101. * @param pitch
  102. * @returns {string[]}
  103. */
  104. public static pitch(note: VexFlowGraphicalNote, pitch: Pitch): [string, string, ClefInstruction] {
  105. const fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
  106. const acc: string = Pitch.accidentalVexflow(pitch.Accidental);
  107. // The octave seems to need a shift of three FIXME?
  108. const octave: number = pitch.Octave - note.Clef().OctaveOffset + 3;
  109. const notehead: Notehead = note.sourceNote.NoteHead;
  110. let noteheadCode: string = "";
  111. if (notehead !== undefined) {
  112. noteheadCode = this.NoteHeadCode(notehead);
  113. }
  114. return [fund + "n/" + octave + noteheadCode, acc, note.Clef()];
  115. }
  116. /** returns the Vexflow code for a note head. Some are still unsupported, see Vexflow/tables.js */
  117. public static NoteHeadCode(notehead: Notehead): string {
  118. const codeStart: string = "/";
  119. const codeFilled: string = notehead.Filled ? "2" : "1"; // filled/unfilled notehead code in most vexflow glyphs
  120. switch (notehead.Shape) {
  121. case NoteHeadShape.NORMAL:
  122. return "";
  123. case NoteHeadShape.DIAMOND:
  124. return codeStart + "D" + codeFilled;
  125. case NoteHeadShape.TRIANGLE:
  126. return codeStart + "T" + codeFilled;
  127. case NoteHeadShape.X:
  128. return codeStart + "X" + codeFilled;
  129. case NoteHeadShape.CIRCLEX:
  130. return codeStart + "X3";
  131. case NoteHeadShape.RECTANGLE:
  132. return codeStart + "R" + codeFilled;
  133. case NoteHeadShape.SQUARE:
  134. return codeStart + "S" + codeFilled;
  135. case NoteHeadShape.SLASH:
  136. return ""; // slash is specified at end of duration string in Vexflow
  137. default:
  138. return "";
  139. }
  140. }
  141. public static GhostNote(frac: Fraction): Vex.Flow.GhostNote {
  142. return new Vex.Flow.GhostNote({
  143. duration: VexFlowConverter.duration(frac, false),
  144. });
  145. }
  146. /**
  147. * Convert a GraphicalVoiceEntry to a VexFlow StaveNote
  148. * @param gve the GraphicalVoiceEntry which can hold a note or a chord on the staff belonging to one voice
  149. * @returns {Vex.Flow.StaveNote}
  150. */
  151. public static StaveNote(gve: GraphicalVoiceEntry): Vex.Flow.StaveNote {
  152. // sort notes
  153. /* seems unnecessary for now
  154. if (gve.octaveShiftValue !== undefined && gve.octaveShiftValue !== OctaveEnum.NONE) {
  155. gve.sort(); // gves with accidentals in octave shift brackets can be unsorted
  156. } */
  157. // VexFlow needs the notes ordered vertically in the other direction:
  158. const notes: GraphicalNote[] = gve.notes.reverse();
  159. const baseNote: GraphicalNote = notes[0];
  160. let keys: string[] = [];
  161. const accidentals: string[] = [];
  162. const frac: Fraction = baseNote.graphicalNoteLength;
  163. const isTuplet: boolean = baseNote.sourceNote.NoteTuplet !== undefined;
  164. let duration: string = VexFlowConverter.duration(frac, isTuplet);
  165. let vfClefType: string = undefined;
  166. let numDots: number = baseNote.numberOfDots;
  167. let alignCenter: boolean = false;
  168. let xShift: number = 0;
  169. let slashNoteHead: boolean = false;
  170. for (const note of notes) {
  171. if (numDots < note.numberOfDots) {
  172. numDots = note.numberOfDots;
  173. }
  174. // if it is a rest:
  175. if (note.sourceNote.isRest()) {
  176. keys = ["b/4"];
  177. // if it is a full measure rest:
  178. if (note.parentVoiceEntry.parentStaffEntry.parentMeasure.parentSourceMeasure.Duration.RealValue <= frac.RealValue) {
  179. keys = ["d/5"];
  180. duration = "w";
  181. numDots = 0;
  182. // If it's a whole rest we want it smack in the middle. Apparently there is still an issue in vexflow:
  183. // https://github.com/0xfe/vexflow/issues/579 The author reports that he needs to add some negative x shift
  184. // if the measure has no modifiers.
  185. alignCenter = true;
  186. xShift = EngravingRules.Rules.WholeRestXShiftVexflow * unitInPixels; // TODO find way to make dependent on the modifiers
  187. // affects VexFlowStaffEntry.calculateXPosition()
  188. }
  189. duration += "r";
  190. break;
  191. }
  192. if (note.sourceNote.NoteHead) {
  193. if (note.sourceNote.NoteHead.Shape === NoteHeadShape.SLASH) {
  194. slashNoteHead = true;
  195. // if we have slash heads and other heads in the voice entry, this will create the same head for all.
  196. // same problem with numDots. The slash case should be extremely rare though.
  197. }
  198. }
  199. const pitch: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
  200. keys.push(pitch[0]);
  201. accidentals.push(pitch[1]);
  202. if (!vfClefType) {
  203. const vfClef: {type: string, annotation: string} = VexFlowConverter.Clef(pitch[2]);
  204. vfClefType = vfClef.type;
  205. }
  206. }
  207. for (let i: number = 0, len: number = numDots; i < len; ++i) {
  208. duration += "d";
  209. }
  210. if (slashNoteHead) {
  211. duration += "s"; // we have to specify a slash note head like this in Vexflow
  212. }
  213. let vfnote: Vex.Flow.StaveNote;
  214. const vfnoteStruct: Object = {
  215. align_center: alignCenter,
  216. auto_stem: true,
  217. clef: vfClefType,
  218. duration: duration,
  219. keys: keys,
  220. slash: gve.parentVoiceEntry.GraceNoteSlash,
  221. };
  222. if (gve.notes[0].sourceNote.IsCueNote) {
  223. (<any>vfnoteStruct).glyph_font_scale = Vex.Flow.DEFAULT_NOTATION_FONT_SCALE * Vex.Flow.GraceNote.SCALE;
  224. (<any>vfnoteStruct).stroke_px = Vex.Flow.GraceNote.LEDGER_LINE_OFFSET;
  225. }
  226. if (gve.parentVoiceEntry.IsGrace || gve.notes[0].sourceNote.IsCueNote) {
  227. vfnote = new Vex.Flow.GraceNote(vfnoteStruct);
  228. } else {
  229. vfnote = new Vex.Flow.StaveNote(vfnoteStruct);
  230. }
  231. if (EngravingRules.Rules.ColoringEnabled) {
  232. const defaultColorStem: string = EngravingRules.Rules.DefaultColorStem;
  233. let stemColor: string = gve.parentVoiceEntry.StemColorXml;
  234. if (!stemColor && defaultColorStem) {
  235. stemColor = defaultColorStem;
  236. }
  237. const stemStyle: Object = { fillStyle: stemColor, strokeStyle: stemColor };
  238. if (stemColor) {
  239. gve.parentVoiceEntry.StemColor = stemColor;
  240. vfnote.setStemStyle(stemStyle);
  241. if (vfnote.flag && EngravingRules.Rules.ColorFlags) {
  242. vfnote.setFlagStyle(stemStyle);
  243. }
  244. }
  245. }
  246. vfnote.x_shift = xShift;
  247. if (gve.parentVoiceEntry.IsGrace && gve.notes[0].sourceNote.NoteBeam) {
  248. // Vexflow seems to have issues with wanted stem direction for beamed grace notes,
  249. // when the stem is connected to a beamed main note (e.g. Haydn Concertante bar 57)
  250. gve.parentVoiceEntry.WantedStemDirection = gve.notes[0].sourceNote.NoteBeam.Notes[0].ParentVoiceEntry.WantedStemDirection;
  251. }
  252. if (gve.parentVoiceEntry !== undefined) {
  253. const wantedStemDirection: StemDirectionType = gve.parentVoiceEntry.WantedStemDirection;
  254. switch (wantedStemDirection) {
  255. case(StemDirectionType.Up):
  256. vfnote.setStemDirection(Vex.Flow.Stem.UP);
  257. break;
  258. case (StemDirectionType.Down):
  259. vfnote.setStemDirection(Vex.Flow.Stem.DOWN);
  260. break;
  261. default:
  262. }
  263. }
  264. // add accidentals
  265. for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
  266. (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
  267. if (accidentals[i]) {
  268. if (accidentals[i] === "++") { // triple sharp
  269. vfnote.addAccidental(i, new Vex.Flow.Accidental("##"));
  270. vfnote.addAccidental(i, new Vex.Flow.Accidental("#"));
  271. continue;
  272. } else if (accidentals[i] === "bbs") { // triple flat
  273. vfnote.addAccidental(i, new Vex.Flow.Accidental("bb"));
  274. vfnote.addAccidental(i, new Vex.Flow.Accidental("b"));
  275. continue;
  276. }
  277. vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i])); // normal accidental
  278. }
  279. }
  280. for (let i: number = 0, len: number = numDots; i < len; ++i) {
  281. vfnote.addDotToAll();
  282. }
  283. return vfnote;
  284. }
  285. public static generateArticulations(vfnote: Vex.Flow.StemmableNote, articulations: ArticulationEnum[]): void {
  286. if (vfnote === undefined || vfnote.getAttribute("type") === "GhostNote") {
  287. return;
  288. }
  289. // Articulations:
  290. let vfArtPosition: number = Vex.Flow.Modifier.Position.ABOVE;
  291. if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
  292. vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
  293. }
  294. for (const articulation of articulations) {
  295. // tslint:disable-next-line:switch-default
  296. let vfArt: Vex.Flow.Articulation = undefined;
  297. switch (articulation) {
  298. case ArticulationEnum.accent: {
  299. vfArt = new Vex.Flow.Articulation("a>");
  300. break;
  301. }
  302. case ArticulationEnum.downbow: {
  303. vfArt = new Vex.Flow.Articulation("am");
  304. break;
  305. }
  306. case ArticulationEnum.fermata: {
  307. vfArt = new Vex.Flow.Articulation("a@a");
  308. vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
  309. break;
  310. }
  311. case ArticulationEnum.invertedfermata: {
  312. vfArt = new Vex.Flow.Articulation("a@u");
  313. vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
  314. break;
  315. }
  316. case ArticulationEnum.lefthandpizzicato: {
  317. vfArt = new Vex.Flow.Articulation("a+");
  318. break;
  319. }
  320. case ArticulationEnum.snappizzicato: {
  321. vfArt = new Vex.Flow.Articulation("ao");
  322. break;
  323. }
  324. case ArticulationEnum.staccatissimo: {
  325. vfArt = new Vex.Flow.Articulation("av");
  326. break;
  327. }
  328. case ArticulationEnum.staccato: {
  329. vfArt = new Vex.Flow.Articulation("a.");
  330. break;
  331. }
  332. case ArticulationEnum.tenuto: {
  333. vfArt = new Vex.Flow.Articulation("a-");
  334. break;
  335. }
  336. case ArticulationEnum.upbow: {
  337. vfArt = new Vex.Flow.Articulation("a|");
  338. break;
  339. }
  340. case ArticulationEnum.strongaccent: {
  341. vfArt = new Vex.Flow.Articulation("a^");
  342. break;
  343. }
  344. default: {
  345. break;
  346. }
  347. }
  348. if (vfArt !== undefined) {
  349. vfArt.setPosition(vfArtPosition);
  350. vfnote.addModifier(0, vfArt);
  351. }
  352. }
  353. }
  354. public static generateOrnaments(vfnote: Vex.Flow.StemmableNote, oContainer: OrnamentContainer): void {
  355. let vfPosition: number = Vex.Flow.Modifier.Position.ABOVE;
  356. if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
  357. vfPosition = Vex.Flow.Modifier.Position.BELOW;
  358. }
  359. let vfOrna: Vex.Flow.Ornament = undefined;
  360. switch (oContainer.GetOrnament) {
  361. case OrnamentEnum.DelayedInvertedTurn: {
  362. vfOrna = new Vex.Flow.Ornament("turn_inverted");
  363. vfOrna.setDelayed(true);
  364. break;
  365. }
  366. case OrnamentEnum.DelayedTurn: {
  367. vfOrna = new Vex.Flow.Ornament("turn");
  368. vfOrna.setDelayed(true);
  369. break;
  370. }
  371. case OrnamentEnum.InvertedMordent: {
  372. vfOrna = new Vex.Flow.Ornament("mordent_inverted");
  373. vfOrna.setDelayed(false);
  374. break;
  375. }
  376. case OrnamentEnum.InvertedTurn: {
  377. vfOrna = new Vex.Flow.Ornament("turn_inverted");
  378. vfOrna.setDelayed(false);
  379. break;
  380. }
  381. case OrnamentEnum.Mordent: {
  382. vfOrna = new Vex.Flow.Ornament("mordent");
  383. vfOrna.setDelayed(false);
  384. break;
  385. }
  386. case OrnamentEnum.Trill: {
  387. vfOrna = new Vex.Flow.Ornament("tr");
  388. vfOrna.setDelayed(false);
  389. break;
  390. }
  391. case OrnamentEnum.Turn: {
  392. vfOrna = new Vex.Flow.Ornament("turn");
  393. vfOrna.setDelayed(false);
  394. break;
  395. }
  396. default: {
  397. log.warn("unhandled OrnamentEnum type: " + oContainer.GetOrnament);
  398. return;
  399. }
  400. }
  401. if (vfOrna !== undefined) {
  402. if (oContainer.AccidentalBelow !== AccidentalEnum.NONE) {
  403. vfOrna.setLowerAccidental(Pitch.accidentalVexflow(oContainer.AccidentalBelow));
  404. }
  405. if (oContainer.AccidentalAbove !== AccidentalEnum.NONE) {
  406. vfOrna.setUpperAccidental(Pitch.accidentalVexflow(oContainer.AccidentalAbove));
  407. }
  408. vfOrna.setPosition(vfPosition);
  409. vfnote.addModifier(0, vfOrna);
  410. }
  411. }
  412. /**
  413. * Convert a ClefInstruction to a string represention of a clef type in VexFlow.
  414. *
  415. * @param clef The OSMD object to be converted representing the clef
  416. * @param size The VexFlow size to be used. Can be `default` or `small`. As soon as
  417. * #118 is done, this parameter will be dispensable.
  418. * @returns A string representation of a VexFlow clef
  419. * @see https://github.com/0xfe/vexflow/blob/master/src/clef.js
  420. * @see https://github.com/0xfe/vexflow/blob/master/tests/clef_tests.js
  421. */
  422. public static Clef(clef: ClefInstruction, size: string = "default"): { type: string, size: string, annotation: string } {
  423. let type: string;
  424. let annotation: string;
  425. // Make sure size is either "default" or "small"
  426. if (size !== "default" && size !== "small") {
  427. log.warn(`Invalid VexFlow clef size "${size}" specified. Using "default".`);
  428. size = "default";
  429. }
  430. /*
  431. * For all of the following conversions, OSMD uses line numbers 1-5 starting from
  432. * the bottom, while VexFlow uses 0-4 starting from the top.
  433. */
  434. switch (clef.ClefType) {
  435. // G Clef
  436. case ClefEnum.G:
  437. switch (clef.Line) {
  438. case 1:
  439. type = "french"; // VexFlow line 4
  440. break;
  441. case 2:
  442. type = "treble"; // VexFlow line 3
  443. break;
  444. default:
  445. type = "treble";
  446. log.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
  447. }
  448. break;
  449. // F Clef
  450. case ClefEnum.F:
  451. switch (clef.Line) {
  452. case 4:
  453. type = "bass"; // VexFlow line 1
  454. break;
  455. case 3:
  456. type = "baritone-f"; // VexFlow line 2
  457. break;
  458. case 5:
  459. type = "subbass"; // VexFlow line 0
  460. break;
  461. default:
  462. type = "bass";
  463. log.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
  464. }
  465. break;
  466. // C Clef
  467. case ClefEnum.C:
  468. switch (clef.Line) {
  469. case 3:
  470. type = "alto"; // VexFlow line 2
  471. break;
  472. case 4:
  473. type = "tenor"; // VexFlow line 1
  474. break;
  475. case 1:
  476. type = "soprano"; // VexFlow line 4
  477. break;
  478. case 2:
  479. type = "mezzo-soprano"; // VexFlow line 3
  480. break;
  481. default:
  482. type = "alto";
  483. log.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
  484. }
  485. break;
  486. // Percussion Clef
  487. case ClefEnum.percussion:
  488. type = "percussion";
  489. break;
  490. // TAB Clef
  491. case ClefEnum.TAB:
  492. type = "tab";
  493. break;
  494. default:
  495. }
  496. // annotations in vexflow don't allow bass and 8va. No matter the offset :(
  497. if (clef.OctaveOffset === 1 && type !== "bass" ) {
  498. annotation = "8va";
  499. } else if (clef.OctaveOffset === -1) {
  500. annotation = "8vb";
  501. }
  502. return { type, size, annotation };
  503. }
  504. /**
  505. * Convert a RhythmInstruction to a VexFlow TimeSignature object
  506. * @param rhythm
  507. * @returns {Vex.Flow.TimeSignature}
  508. * @constructor
  509. */
  510. public static TimeSignature(rhythm: RhythmInstruction): Vex.Flow.TimeSignature {
  511. let timeSpec: string;
  512. switch (rhythm.SymbolEnum) {
  513. case RhythmSymbolEnum.NONE:
  514. timeSpec = rhythm.Rhythm.Numerator + "/" + rhythm.Rhythm.Denominator;
  515. break;
  516. case RhythmSymbolEnum.COMMON:
  517. timeSpec = "C";
  518. break;
  519. case RhythmSymbolEnum.CUT:
  520. timeSpec = "C|";
  521. break;
  522. default:
  523. }
  524. return new Vex.Flow.TimeSignature(timeSpec);
  525. }
  526. /**
  527. * Convert a KeyInstruction to a string representing in VexFlow a key
  528. * @param key
  529. * @returns {string}
  530. */
  531. public static keySignature(key: KeyInstruction): string {
  532. if (key === undefined) {
  533. return undefined;
  534. }
  535. let ret: string;
  536. switch (key.Mode) {
  537. case KeyEnum.minor:
  538. ret = VexFlowConverter.minorMap[key.Key] + "m";
  539. break;
  540. case KeyEnum.major:
  541. ret = VexFlowConverter.majorMap[key.Key];
  542. break;
  543. // some XMLs don't have the mode set despite having a key signature.
  544. case KeyEnum.none:
  545. ret = VexFlowConverter.majorMap[key.Key];
  546. break;
  547. default:
  548. ret = "C";
  549. }
  550. return ret;
  551. }
  552. /**
  553. * Converts a lineType to a VexFlow StaveConnector type
  554. * @param lineType
  555. * @returns {any}
  556. */
  557. public static line(lineType: SystemLinesEnum, linePosition: SystemLinePosition): any {
  558. switch (lineType) {
  559. case SystemLinesEnum.SingleThin:
  560. if (linePosition === SystemLinePosition.MeasureBegin) {
  561. return Vex.Flow.StaveConnector.type.SINGLE;
  562. }
  563. return Vex.Flow.StaveConnector.type.SINGLE_RIGHT;
  564. case SystemLinesEnum.DoubleThin:
  565. return Vex.Flow.StaveConnector.type.DOUBLE;
  566. case SystemLinesEnum.ThinBold:
  567. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
  568. case SystemLinesEnum.BoldThinDots:
  569. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_LEFT;
  570. case SystemLinesEnum.DotsThinBold:
  571. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
  572. case SystemLinesEnum.DotsBoldBoldDots:
  573. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
  574. case SystemLinesEnum.None:
  575. return Vex.Flow.StaveConnector.type.NONE;
  576. default:
  577. }
  578. }
  579. /**
  580. * Construct a string which can be used in a CSS font property
  581. * @param fontSize
  582. * @param fontStyle
  583. * @param font
  584. * @returns {string}
  585. */
  586. public static font(fontSize: number, fontStyle: FontStyles = FontStyles.Regular, font: Fonts = Fonts.TimesNewRoman): string {
  587. let style: string = "normal";
  588. let weight: string = "normal";
  589. const family: string = "'Times New Roman'";
  590. switch (fontStyle) {
  591. case FontStyles.Bold:
  592. weight = "bold";
  593. break;
  594. case FontStyles.Italic:
  595. style = "italic";
  596. break;
  597. case FontStyles.BoldItalic:
  598. style = "italic";
  599. weight = "bold";
  600. break;
  601. case FontStyles.Underlined:
  602. // TODO
  603. break;
  604. default:
  605. break;
  606. }
  607. switch (font) {
  608. case Fonts.Kokila:
  609. // TODO Not Supported
  610. break;
  611. default:
  612. }
  613. return style + " " + weight + " " + Math.floor(fontSize) + "px " + family;
  614. }
  615. /**
  616. * Converts the style into a string that VexFlow RenderContext can understand
  617. * as the weight of the font
  618. */
  619. public static fontStyle(style: FontStyles): string {
  620. switch (style) {
  621. case FontStyles.Bold:
  622. return "bold";
  623. case FontStyles.Italic:
  624. return "italic";
  625. case FontStyles.BoldItalic:
  626. return "italic bold";
  627. default:
  628. return "normal";
  629. }
  630. }
  631. /**
  632. * Convert OutlineAndFillStyle to CSS properties
  633. * @param styleId
  634. * @returns {string}
  635. */
  636. public static style(styleId: OutlineAndFillStyleEnum): string {
  637. const ret: string = OUTLINE_AND_FILL_STYLE_DICT.getValue(styleId);
  638. return ret;
  639. }
  640. }
  641. export enum VexFlowRepetitionType {
  642. NONE = 1, // no coda or segno
  643. CODA_LEFT = 2, // coda at beginning of stave
  644. CODA_RIGHT = 3, // coda at end of stave
  645. SEGNO_LEFT = 4, // segno at beginning of stave
  646. SEGNO_RIGHT = 5, // segno at end of stave
  647. DC = 6, // D.C. at end of stave
  648. DC_AL_CODA = 7, // D.C. al coda at end of stave
  649. DC_AL_FINE = 8, // D.C. al Fine end of stave
  650. DS = 9, // D.S. at end of stave
  651. DS_AL_CODA = 10, // D.S. al coda at end of stave
  652. DS_AL_FINE = 11, // D.S. al Fine at end of stave
  653. FINE = 12, // Fine at end of stave
  654. }
  655. export enum VexFlowBarlineType {
  656. SINGLE = 1,
  657. DOUBLE = 2,
  658. END = 3,
  659. REPEAT_BEGIN = 4,
  660. REPEAT_END = 5,
  661. REPEAT_BOTH = 6,
  662. NONE = 7,
  663. }