InstrumentReader.ts 50 KB

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