InstrumentReader.ts 49 KB

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