VexFlowConverter.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. "0": "C", 1: "G", 2: "D", 3: "A", 4: "E", 5: "B", 6: "F#", 7: "C#",
  29. 8: "G#", "-1": "F", "-8": "Fb", "-7": "Cb", "-6": "Gb", "-5": "Db", "-4": "Ab", "-3": "Eb", "-2": "Bb",
  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": "E", "7": "A#", "0": "A", "6": "D#", "3": "F#", "-5": "Bb", "-4": "F", "-7": "Ab", "-6": "Eb",
  37. "-1": "D", "4": "C#", "-3": "C", "-2": "G", "2": "B", "5": "G#", "-8": "Db", "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 vfclef: string;
  112. for (let note of notes) {
  113. let res: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
  114. if (res === undefined) {
  115. keys = ["b/4"];
  116. duration += "r";
  117. break;
  118. }
  119. keys.push(res[0]);
  120. accidentals.push(res[1]);
  121. if (!vfclef) {
  122. vfclef = VexFlowConverter.Clef(res[2]);
  123. }
  124. }
  125. let vfnote: Vex.Flow.StaveNote = new Vex.Flow.StaveNote({
  126. auto_stem: true,
  127. clef: vfclef,
  128. duration: duration,
  129. duration_override: {
  130. denominator: frac.Denominator,
  131. numerator: frac.Numerator,
  132. },
  133. keys: keys,
  134. });
  135. for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
  136. (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
  137. if (accidentals[i]) {
  138. vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i]));
  139. }
  140. }
  141. return vfnote;
  142. }
  143. /**
  144. * Convert a ClefInstruction to a string representing a clef type in VexFlow
  145. * @param clef
  146. * @returns {string}
  147. * @constructor
  148. */
  149. public static Clef(clef: ClefInstruction): string {
  150. let type: string;
  151. switch (clef.ClefType) {
  152. case ClefEnum.G:
  153. type = "treble";
  154. break;
  155. case ClefEnum.F:
  156. type = "bass";
  157. break;
  158. case ClefEnum.C:
  159. type = "alto";
  160. break;
  161. case ClefEnum.percussion:
  162. type = "percussion";
  163. break;
  164. case ClefEnum.TAB:
  165. type = "tab";
  166. break;
  167. default:
  168. }
  169. return type;
  170. }
  171. /**
  172. * Convert a RhythmInstruction to a VexFlow TimeSignature object
  173. * @param rhythm
  174. * @returns {Vex.Flow.TimeSignature}
  175. * @constructor
  176. */
  177. public static TimeSignature(rhythm: RhythmInstruction): Vex.Flow.TimeSignature {
  178. let timeSpec: string;
  179. switch (rhythm.SymbolEnum) {
  180. case RhythmSymbolEnum.NONE:
  181. timeSpec = rhythm.Rhythm.Numerator + "/" + rhythm.Rhythm.Denominator;
  182. break;
  183. case RhythmSymbolEnum.COMMON:
  184. timeSpec = "C";
  185. break;
  186. case RhythmSymbolEnum.CUT:
  187. timeSpec = "C|";
  188. break;
  189. default:
  190. }
  191. return new Vex.Flow.TimeSignature(timeSpec);
  192. }
  193. /**
  194. * Convert a KeyInstruction to a string representing in VexFlow a key
  195. * @param key
  196. * @returns {string}
  197. */
  198. public static keySignature(key: KeyInstruction): string {
  199. if (key === undefined) {
  200. return undefined;
  201. }
  202. let ret: string;
  203. switch (key.Mode) {
  204. case KeyEnum.none:
  205. ret = undefined;
  206. break;
  207. case KeyEnum.minor:
  208. ret = VexFlowConverter.minorMap[key.Key] + "m";
  209. break;
  210. case KeyEnum.major:
  211. ret = VexFlowConverter.majorMap[key.Key];
  212. break;
  213. default:
  214. }
  215. return ret;
  216. }
  217. /**
  218. * Converts a lineType to a VexFlow StaveConnector type
  219. * @param lineType
  220. * @returns {any}
  221. */
  222. public static line(lineType: SystemLinesEnum): any {
  223. // TODO Not all line types are correctly mapped!
  224. switch (lineType) {
  225. case SystemLinesEnum.SingleThin:
  226. return Vex.Flow.StaveConnector.type.SINGLE;
  227. case SystemLinesEnum.DoubleThin:
  228. return Vex.Flow.StaveConnector.type.DOUBLE;
  229. case SystemLinesEnum.ThinBold:
  230. return Vex.Flow.StaveConnector.type.SINGLE;
  231. case SystemLinesEnum.BoldThinDots:
  232. return Vex.Flow.StaveConnector.type.DOUBLE;
  233. case SystemLinesEnum.DotsThinBold:
  234. return Vex.Flow.StaveConnector.type.DOUBLE;
  235. case SystemLinesEnum.DotsBoldBoldDots:
  236. return Vex.Flow.StaveConnector.type.DOUBLE;
  237. case SystemLinesEnum.None:
  238. return Vex.Flow.StaveConnector.type.NONE;
  239. default:
  240. }
  241. }
  242. /**
  243. * Construct a string which can be used in a CSS font property
  244. * @param fontSize
  245. * @param fontStyle
  246. * @param font
  247. * @returns {string}
  248. */
  249. public static font(fontSize: number, fontStyle: FontStyles = FontStyles.Regular, font: Fonts = Fonts.TimesNewRoman): string {
  250. let style: string = "normal";
  251. let weight: string = "normal";
  252. let family: string = "'Times New Roman'";
  253. switch (fontStyle) {
  254. case FontStyles.Bold:
  255. weight = "bold";
  256. break;
  257. case FontStyles.Italic:
  258. style = "italic";
  259. break;
  260. case FontStyles.BoldItalic:
  261. style = "italic";
  262. weight = "bold";
  263. break;
  264. case FontStyles.Underlined:
  265. // TODO
  266. break;
  267. default:
  268. break;
  269. }
  270. switch (font) {
  271. case Fonts.Kokila:
  272. // TODO Not Supported
  273. break;
  274. default:
  275. }
  276. return style + " " + weight + " " + Math.floor(fontSize) + "px " + family;
  277. }
  278. /**
  279. * Convert OutlineAndFillStyle to CSS properties
  280. * @param styleId
  281. * @returns {string}
  282. */
  283. public static style(styleId: OutlineAndFillStyleEnum): string {
  284. // TODO To be implemented
  285. return "purple";
  286. }
  287. }