InstrumentReader.ts 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. import {Instrument} from "../Instrument";
  2. import {MusicSheet} from "../MusicSheet";
  3. import {VoiceGenerator} from "./VoiceGenerator";
  4. import {Staff} from "../VoiceData/Staff";
  5. import {SourceMeasure} from "../VoiceData/SourceMeasure";
  6. import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
  7. import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
  8. import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
  9. import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
  10. import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
  11. import {Fraction} from "../../Common/DataObjects/Fraction";
  12. import {IXmlElement} from "../../Common/FileIO/Xml";
  13. import {ITextTranslation} from "../Interfaces/ITextTranslation";
  14. import {MusicSheetReadingException} from "../Exceptions";
  15. import {ClefEnum} from "../VoiceData/Instructions/ClefInstruction";
  16. import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
  17. import {KeyEnum} from "../VoiceData/Instructions/KeyInstruction";
  18. import {IXmlAttribute} from "../../Common/FileIO/Xml";
  19. import {ChordSymbolContainer} from "../VoiceData/ChordSymbolContainer";
  20. import * as log from "loglevel";
  21. import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
  22. import {ChordSymbolReader} from "./MusicSymbolModules/ChordSymbolReader";
  23. import {ExpressionReader} from "./MusicSymbolModules/ExpressionReader";
  24. import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
  25. import {SlurReader} from "./MusicSymbolModules/SlurReader";
  26. import {StemDirectionType} from "../VoiceData/VoiceEntry";
  27. //import Dictionary from "typescript-collections/dist/lib/Dictionary";
  28. // FIXME: The following classes are missing
  29. //type ChordSymbolContainer = any;
  30. //type SlurReader = any;
  31. //type RepetitionInstructionReader = any;
  32. //declare class MusicSymbolModuleFactory {
  33. // public static createSlurReader(x: any): any;
  34. //}
  35. //
  36. //class MetronomeReader {
  37. // public static addMetronomeSettings(xmlNode: IXmlElement, musicSheet: MusicSheet): void { }
  38. // public static readMetronomeInstructions(xmlNode: IXmlElement, musicSheet: MusicSheet, currentXmlMeasureIndex: number): void { }
  39. // public static readTempoInstruction(soundNode: IXmlElement, musicSheet: MusicSheet, currentXmlMeasureIndex: number): void { }
  40. //}
  41. //
  42. //class ChordSymbolReader {
  43. // public static readChordSymbol(xmlNode:IXmlElement, musicSheet:MusicSheet, activeKey:any): void {
  44. // }
  45. //}
  46. /**
  47. * An InstrumentReader is used during the reading phase to keep parsing new measures from the MusicXML file
  48. * with the readNextXmlMeasure method.
  49. */
  50. export class InstrumentReader {
  51. constructor(repetitionInstructionReader: RepetitionInstructionReader, xmlMeasureList: IXmlElement[], instrument: Instrument) {
  52. this.repetitionInstructionReader = repetitionInstructionReader;
  53. this.xmlMeasureList = xmlMeasureList;
  54. this.musicSheet = instrument.GetMusicSheet;
  55. this.instrument = instrument;
  56. this.activeClefs = new Array(instrument.Staves.length);
  57. this.activeClefsHaveBeenInitialized = new Array(instrument.Staves.length);
  58. for (let i: number = 0; i < instrument.Staves.length; i++) {
  59. this.activeClefsHaveBeenInitialized[i] = false;
  60. }
  61. this.createExpressionGenerators(instrument.Staves.length);
  62. this.slurReader = new SlurReader(this.musicSheet);
  63. }
  64. private repetitionInstructionReader: RepetitionInstructionReader;
  65. private xmlMeasureList: IXmlElement[];
  66. private musicSheet: MusicSheet;
  67. private slurReader: SlurReader;
  68. private instrument: Instrument;
  69. private voiceGeneratorsDict: { [n: number]: VoiceGenerator; } = {};
  70. private staffMainVoiceGeneratorDict: { [staffId: number]: VoiceGenerator } = {};
  71. private inSourceMeasureInstrumentIndex: number;
  72. private divisions: number = 0;
  73. private currentMeasure: SourceMeasure;
  74. private previousMeasure: SourceMeasure;
  75. private currentXmlMeasureIndex: number = 0;
  76. private currentStaff: Staff;
  77. private currentStaffEntry: SourceStaffEntry;
  78. private activeClefs: ClefInstruction[];
  79. private activeKey: KeyInstruction;
  80. private activeRhythm: RhythmInstruction;
  81. private activeClefsHaveBeenInitialized: boolean[];
  82. private activeKeyHasBeenInitialized: boolean = false;
  83. private abstractInstructions: [number, AbstractNotationInstruction][] = [];
  84. private openChordSymbolContainer: ChordSymbolContainer;
  85. private expressionReaders: ExpressionReader[];
  86. private currentVoiceGenerator: VoiceGenerator;
  87. //private openSlurDict: { [n: number]: Slur; } = {};
  88. private maxTieNoteFraction: Fraction;
  89. public get ActiveKey(): KeyInstruction {
  90. return this.activeKey;
  91. }
  92. public get MaxTieNoteFraction(): Fraction {
  93. return this.maxTieNoteFraction;
  94. }
  95. public get ActiveRhythm(): RhythmInstruction {
  96. return this.activeRhythm;
  97. }
  98. public set ActiveRhythm(value: RhythmInstruction) {
  99. this.activeRhythm = value;
  100. }
  101. /**
  102. * Main CreateSheet: read the next XML Measure and save all data to the given [[SourceMeasure]].
  103. * @param currentMeasure
  104. * @param measureStartAbsoluteTimestamp - Using this instead of currentMeasure.AbsoluteTimestamp as it isn't set yet
  105. * @param guitarPro
  106. * @returns {boolean}
  107. */
  108. public readNextXmlMeasure(currentMeasure: SourceMeasure, measureStartAbsoluteTimestamp: Fraction, guitarPro: boolean): boolean {
  109. if (this.currentXmlMeasureIndex >= this.xmlMeasureList.length) {
  110. return false;
  111. }
  112. this.currentMeasure = currentMeasure;
  113. this.inSourceMeasureInstrumentIndex = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument);
  114. if (this.repetitionInstructionReader !== undefined) {
  115. this.repetitionInstructionReader.prepareReadingMeasure(currentMeasure, this.currentXmlMeasureIndex);
  116. }
  117. let currentFraction: Fraction = new Fraction(0, 1);
  118. let previousFraction: Fraction = new Fraction(0, 1);
  119. let divisionsException: boolean = false;
  120. this.maxTieNoteFraction = new Fraction(0, 1);
  121. let lastNoteWasGrace: boolean = false;
  122. try {
  123. const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
  124. for (const xmlNode of xmlMeasureListArr) {
  125. if (xmlNode.name === "note") {
  126. let printObject: boolean = true;
  127. if (xmlNode.hasAttributes && xmlNode.attribute("print-object") &&
  128. xmlNode.attribute("print-object").value === "no") {
  129. printObject = false; // note will not be rendered, but still parsed for Playback etc.
  130. // if (xmlNode.attribute("print-spacing")) {
  131. // if (xmlNode.attribute("print-spacing").value === "yes" {
  132. // // TODO give spacing for invisible notes even when not displayed. might be hard with Vexflow formatting
  133. }
  134. let noteStaff: number = 1;
  135. if (this.instrument.Staves.length > 1) {
  136. if (xmlNode.element("staff") !== undefined) {
  137. noteStaff = parseInt(xmlNode.element("staff").value, 10);
  138. if (isNaN(noteStaff)) {
  139. log.debug("InstrumentReader.readNextXmlMeasure.get staff number");
  140. noteStaff = 1;
  141. }
  142. }
  143. }
  144. this.currentStaff = this.instrument.Staves[noteStaff - 1];
  145. const isChord: boolean = xmlNode.element("chord") !== undefined;
  146. if (xmlNode.element("voice") !== undefined) {
  147. const noteVoice: number = parseInt(xmlNode.element("voice").value, 10);
  148. this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(noteVoice, noteStaff - 1);
  149. } else {
  150. if (!isChord || this.currentVoiceGenerator === undefined) {
  151. this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(1, noteStaff - 1);
  152. }
  153. }
  154. let noteDivisions: number = 0;
  155. let noteDuration: Fraction = new Fraction(0, 1);
  156. let normalNotes: number = 2;
  157. let typeDuration: Fraction = undefined;
  158. let isTuplet: boolean = false;
  159. if (xmlNode.element("duration") !== undefined) {
  160. noteDivisions = parseInt(xmlNode.element("duration").value, 10);
  161. if (!isNaN(noteDivisions)) {
  162. noteDuration = new Fraction(noteDivisions, 4 * this.divisions);
  163. if (noteDivisions === 0) {
  164. noteDuration = this.getNoteDurationFromTypeNode(xmlNode);
  165. } else {
  166. typeDuration = this.getNoteDurationFromTypeNode(xmlNode);
  167. }
  168. if (xmlNode.element("time-modification") !== undefined) {
  169. noteDuration = this.getNoteDurationForTuplet(xmlNode);
  170. const time: IXmlElement = xmlNode.element("time-modification");
  171. if (time !== undefined) {
  172. if (time.element("normal-notes") !== undefined) {
  173. normalNotes = parseInt(time.element("normal-notes").value, 10);
  174. }
  175. }
  176. isTuplet = true;
  177. }
  178. } else {
  179. const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid Note Duration.");
  180. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  181. log.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
  182. continue;
  183. }
  184. }
  185. const restNote: boolean = xmlNode.element("rest") !== undefined;
  186. //log.info("New note found!", noteDivisions, noteDuration.toString(), restNote);
  187. const notationsNode: IXmlElement = xmlNode.element("notations"); // used for multiple checks further on
  188. const isGraceNote: boolean = xmlNode.element("grace") !== undefined || noteDivisions === 0 || isChord && lastNoteWasGrace;
  189. let graceNoteSlash: boolean = false;
  190. let graceSlur: boolean = false;
  191. if (isGraceNote) {
  192. const graceNode: IXmlElement = xmlNode.element("grace");
  193. if (graceNode && graceNode.attributes()) {
  194. if (graceNode.attribute("slash")) {
  195. const slash: string = graceNode.attribute("slash").value;
  196. if (slash === "yes") {
  197. graceNoteSlash = true;
  198. }
  199. }
  200. }
  201. noteDuration = this.getNoteDurationFromTypeNode(xmlNode);
  202. const notationNode: IXmlElement = xmlNode.element("notations");
  203. if (notationNode !== undefined) {
  204. if (notationNode.element("slur") !== undefined) {
  205. graceSlur = true;
  206. // grace slurs could be non-binary, but VexFlow.GraceNoteGroup modifier system is currently only boolean for slurs.
  207. }
  208. }
  209. }
  210. // check for cue note
  211. let isCueNote: boolean = false;
  212. const cueNode: IXmlElement = xmlNode.element("cue");
  213. if (cueNode !== undefined) {
  214. isCueNote = true;
  215. }
  216. // alternative: check for <type size="cue">
  217. const typeNode: IXmlElement = xmlNode.element("type");
  218. if (typeNode !== undefined) {
  219. const sizeAttr: Attr = typeNode.attribute("size");
  220. if (sizeAttr !== undefined && sizeAttr !== null) {
  221. if (sizeAttr.value === "cue") {
  222. isCueNote = true;
  223. }
  224. }
  225. }
  226. // check stem element
  227. let stemDirectionXml: StemDirectionType = StemDirectionType.Undefined;
  228. let stemColorXml: string;
  229. const stemNode: IXmlElement = xmlNode.element("stem");
  230. if (stemNode !== undefined) {
  231. switch (stemNode.value) {
  232. case "down":
  233. stemDirectionXml = StemDirectionType.Down;
  234. break;
  235. case "up":
  236. stemDirectionXml = StemDirectionType.Up;
  237. break;
  238. case "double":
  239. stemDirectionXml = StemDirectionType.Double;
  240. break;
  241. case "none":
  242. stemDirectionXml = StemDirectionType.None;
  243. break;
  244. default:
  245. stemDirectionXml = StemDirectionType.Undefined;
  246. }
  247. const stemColorAttr: Attr = stemNode.attribute("color");
  248. if (stemColorAttr) { // can be null, maybe also undefined
  249. stemColorXml = this.parseXmlColor(stemColorAttr.value);
  250. }
  251. }
  252. // check Tremolo
  253. let tremoloStrokes: number = 0;
  254. if (notationsNode !== undefined) {
  255. const ornamentsNode: IXmlElement = notationsNode.element("ornaments");
  256. if (ornamentsNode !== undefined) {
  257. const tremoloNode: IXmlElement = ornamentsNode.element("tremolo");
  258. if (tremoloNode !== undefined) {
  259. const tremoloType: Attr = tremoloNode.attribute("type");
  260. if (tremoloType && tremoloType.value === "single") {
  261. const tremoloStrokesGiven: number = parseInt(tremoloNode.value, 10);
  262. if (tremoloStrokesGiven > 0) {
  263. tremoloStrokes = tremoloStrokesGiven;
  264. }
  265. }
  266. // TODO implement type "start". Vexflow doesn't have tremolo beams yet though (shorter than normal beams)
  267. }
  268. }
  269. }
  270. // check notehead/color
  271. let noteheadColorXml: string;
  272. const noteheadNode: IXmlElement = xmlNode.element("notehead");
  273. if (noteheadNode) {
  274. const colorAttr: Attr = noteheadNode.attribute("color");
  275. if (colorAttr) {
  276. noteheadColorXml = this.parseXmlColor(colorAttr.value);
  277. }
  278. }
  279. let noteColorXml: string;
  280. const noteColorAttr: Attr = xmlNode.attribute("color");
  281. if (noteColorAttr) { // can be undefined
  282. noteColorXml = this.parseXmlColor(noteColorAttr.value);
  283. if (noteheadColorXml === undefined) {
  284. noteheadColorXml = noteColorXml;
  285. }
  286. if (stemColorXml === undefined) {
  287. stemColorXml = noteColorXml;
  288. }
  289. }
  290. let musicTimestamp: Fraction = currentFraction.clone();
  291. if (isChord) {
  292. musicTimestamp = previousFraction.clone();
  293. }
  294. this.currentStaffEntry = this.currentMeasure.findOrCreateStaffEntry(
  295. musicTimestamp,
  296. this.inSourceMeasureInstrumentIndex + noteStaff - 1,
  297. this.currentStaff
  298. ).staffEntry;
  299. //log.info("currentStaffEntry", this.currentStaffEntry, this.currentMeasure.VerticalSourceStaffEntryContainers.length);
  300. if (!this.currentVoiceGenerator.hasVoiceEntry()
  301. || (!isChord && !isGraceNote && !lastNoteWasGrace)
  302. || (isGraceNote && !lastNoteWasGrace)
  303. || (isGraceNote && !isChord)
  304. || (!isGraceNote && lastNoteWasGrace)
  305. ) {
  306. this.currentVoiceGenerator.createVoiceEntry(musicTimestamp, this.currentStaffEntry, !restNote && !isGraceNote,
  307. isGraceNote, graceNoteSlash, graceSlur);
  308. }
  309. if (!isGraceNote && !isChord) {
  310. previousFraction = currentFraction.clone();
  311. currentFraction.Add(noteDuration);
  312. }
  313. if (
  314. isChord &&
  315. this.currentStaffEntry !== undefined &&
  316. this.currentStaffEntry.ParentStaff !== this.currentStaff
  317. ) {
  318. this.currentStaffEntry = this.currentVoiceGenerator.checkForStaffEntryLink(
  319. this.inSourceMeasureInstrumentIndex + noteStaff - 1, this.currentStaff, this.currentStaffEntry, this.currentMeasure
  320. );
  321. }
  322. const beginOfMeasure: boolean = (
  323. this.currentStaffEntry !== undefined &&
  324. this.currentStaffEntry.Timestamp !== undefined &&
  325. this.currentStaffEntry.Timestamp.Equals(new Fraction(0, 1)) && !this.currentStaffEntry.hasNotes()
  326. );
  327. this.saveAbstractInstructionList(this.instrument.Staves.length, beginOfMeasure);
  328. if (this.openChordSymbolContainer !== undefined) {
  329. this.currentStaffEntry.ChordContainer = this.openChordSymbolContainer;
  330. this.openChordSymbolContainer = undefined;
  331. }
  332. if (this.activeRhythm !== undefined) {
  333. // (*) this.musicSheet.SheetPlaybackSetting.Rhythm = this.activeRhythm.Rhythm;
  334. }
  335. if (!isTuplet && !isGraceNote) {
  336. noteDuration = new Fraction(noteDivisions, 4 * this.divisions);
  337. }
  338. this.currentVoiceGenerator.read(
  339. xmlNode, noteDuration, typeDuration, normalNotes, restNote,
  340. this.currentStaffEntry, this.currentMeasure,
  341. measureStartAbsoluteTimestamp,
  342. this.maxTieNoteFraction, isChord, guitarPro,
  343. printObject, isCueNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml
  344. );
  345. // notationsNode created further up for multiple checks
  346. if (notationsNode !== undefined && notationsNode.element("dynamics") !== undefined) {
  347. const expressionReader: ExpressionReader = this.expressionReaders[this.readExpressionStaffNumber(xmlNode) - 1];
  348. if (expressionReader !== undefined) {
  349. expressionReader.readExpressionParameters(
  350. xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
  351. );
  352. expressionReader.read(
  353. xmlNode, this.currentMeasure, previousFraction
  354. );
  355. }
  356. }
  357. lastNoteWasGrace = isGraceNote;
  358. } else if (xmlNode.name === "attributes") {
  359. const divisionsNode: IXmlElement = xmlNode.element("divisions");
  360. if (divisionsNode !== undefined) {
  361. this.divisions = parseInt(divisionsNode.value, 10);
  362. if (isNaN(this.divisions)) {
  363. const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DivisionError",
  364. "Invalid divisions value at Instrument: ");
  365. log.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
  366. this.divisions = this.readDivisionsFromNotes();
  367. if (this.divisions > 0) {
  368. this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
  369. } else {
  370. divisionsException = true;
  371. throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
  372. }
  373. }
  374. }
  375. if (
  376. xmlNode.element("divisions") === undefined &&
  377. this.divisions === 0 &&
  378. this.currentXmlMeasureIndex === 0
  379. ) {
  380. const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DivisionError", "Invalid divisions value at Instrument: ");
  381. this.divisions = this.readDivisionsFromNotes();
  382. if (this.divisions > 0) {
  383. this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
  384. } else {
  385. divisionsException = true;
  386. throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
  387. }
  388. }
  389. this.addAbstractInstruction(xmlNode, guitarPro);
  390. if (currentFraction.Equals(new Fraction(0, 1)) &&
  391. this.isAttributesNodeAtBeginOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
  392. this.saveAbstractInstructionList(this.instrument.Staves.length, true);
  393. }
  394. if (this.isAttributesNodeAtEndOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
  395. this.saveClefInstructionAtEndOfMeasure();
  396. }
  397. } else if (xmlNode.name === "forward") {
  398. const forFraction: number = parseInt(xmlNode.element("duration").value, 10);
  399. currentFraction.Add(new Fraction(forFraction, 4 * this.divisions));
  400. } else if (xmlNode.name === "backup") {
  401. const backFraction: number = parseInt(xmlNode.element("duration").value, 10);
  402. currentFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
  403. if (currentFraction.IsNegative()) {
  404. currentFraction = new Fraction(0, 1);
  405. }
  406. previousFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
  407. if (previousFraction.IsNegative()) {
  408. previousFraction = new Fraction(0, 1);
  409. }
  410. } else if (xmlNode.name === "direction") {
  411. const directionTypeNode: IXmlElement = xmlNode.element("direction-type");
  412. // (*) MetronomeReader.readMetronomeInstructions(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
  413. let relativePositionInMeasure: number = Math.min(1, currentFraction.RealValue);
  414. if (this.activeRhythm !== undefined && this.activeRhythm.Rhythm !== undefined) {
  415. relativePositionInMeasure /= this.activeRhythm.Rhythm.RealValue;
  416. }
  417. let handeled: boolean = false;
  418. if (this.repetitionInstructionReader !== undefined) {
  419. handeled = this.repetitionInstructionReader.handleRepetitionInstructionsFromWordsOrSymbols( directionTypeNode,
  420. relativePositionInMeasure);
  421. }
  422. if (!handeled) {
  423. let expressionReader: ExpressionReader = this.expressionReaders[0];
  424. const staffIndex: number = this.readExpressionStaffNumber(xmlNode) - 1;
  425. if (staffIndex < this.expressionReaders.length) {
  426. expressionReader = this.expressionReaders[staffIndex];
  427. }
  428. if (expressionReader !== undefined) {
  429. if (directionTypeNode.element("octave-shift") !== undefined) {
  430. expressionReader.readExpressionParameters(
  431. xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, true
  432. );
  433. expressionReader.addOctaveShift(xmlNode, this.currentMeasure, previousFraction.clone());
  434. }
  435. expressionReader.readExpressionParameters(
  436. xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
  437. );
  438. expressionReader.read(xmlNode, this.currentMeasure, currentFraction);
  439. }
  440. }
  441. } else if (xmlNode.name === "barline") {
  442. if (this.repetitionInstructionReader !== undefined) {
  443. const measureEndsSystem: boolean = false;
  444. this.repetitionInstructionReader.handleLineRepetitionInstructions(xmlNode, measureEndsSystem);
  445. if (measureEndsSystem) {
  446. this.currentMeasure.BreakSystemAfter = true;
  447. this.currentMeasure.endsPiece = true;
  448. }
  449. }
  450. if (xmlNode.element("bar-style") !== undefined) {
  451. this.currentMeasure.endingBarStyle = xmlNode.element("bar-style").value;
  452. }
  453. } else if (xmlNode.name === "sound") {
  454. // (*) MetronomeReader.readTempoInstruction(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
  455. } else if (xmlNode.name === "harmony") {
  456. this.openChordSymbolContainer = ChordSymbolReader.readChordSymbol(xmlNode, this.musicSheet, this.activeKey);
  457. }
  458. }
  459. for (const j in this.voiceGeneratorsDict) {
  460. if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
  461. const voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[j];
  462. voiceGenerator.checkForOpenBeam();
  463. }
  464. }
  465. if (this.currentXmlMeasureIndex === this.xmlMeasureList.length - 1) {
  466. for (let i: number = 0; i < this.instrument.Staves.length; i++) {
  467. if (!this.activeClefsHaveBeenInitialized[i]) {
  468. this.createDefaultClefInstruction(this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument) + i);
  469. }
  470. }
  471. if (!this.activeKeyHasBeenInitialized) {
  472. this.createDefaultKeyInstruction();
  473. }
  474. for (let i: number = 0; i < this.expressionReaders.length; i++) {
  475. const reader: ExpressionReader = this.expressionReaders[i];
  476. if (reader !== undefined) {
  477. reader.checkForOpenExpressions(this.currentMeasure, currentFraction);
  478. }
  479. }
  480. }
  481. } catch (e) {
  482. if (divisionsException) {
  483. throw new MusicSheetReadingException(e.Message);
  484. }
  485. const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MeasureError", "Error while reading Measure.");
  486. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  487. log.debug("InstrumentReader.readNextXmlMeasure", errorMsg, e);
  488. }
  489. this.previousMeasure = this.currentMeasure;
  490. this.currentXmlMeasureIndex += 1;
  491. return true;
  492. }
  493. /** Parse a color in XML format. Can be #ARGB or #RGB format, colors as byte hex values.
  494. * @return color in Vexflow format #[A]RGB or undefined for invalid xmlColorString
  495. */
  496. public parseXmlColor(xmlColorString: string): string {
  497. if (!xmlColorString) {
  498. return undefined;
  499. }
  500. if (xmlColorString.length === 7) { // #RGB
  501. return xmlColorString;
  502. } else if (xmlColorString.length === 9) { // #ARGB
  503. return "#" + xmlColorString.substr(3); // cut away alpha channel
  504. } else {
  505. return undefined; // invalid xml color
  506. }
  507. }
  508. public doCalculationsAfterDurationHasBeenSet(): void {
  509. for (const j in this.voiceGeneratorsDict) {
  510. if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
  511. this.voiceGeneratorsDict[j].checkOpenTies();
  512. }
  513. }
  514. }
  515. /**
  516. * Get or create the passing [[VoiceGenerator]].
  517. * @param voiceId
  518. * @param staffId
  519. * @returns {VoiceGenerator}
  520. */
  521. private getOrCreateVoiceGenerator(voiceId: number, staffId: number): VoiceGenerator {
  522. const staff: Staff = this.instrument.Staves[staffId];
  523. let voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[voiceId];
  524. if (voiceGenerator !== undefined) {
  525. if (staff.Voices.indexOf(voiceGenerator.GetVoice) === -1) {
  526. staff.Voices.push(voiceGenerator.GetVoice);
  527. }
  528. } else {
  529. const mainVoiceGenerator: VoiceGenerator = this.staffMainVoiceGeneratorDict[staffId];
  530. if (mainVoiceGenerator !== undefined) {
  531. voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader, mainVoiceGenerator.GetVoice);
  532. staff.Voices.push(voiceGenerator.GetVoice);
  533. this.voiceGeneratorsDict[voiceId] = voiceGenerator;
  534. } else {
  535. voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader);
  536. staff.Voices.push(voiceGenerator.GetVoice);
  537. this.voiceGeneratorsDict[voiceId] = voiceGenerator;
  538. this.staffMainVoiceGeneratorDict[staffId] = voiceGenerator;
  539. }
  540. }
  541. return voiceGenerator;
  542. }
  543. private createExpressionGenerators(numberOfStaves: number): void {
  544. this.expressionReaders = new Array(numberOfStaves);
  545. for (let i: number = 0; i < numberOfStaves; i++) {
  546. this.expressionReaders[i] = new ExpressionReader(this.musicSheet, this.instrument, i + 1);
  547. }
  548. }
  549. /**
  550. * Create the default [[ClefInstruction]] for the given staff index.
  551. * @param staffIndex
  552. */
  553. private createDefaultClefInstruction(staffIndex: number): void {
  554. let first: SourceMeasure;
  555. if (this.musicSheet.SourceMeasures.length > 0) {
  556. first = this.musicSheet.SourceMeasures[0];
  557. } else {
  558. first = this.currentMeasure;
  559. }
  560. const clefInstruction: ClefInstruction = new ClefInstruction(ClefEnum.G, 0, 2);
  561. let firstStaffEntry: SourceStaffEntry;
  562. if (first.FirstInstructionsStaffEntries[staffIndex] === undefined) {
  563. firstStaffEntry = new SourceStaffEntry(undefined, undefined);
  564. first.FirstInstructionsStaffEntries[staffIndex] = firstStaffEntry;
  565. } else {
  566. firstStaffEntry = first.FirstInstructionsStaffEntries[staffIndex];
  567. firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
  568. }
  569. clefInstruction.Parent = firstStaffEntry;
  570. firstStaffEntry.Instructions.splice(0, 0, clefInstruction);
  571. }
  572. /**
  573. * Create the default [[KeyInstruction]] in case no [[KeyInstruction]] is given in the whole [[Instrument]].
  574. */
  575. private createDefaultKeyInstruction(): void {
  576. let first: SourceMeasure;
  577. if (this.musicSheet.SourceMeasures.length > 0) {
  578. first = this.musicSheet.SourceMeasures[0];
  579. } else {
  580. first = this.currentMeasure;
  581. }
  582. const keyInstruction: KeyInstruction = new KeyInstruction(undefined, 0, KeyEnum.major);
  583. for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + this.instrument.Staves.length; j++) {
  584. if (first.FirstInstructionsStaffEntries[j] === undefined) {
  585. const firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
  586. first.FirstInstructionsStaffEntries[j] = firstStaffEntry;
  587. keyInstruction.Parent = firstStaffEntry;
  588. firstStaffEntry.Instructions.push(keyInstruction);
  589. } else {
  590. const firstStaffEntry: SourceStaffEntry = first.FirstInstructionsStaffEntries[j];
  591. keyInstruction.Parent = firstStaffEntry;
  592. firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
  593. if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
  594. firstStaffEntry.Instructions.splice(1, 0, keyInstruction);
  595. } else {
  596. firstStaffEntry.Instructions.splice(0, 0, keyInstruction);
  597. }
  598. }
  599. }
  600. }
  601. /**
  602. * Check if the given attributesNode is at the begin of a XmlMeasure.
  603. * @param parentNode
  604. * @param attributesNode
  605. * @returns {boolean}
  606. */
  607. private isAttributesNodeAtBeginOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
  608. const children: IXmlElement[] = parentNode.elements();
  609. const attributesNodeIndex: number = children.indexOf(attributesNode); // FIXME | 0
  610. if (attributesNodeIndex > 0 && children[attributesNodeIndex - 1].name === "backup") {
  611. return true;
  612. }
  613. let firstNoteNodeIndex: number = -1;
  614. for (let i: number = 0; i < children.length; i++) {
  615. if (children[i].name === "note") {
  616. firstNoteNodeIndex = i;
  617. break;
  618. }
  619. }
  620. return (attributesNodeIndex < firstNoteNodeIndex && firstNoteNodeIndex > 0) || (firstNoteNodeIndex < 0);
  621. }
  622. /**
  623. * Check if the given attributesNode is at the end of a XmlMeasure.
  624. * @param parentNode
  625. * @param attributesNode
  626. * @returns {boolean}
  627. */
  628. private isAttributesNodeAtEndOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
  629. const childs: IXmlElement[] = parentNode.elements().slice();
  630. let attributesNodeIndex: number = 0;
  631. for (let i: number = 0; i < childs.length; i++) {
  632. if (childs[i] === attributesNode) {
  633. attributesNodeIndex = i;
  634. break;
  635. }
  636. }
  637. let nextNoteNodeIndex: number = 0;
  638. for (let i: number = attributesNodeIndex; i < childs.length; i++) {
  639. if (childs[i].name === "note") {
  640. nextNoteNodeIndex = i;
  641. break;
  642. }
  643. }
  644. return attributesNodeIndex > nextNoteNodeIndex;
  645. }
  646. /**
  647. * Called only when no noteDuration is given in XML.
  648. * @param xmlNode
  649. * @returns {Fraction}
  650. */
  651. private getNoteDurationFromTypeNode(xmlNode: IXmlElement): Fraction {
  652. const typeNode: IXmlElement = xmlNode.element("type");
  653. if (typeNode !== undefined) {
  654. const type: string = typeNode.value;
  655. return this.currentVoiceGenerator.getNoteDurationFromType(type);
  656. }
  657. return new Fraction(0, 4 * this.divisions);
  658. }
  659. /**
  660. * Add (the three basic) Notation Instructions to a list
  661. * @param node
  662. * @param guitarPro
  663. */
  664. private addAbstractInstruction(node: IXmlElement, guitarPro: boolean): void {
  665. if (node.element("divisions") !== undefined) {
  666. if (node.elements().length === 1) {
  667. return;
  668. }
  669. }
  670. const transposeNode: IXmlElement = node.element("transpose");
  671. if (transposeNode !== undefined) {
  672. const chromaticNode: IXmlElement = transposeNode.element("chromatic");
  673. if (chromaticNode !== undefined) {
  674. this.instrument.PlaybackTranspose = parseInt(chromaticNode.value, 10);
  675. }
  676. }
  677. const clefList: IXmlElement[] = node.elements("clef");
  678. let errorMsg: string;
  679. if (clefList.length > 0) {
  680. for (let idx: number = 0, len: number = clefList.length; idx < len; ++idx) {
  681. const nodeList: IXmlElement = clefList[idx];
  682. let clefEnum: ClefEnum = ClefEnum.G;
  683. let line: number = 2;
  684. let staffNumber: number = 1;
  685. let clefOctaveOffset: number = 0;
  686. const lineNode: IXmlElement = nodeList.element("line");
  687. if (lineNode !== undefined) {
  688. try {
  689. line = parseInt(lineNode.value, 10);
  690. } catch (ex) {
  691. errorMsg = ITextTranslation.translateText(
  692. "ReaderErrorMessages/ClefLineError",
  693. "Invalid clef line given -> using default clef line."
  694. );
  695. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  696. line = 2;
  697. log.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
  698. }
  699. }
  700. const signNode: IXmlElement = nodeList.element("sign");
  701. if (signNode !== undefined) {
  702. try {
  703. clefEnum = ClefEnum[signNode.value];
  704. if (!ClefInstruction.isSupportedClef(clefEnum)) {
  705. if (clefEnum === ClefEnum.TAB && guitarPro) {
  706. clefOctaveOffset = -1;
  707. }
  708. errorMsg = ITextTranslation.translateText(
  709. "ReaderErrorMessages/ClefError",
  710. "Unsupported clef found -> using default clef."
  711. );
  712. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  713. clefEnum = ClefEnum.G;
  714. line = 2;
  715. }
  716. } catch (e) {
  717. errorMsg = ITextTranslation.translateText(
  718. "ReaderErrorMessages/ClefError",
  719. "Invalid clef found -> using default clef."
  720. );
  721. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  722. clefEnum = ClefEnum.G;
  723. line = 2;
  724. log.debug("InstrumentReader.addAbstractInstruction", errorMsg, e);
  725. }
  726. }
  727. const clefOctaveNode: IXmlElement = nodeList.element("clef-octave-change");
  728. if (clefOctaveNode !== undefined) {
  729. try {
  730. clefOctaveOffset = parseInt(clefOctaveNode.value, 10);
  731. } catch (e) {
  732. errorMsg = ITextTranslation.translateText(
  733. "ReaderErrorMessages/ClefOctaveError",
  734. "Invalid clef octave found -> using default clef octave."
  735. );
  736. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  737. clefOctaveOffset = 0;
  738. }
  739. }
  740. if (nodeList.hasAttributes && nodeList.attributes()[0].name === "number") {
  741. try {
  742. staffNumber = parseInt(nodeList.attributes()[0].value, 10);
  743. } catch (err) {
  744. errorMsg = ITextTranslation.translateText(
  745. "ReaderErrorMessages/ClefError",
  746. "Invalid clef found -> using default clef."
  747. );
  748. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  749. staffNumber = 1;
  750. }
  751. }
  752. const clefInstruction: ClefInstruction = new ClefInstruction(clefEnum, clefOctaveOffset, line);
  753. this.abstractInstructions.push([staffNumber, clefInstruction]);
  754. }
  755. }
  756. if (node.element("key") !== undefined && this.instrument.MidiInstrumentId !== MidiInstrument.Percussion) {
  757. let key: number = 0;
  758. const keyNode: IXmlElement = node.element("key").element("fifths");
  759. if (keyNode !== undefined) {
  760. try {
  761. key = parseInt(keyNode.value, 10);
  762. } catch (ex) {
  763. errorMsg = ITextTranslation.translateText(
  764. "ReaderErrorMessages/KeyError",
  765. "Invalid key found -> set to default."
  766. );
  767. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  768. key = 0;
  769. log.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
  770. }
  771. }
  772. let keyEnum: KeyEnum = KeyEnum.none;
  773. let modeNode: IXmlElement = node.element("key");
  774. if (modeNode !== undefined) {
  775. modeNode = modeNode.element("mode");
  776. }
  777. if (modeNode !== undefined) {
  778. try {
  779. keyEnum = KeyEnum[modeNode.value];
  780. } catch (ex) {
  781. errorMsg = ITextTranslation.translateText(
  782. "ReaderErrorMessages/KeyError",
  783. "Invalid key found -> set to default."
  784. );
  785. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  786. keyEnum = KeyEnum.major;
  787. log.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
  788. }
  789. }
  790. const keyInstruction: KeyInstruction = new KeyInstruction(undefined, key, keyEnum);
  791. this.abstractInstructions.push([1, keyInstruction]);
  792. }
  793. if (node.element("time") !== undefined) {
  794. const timeNode: IXmlElement = node.element("time");
  795. let symbolEnum: RhythmSymbolEnum = RhythmSymbolEnum.NONE;
  796. let timePrintObject: boolean = true;
  797. if (timeNode !== undefined && timeNode.hasAttributes) {
  798. const symbolAttribute: IXmlAttribute = timeNode.attribute("symbol");
  799. if (symbolAttribute) {
  800. if (symbolAttribute.value === "common") {
  801. symbolEnum = RhythmSymbolEnum.COMMON;
  802. } else if (symbolAttribute.value === "cut") {
  803. symbolEnum = RhythmSymbolEnum.CUT;
  804. }
  805. }
  806. const printObjectAttribute: IXmlAttribute = timeNode.attribute("print-object");
  807. if (printObjectAttribute) {
  808. if (printObjectAttribute.value === "no") {
  809. timePrintObject = false;
  810. }
  811. }
  812. }
  813. let num: number = 0;
  814. let denom: number = 0;
  815. const senzaMisura: boolean = (timeNode !== undefined && timeNode.element("senza-misura") !== undefined);
  816. const timeList: IXmlElement[] = node.elements("time");
  817. const beatsList: IXmlElement[] = [];
  818. const typeList: IXmlElement[] = [];
  819. for (let idx: number = 0, len: number = timeList.length; idx < len; ++idx) {
  820. const xmlNode: IXmlElement = timeList[idx];
  821. beatsList.push.apply(beatsList, xmlNode.elements("beats"));
  822. typeList.push.apply(typeList, xmlNode.elements("beat-type"));
  823. }
  824. if (!senzaMisura) {
  825. try {
  826. if (beatsList !== undefined && beatsList.length > 0 && typeList !== undefined && beatsList.length === typeList.length) {
  827. const length: number = beatsList.length;
  828. const fractions: Fraction[] = new Array(length);
  829. let maxDenom: number = 0;
  830. for (let i: number = 0; i < length; i++) {
  831. const s: string = beatsList[i].value;
  832. let n: number = 0;
  833. let d: number = 0;
  834. if (s.indexOf("+") !== -1) {
  835. const numbers: string[] = s.split("+");
  836. for (let idx: number = 0, len: number = numbers.length; idx < len; ++idx) {
  837. n += parseInt(numbers[idx], 10);
  838. }
  839. } else {
  840. n = parseInt(s, 10);
  841. }
  842. d = parseInt(typeList[i].value, 10);
  843. maxDenom = Math.max(maxDenom, d);
  844. fractions[i] = new Fraction(n, d, 0, false);
  845. }
  846. for (let i: number = 0; i < length; i++) {
  847. if (fractions[i].Denominator === maxDenom) {
  848. num += fractions[i].Numerator;
  849. } else {
  850. num += (maxDenom / fractions[i].Denominator) * fractions[i].Numerator;
  851. }
  852. }
  853. denom = maxDenom;
  854. } else {
  855. num = parseInt(node.element("time").element("beats").value, 10);
  856. denom = parseInt(node.element("time").element("beat-type").value, 10);
  857. }
  858. } catch (ex) {
  859. errorMsg = ITextTranslation.translateText("ReaderErrorMessages/RhythmError", "Invalid rhythm found -> set to default.");
  860. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  861. num = 4;
  862. denom = 4;
  863. log.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
  864. }
  865. const newRhythmInstruction: RhythmInstruction = new RhythmInstruction(
  866. new Fraction(num, denom, 0, false), symbolEnum
  867. );
  868. newRhythmInstruction.PrintObject = timePrintObject;
  869. this.abstractInstructions.push([1, newRhythmInstruction]);
  870. } else {
  871. this.abstractInstructions.push([1, new RhythmInstruction(new Fraction(4, 4, 0, false), RhythmSymbolEnum.NONE)]);
  872. }
  873. }
  874. }
  875. /**
  876. * Save the current AbstractInstructions to the corresponding [[StaffEntry]]s.
  877. * @param numberOfStaves
  878. * @param beginOfMeasure
  879. */
  880. private saveAbstractInstructionList(numberOfStaves: number, beginOfMeasure: boolean): void {
  881. for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
  882. const pair: [number, AbstractNotationInstruction] = this.abstractInstructions[i];
  883. const key: number = pair[0];
  884. const value: AbstractNotationInstruction = pair[1];
  885. if (value instanceof ClefInstruction) {
  886. const clefInstruction: ClefInstruction = <ClefInstruction>value;
  887. if (this.currentXmlMeasureIndex === 0 || (key <= this.activeClefs.length && clefInstruction !== this.activeClefs[key - 1])) {
  888. if (!beginOfMeasure && this.currentStaffEntry !== undefined && !this.currentStaffEntry.hasNotes() && key - 1
  889. === this.instrument.Staves.indexOf(this.currentStaffEntry.ParentStaff)) {
  890. const newClefInstruction: ClefInstruction = clefInstruction;
  891. newClefInstruction.Parent = this.currentStaffEntry;
  892. this.currentStaffEntry.removeFirstInstructionOfTypeClefInstruction();
  893. this.currentStaffEntry.Instructions.push(newClefInstruction);
  894. this.activeClefs[key - 1] = clefInstruction;
  895. this.abstractInstructions.splice(i, 1);
  896. } else if (beginOfMeasure) {
  897. let firstStaffEntry: SourceStaffEntry;
  898. if (this.currentMeasure !== undefined) {
  899. const newClefInstruction: ClefInstruction = clefInstruction;
  900. const sseIndex: number = this.inSourceMeasureInstrumentIndex + key - 1;
  901. const firstSse: SourceStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[sseIndex];
  902. if (this.currentXmlMeasureIndex === 0) {
  903. if (firstSse === undefined) {
  904. firstStaffEntry = new SourceStaffEntry(undefined, undefined);
  905. this.currentMeasure.FirstInstructionsStaffEntries[sseIndex] = firstStaffEntry;
  906. newClefInstruction.Parent = firstStaffEntry;
  907. firstStaffEntry.Instructions.push(newClefInstruction);
  908. this.activeClefsHaveBeenInitialized[key - 1] = true;
  909. } else if (this.currentMeasure.FirstInstructionsStaffEntries[sseIndex]
  910. !==
  911. undefined && !(firstSse.Instructions[0] instanceof ClefInstruction)) {
  912. firstStaffEntry = firstSse;
  913. newClefInstruction.Parent = firstStaffEntry;
  914. firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
  915. firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
  916. this.activeClefsHaveBeenInitialized[key - 1] = true;
  917. } else {
  918. const lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
  919. this.currentMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
  920. newClefInstruction.Parent = lastStaffEntry;
  921. lastStaffEntry.Instructions.push(newClefInstruction);
  922. }
  923. } else if (!this.activeClefsHaveBeenInitialized[key - 1]) {
  924. const first: SourceMeasure = this.musicSheet.SourceMeasures[0];
  925. if (first.FirstInstructionsStaffEntries[sseIndex] === undefined) {
  926. firstStaffEntry = new SourceStaffEntry(undefined, undefined);
  927. } else {
  928. firstStaffEntry = first.FirstInstructionsStaffEntries[sseIndex];
  929. firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
  930. }
  931. newClefInstruction.Parent = firstStaffEntry;
  932. firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
  933. this.activeClefsHaveBeenInitialized[key - 1] = true;
  934. } else {
  935. const lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
  936. this.previousMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
  937. newClefInstruction.Parent = lastStaffEntry;
  938. lastStaffEntry.Instructions.push(newClefInstruction);
  939. }
  940. this.activeClefs[key - 1] = clefInstruction;
  941. this.abstractInstructions.splice(i, 1);
  942. }
  943. }
  944. } else if (key <= this.activeClefs.length && clefInstruction === this.activeClefs[key - 1]) {
  945. this.abstractInstructions.splice(i, 1);
  946. }
  947. }
  948. if (value instanceof KeyInstruction) {
  949. const keyInstruction: KeyInstruction = <KeyInstruction>value;
  950. if (this.activeKey === undefined || this.activeKey.Key !== keyInstruction.Key) {
  951. this.activeKey = keyInstruction;
  952. this.abstractInstructions.splice(i, 1);
  953. let sourceMeasure: SourceMeasure;
  954. if (!this.activeKeyHasBeenInitialized) {
  955. this.activeKeyHasBeenInitialized = true;
  956. if (this.currentXmlMeasureIndex > 0) {
  957. sourceMeasure = this.musicSheet.SourceMeasures[0];
  958. } else {
  959. sourceMeasure = this.currentMeasure;
  960. }
  961. } else {
  962. sourceMeasure = this.currentMeasure;
  963. }
  964. if (sourceMeasure !== undefined) {
  965. for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
  966. const newKeyInstruction: KeyInstruction = keyInstruction;
  967. if (sourceMeasure.FirstInstructionsStaffEntries[j] === undefined) {
  968. const firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
  969. sourceMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
  970. newKeyInstruction.Parent = firstStaffEntry;
  971. firstStaffEntry.Instructions.push(newKeyInstruction);
  972. } else {
  973. const firstStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[j];
  974. newKeyInstruction.Parent = firstStaffEntry;
  975. firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
  976. if (firstStaffEntry.Instructions.length === 0) {
  977. firstStaffEntry.Instructions.push(newKeyInstruction);
  978. } else {
  979. if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
  980. firstStaffEntry.Instructions.splice(1, 0, newKeyInstruction);
  981. } else {
  982. firstStaffEntry.Instructions.splice(0, 0, newKeyInstruction);
  983. }
  984. }
  985. }
  986. }
  987. }
  988. } else {
  989. this.abstractInstructions.splice(i, 1);
  990. }
  991. }
  992. if (value instanceof RhythmInstruction) {
  993. const rhythmInstruction: RhythmInstruction = <RhythmInstruction>value;
  994. if (this.activeRhythm === undefined || this.activeRhythm !== rhythmInstruction) {
  995. this.activeRhythm = rhythmInstruction;
  996. this.abstractInstructions.splice(i, 1);
  997. if (this.currentMeasure !== undefined) {
  998. for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
  999. const newRhythmInstruction: RhythmInstruction = rhythmInstruction;
  1000. let firstStaffEntry: SourceStaffEntry;
  1001. if (this.currentMeasure.FirstInstructionsStaffEntries[j] === undefined) {
  1002. firstStaffEntry = new SourceStaffEntry(undefined, undefined);
  1003. this.currentMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
  1004. } else {
  1005. firstStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[j];
  1006. firstStaffEntry.removeFirstInstructionOfTypeRhythmInstruction();
  1007. }
  1008. newRhythmInstruction.Parent = firstStaffEntry;
  1009. firstStaffEntry.Instructions.push(newRhythmInstruction);
  1010. }
  1011. }
  1012. } else {
  1013. this.abstractInstructions.splice(i, 1);
  1014. }
  1015. }
  1016. }
  1017. }
  1018. /**
  1019. * Save any ClefInstruction given - exceptionally - at the end of the currentMeasure.
  1020. */
  1021. private saveClefInstructionAtEndOfMeasure(): void {
  1022. for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
  1023. const key: number = this.abstractInstructions[i][0];
  1024. const value: AbstractNotationInstruction = this.abstractInstructions[i][1];
  1025. if (value instanceof ClefInstruction) {
  1026. const clefInstruction: ClefInstruction = <ClefInstruction>value;
  1027. if (
  1028. (this.activeClefs[key - 1] === undefined) ||
  1029. (clefInstruction.ClefType !== this.activeClefs[key - 1].ClefType || (
  1030. clefInstruction.ClefType === this.activeClefs[key - 1].ClefType &&
  1031. clefInstruction.Line !== this.activeClefs[key - 1].Line
  1032. ))) {
  1033. const lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
  1034. this.currentMeasure.LastInstructionsStaffEntries[this.inSourceMeasureInstrumentIndex + key - 1] = lastStaffEntry;
  1035. const newClefInstruction: ClefInstruction = clefInstruction;
  1036. newClefInstruction.Parent = lastStaffEntry;
  1037. lastStaffEntry.Instructions.push(newClefInstruction);
  1038. this.activeClefs[key - 1] = clefInstruction;
  1039. this.abstractInstructions.splice(i, 1);
  1040. }
  1041. }
  1042. }
  1043. }
  1044. /**
  1045. * In case of a [[Tuplet]], read NoteDuration from type.
  1046. * @param xmlNode
  1047. * @returns {Fraction}
  1048. */
  1049. private getNoteDurationForTuplet(xmlNode: IXmlElement): Fraction {
  1050. let duration: Fraction = new Fraction(0, 1);
  1051. const typeDuration: Fraction = this.getNoteDurationFromTypeNode(xmlNode);
  1052. if (xmlNode.element("time-modification") !== undefined) {
  1053. const time: IXmlElement = xmlNode.element("time-modification");
  1054. if (time !== undefined) {
  1055. if (time.element("actual-notes") !== undefined && time.element("normal-notes") !== undefined) {
  1056. const actualNotes: IXmlElement = time.element("actual-notes");
  1057. const normalNotes: IXmlElement = time.element("normal-notes");
  1058. if (actualNotes !== undefined && normalNotes !== undefined) {
  1059. const actual: number = parseInt(actualNotes.value, 10);
  1060. const normal: number = parseInt(normalNotes.value, 10);
  1061. duration = new Fraction(normal * typeDuration.Numerator, actual * typeDuration.Denominator);
  1062. }
  1063. }
  1064. }
  1065. }
  1066. return duration;
  1067. }
  1068. private readExpressionStaffNumber(xmlNode: IXmlElement): number {
  1069. let directionStaffNumber: number = 1;
  1070. if (xmlNode.element("staff") !== undefined) {
  1071. const staffNode: IXmlElement = xmlNode.element("staff");
  1072. if (staffNode !== undefined) {
  1073. try {
  1074. directionStaffNumber = parseInt(staffNode.value, 10);
  1075. } catch (ex) {
  1076. const errorMsg: string = ITextTranslation.translateText(
  1077. "ReaderErrorMessages/ExpressionStaffError", "Invalid Expression staff number -> set to default."
  1078. );
  1079. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  1080. directionStaffNumber = 1;
  1081. log.debug("InstrumentReader.readExpressionStaffNumber", errorMsg, ex);
  1082. }
  1083. }
  1084. }
  1085. return directionStaffNumber;
  1086. }
  1087. /**
  1088. * Calculate the divisions value from the type and duration of the first MeasureNote that makes sense
  1089. * (meaning itself hasn't any errors and it doesn't belong to a [[Tuplet]]).
  1090. *
  1091. * If all the MeasureNotes belong to a [[Tuplet]], then we read the next XmlMeasure (and so on...).
  1092. * If we have reached the end of the [[Instrument]] and still the divisions aren't set, we throw an exception
  1093. * @returns {number}
  1094. */
  1095. private readDivisionsFromNotes(): number {
  1096. let divisionsFromNote: number = 0;
  1097. let xmlMeasureIndex: number = this.currentXmlMeasureIndex;
  1098. let read: boolean = false;
  1099. while (!read) {
  1100. const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[xmlMeasureIndex].elements();
  1101. for (let idx: number = 0, len: number = xmlMeasureListArr.length; idx < len; ++idx) {
  1102. const xmlNode: IXmlElement = xmlMeasureListArr[idx];
  1103. if (xmlNode.name === "note" && xmlNode.element("time-modification") === undefined) {
  1104. const durationNode: IXmlElement = xmlNode.element("duration");
  1105. const typeNode: IXmlElement = xmlNode.element("type");
  1106. if (durationNode !== undefined && typeNode !== undefined) {
  1107. const type: string = typeNode.value;
  1108. let noteDuration: number = 0;
  1109. try {
  1110. noteDuration = parseInt(durationNode.value, 10);
  1111. } catch (ex) {
  1112. log.debug("InstrumentReader.readDivisionsFromNotes", ex);
  1113. continue;
  1114. }
  1115. switch (type) {
  1116. case "1024th":
  1117. divisionsFromNote = (noteDuration / 4) * 1024;
  1118. break;
  1119. case "512th":
  1120. divisionsFromNote = (noteDuration / 4) * 512;
  1121. break;
  1122. case "256th":
  1123. divisionsFromNote = (noteDuration / 4) * 256;
  1124. break;
  1125. case "128th":
  1126. divisionsFromNote = (noteDuration / 4) * 128;
  1127. break;
  1128. case "64th":
  1129. divisionsFromNote = (noteDuration / 4) * 64;
  1130. break;
  1131. case "32nd":
  1132. divisionsFromNote = (noteDuration / 4) * 32;
  1133. break;
  1134. case "16th":
  1135. divisionsFromNote = (noteDuration / 4) * 16;
  1136. break;
  1137. case "eighth":
  1138. divisionsFromNote = (noteDuration / 4) * 8;
  1139. break;
  1140. case "quarter":
  1141. divisionsFromNote = (noteDuration / 4) * 4;
  1142. break;
  1143. case "half":
  1144. divisionsFromNote = (noteDuration / 4) * 2;
  1145. break;
  1146. case "whole":
  1147. divisionsFromNote = (noteDuration / 4);
  1148. break;
  1149. case "breve":
  1150. divisionsFromNote = (noteDuration / 4) / 2;
  1151. break;
  1152. case "long":
  1153. divisionsFromNote = (noteDuration / 4) / 4;
  1154. break;
  1155. case "maxima":
  1156. divisionsFromNote = (noteDuration / 4) / 8;
  1157. break;
  1158. default:
  1159. break;
  1160. }
  1161. }
  1162. }
  1163. if (divisionsFromNote > 0) {
  1164. read = true;
  1165. break;
  1166. }
  1167. }
  1168. if (divisionsFromNote === 0) {
  1169. xmlMeasureIndex++;
  1170. if (xmlMeasureIndex === this.xmlMeasureList.length) {
  1171. const errorMsg: string = ITextTranslation.translateText("ReaderErrorMEssages/DivisionsError", "Invalid divisions value at Instrument: ");
  1172. throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
  1173. }
  1174. }
  1175. }
  1176. return divisionsFromNote;
  1177. }
  1178. }