VexFlowConverter.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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} from "../DrawingEnums";
  18. /**
  19. * Helper class, which contains static methods which actually convert
  20. * from OSMD objects to VexFlow objects.
  21. */
  22. export class VexFlowConverter {
  23. /**
  24. * Mapping from numbers of alterations on the key signature to major keys
  25. * @type {[alterationsNo: number]: string; }
  26. */
  27. private static majorMap: {[_: number]: string; } = {
  28. "-1": "F", "-2": "Bb", "-3": "Eb", "-4": "Ab", "-5": "Db", "-6": "Gb", "-7": "Cb", "-8": "Fb",
  29. "0": "C", "1": "G", "2": "D", "3": "A", "4": "E", "5": "B", "6": "F#", "7": "C#", "8": "G#"
  30. };
  31. /**
  32. * Mapping from numbers of alterations on the key signature to minor keys
  33. * @type {[alterationsNo: number]: string; }
  34. */
  35. private static minorMap: {[_: number]: string; } = {
  36. "-1": "D", "-2": "G", "-3": "C", "-4": "F", "-5": "Bb", "-6": "Eb", "-7": "Ab", "-8": "Db",
  37. "0": "A", "1": "E", "2": "B", "3": "F#", "4": "C#", "5": "G#", "6": "D#", "7": "A#", "8": "E#"
  38. };
  39. /**
  40. * Convert a fraction to a string which represents a duration in VexFlow
  41. * @param fraction a fraction representing the duration of a note
  42. * @returns {string}
  43. */
  44. public static duration(fraction: Fraction): string {
  45. let dur: number = fraction.RealValue;
  46. if (dur >= 1) {
  47. return "w";
  48. } else if (dur < 1 && dur >= 0.5) {
  49. return "h";
  50. } else if (dur < 0.5 && dur >= 0.25) {
  51. return "q";
  52. } else if (dur < 0.25 && dur >= 0.125) {
  53. return "8";
  54. } else if (dur < 0.125 && dur >= 0.0625) {
  55. return "16";
  56. } else if (dur < 0.0625 && dur >= 0.03125) {
  57. return "32";
  58. }
  59. return "128";
  60. }
  61. /**
  62. * Takes a Pitch and returns a string representing a VexFlow pitch,
  63. * which has the form "b/4", plus its alteration (accidental)
  64. * @param pitch
  65. * @returns {string[]}
  66. */
  67. public static pitch(pitch: Pitch, clef: ClefInstruction): [string, string, ClefInstruction] {
  68. let fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
  69. // The octave seems to need a shift of three FIXME?
  70. let octave: number = pitch.Octave - clef.OctaveOffset + 3;
  71. let acc: string = VexFlowConverter.accidental(pitch.Accidental);
  72. return [fund + "n/" + octave, acc, clef];
  73. }
  74. /**
  75. * Converts AccidentalEnum to a string which represents an accidental in VexFlow
  76. * @param accidental
  77. * @returns {string}
  78. */
  79. public static accidental(accidental: AccidentalEnum): string {
  80. let acc: string;
  81. switch (accidental) {
  82. case AccidentalEnum.NONE:
  83. acc = "n";
  84. break;
  85. case AccidentalEnum.FLAT:
  86. acc = "b";
  87. break;
  88. case AccidentalEnum.SHARP:
  89. acc = "#";
  90. break;
  91. case AccidentalEnum.DOUBLESHARP:
  92. acc = "##";
  93. break;
  94. case AccidentalEnum.DOUBLEFLAT:
  95. acc = "bb";
  96. break;
  97. default:
  98. }
  99. return acc;
  100. }
  101. /**
  102. * Convert a set of GraphicalNotes to a VexFlow StaveNote
  103. * @param notes form a chord on the staff
  104. * @returns {Vex.Flow.StaveNote}
  105. */
  106. public static StaveNote(notes: GraphicalNote[]): Vex.Flow.StaveNote {
  107. let keys: string[] = [];
  108. let accidentals: string[] = [];
  109. let frac: Fraction = notes[0].graphicalNoteLength;
  110. let duration: string = VexFlowConverter.duration(frac);
  111. let vfClefType: string = undefined;
  112. let numDots: number = 0;
  113. for (let note of notes) {
  114. let res: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
  115. if (res === undefined) {
  116. keys = ["b/4"];
  117. duration += "r";
  118. break;
  119. }
  120. keys.push(res[0]);
  121. accidentals.push(res[1]);
  122. if (!vfClefType) {
  123. let vfClef: {type: string, annotation: string} = VexFlowConverter.Clef(res[2]);
  124. vfClefType = vfClef.type;
  125. }
  126. if (numDots < note.numberOfDots) {
  127. numDots = note.numberOfDots;
  128. }
  129. }
  130. for (let i: number = 0, len: number = numDots; i < len; ++i) {
  131. duration += "d";
  132. }
  133. let vfnote: Vex.Flow.StaveNote = new Vex.Flow.StaveNote({
  134. auto_stem: true,
  135. clef: vfClefType,
  136. duration: duration,
  137. duration_override: {
  138. denominator: frac.Denominator,
  139. numerator: frac.Numerator,
  140. },
  141. keys: keys,
  142. });
  143. for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
  144. (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
  145. if (accidentals[i]) {
  146. vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i]));
  147. }
  148. }
  149. for (let i: number = 0, len: number = numDots; i < len; ++i) {
  150. vfnote.addDotToAll();
  151. }
  152. return vfnote;
  153. }
  154. /**
  155. * Convert a ClefInstruction to a string representing a clef type in VexFlow
  156. * @param clef
  157. * @returns {string}
  158. * @constructor
  159. */
  160. public static Clef(clef: ClefInstruction): {type: string, annotation: string} {
  161. let type: string;
  162. let annotation: string = undefined;
  163. switch (clef.ClefType) {
  164. case ClefEnum.G:
  165. type = "treble";
  166. break;
  167. case ClefEnum.F:
  168. type = "bass";
  169. break;
  170. case ClefEnum.C:
  171. type = "alto";
  172. break;
  173. case ClefEnum.percussion:
  174. type = "percussion";
  175. break;
  176. case ClefEnum.TAB:
  177. type = "tab";
  178. break;
  179. default:
  180. }
  181. switch (clef.OctaveOffset) {
  182. case 1:
  183. annotation = "8va";
  184. break;
  185. case -1:
  186. annotation = "8vb";
  187. break;
  188. default:
  189. }
  190. return {type, annotation};
  191. }
  192. /**
  193. * Convert a RhythmInstruction to a VexFlow TimeSignature object
  194. * @param rhythm
  195. * @returns {Vex.Flow.TimeSignature}
  196. * @constructor
  197. */
  198. public static TimeSignature(rhythm: RhythmInstruction): Vex.Flow.TimeSignature {
  199. let timeSpec: string;
  200. switch (rhythm.SymbolEnum) {
  201. case RhythmSymbolEnum.NONE:
  202. timeSpec = rhythm.Rhythm.Numerator + "/" + rhythm.Rhythm.Denominator;
  203. break;
  204. case RhythmSymbolEnum.COMMON:
  205. timeSpec = "C";
  206. break;
  207. case RhythmSymbolEnum.CUT:
  208. timeSpec = "C|";
  209. break;
  210. default:
  211. }
  212. return new Vex.Flow.TimeSignature(timeSpec);
  213. }
  214. /**
  215. * Convert a KeyInstruction to a string representing in VexFlow a key
  216. * @param key
  217. * @returns {string}
  218. */
  219. public static keySignature(key: KeyInstruction): string {
  220. if (key === undefined) {
  221. return undefined;
  222. }
  223. let ret: string;
  224. switch (key.Mode) {
  225. case KeyEnum.minor:
  226. ret = VexFlowConverter.minorMap[key.Key] + "m";
  227. break;
  228. case KeyEnum.major:
  229. ret = VexFlowConverter.majorMap[key.Key];
  230. break;
  231. case KeyEnum.none:
  232. default:
  233. ret = "C";
  234. }
  235. return ret;
  236. }
  237. /**
  238. * Converts a lineType to a VexFlow StaveConnector type
  239. * @param lineType
  240. * @returns {any}
  241. */
  242. public static line(lineType: SystemLinesEnum): any {
  243. // TODO Not all line types are correctly mapped!
  244. switch (lineType) {
  245. case SystemLinesEnum.SingleThin:
  246. return Vex.Flow.StaveConnector.type.SINGLE;
  247. case SystemLinesEnum.DoubleThin:
  248. return Vex.Flow.StaveConnector.type.DOUBLE;
  249. case SystemLinesEnum.ThinBold:
  250. return Vex.Flow.StaveConnector.type.SINGLE;
  251. case SystemLinesEnum.BoldThinDots:
  252. return Vex.Flow.StaveConnector.type.DOUBLE;
  253. case SystemLinesEnum.DotsThinBold:
  254. return Vex.Flow.StaveConnector.type.DOUBLE;
  255. case SystemLinesEnum.DotsBoldBoldDots:
  256. return Vex.Flow.StaveConnector.type.DOUBLE;
  257. case SystemLinesEnum.None:
  258. return Vex.Flow.StaveConnector.type.NONE;
  259. default:
  260. }
  261. }
  262. /**
  263. * Construct a string which can be used in a CSS font property
  264. * @param fontSize
  265. * @param fontStyle
  266. * @param font
  267. * @returns {string}
  268. */
  269. public static font(fontSize: number, fontStyle: FontStyles = FontStyles.Regular, font: Fonts = Fonts.TimesNewRoman): string {
  270. let style: string = "normal";
  271. let weight: string = "normal";
  272. let family: string = "'Times New Roman'";
  273. switch (fontStyle) {
  274. case FontStyles.Bold:
  275. weight = "bold";
  276. break;
  277. case FontStyles.Italic:
  278. style = "italic";
  279. break;
  280. case FontStyles.BoldItalic:
  281. style = "italic";
  282. weight = "bold";
  283. break;
  284. case FontStyles.Underlined:
  285. // TODO
  286. break;
  287. default:
  288. break;
  289. }
  290. switch (font) {
  291. case Fonts.Kokila:
  292. // TODO Not Supported
  293. break;
  294. default:
  295. }
  296. return style + " " + weight + " " + Math.floor(fontSize) + "px " + family;
  297. }
  298. /**
  299. * Convert OutlineAndFillStyle to CSS properties
  300. * @param styleId
  301. * @returns {string}
  302. */
  303. public static style(styleId: OutlineAndFillStyleEnum): string {
  304. // TODO To be implemented
  305. return "lightgreen";
  306. }
  307. }