MusicSheetReader.ts 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. import {MusicSheet} from "../MusicSheet";
  2. import {SourceMeasure} from "../VoiceData/SourceMeasure";
  3. import {Fraction} from "../../Common/DataObjects/fraction";
  4. import {InstrumentReader} from "./InstrumentReader";
  5. import {IXmlElement} from "../../Common/FileIO/Xml";
  6. import {Instrument} from "../Instrument";
  7. import {ITextTranslation} from "../Interfaces/ITextTranslation";
  8. import {MusicSheetReadingException} from "../Exceptions";
  9. import {Logging} from "../../Common/logging";
  10. import {IXmlAttribute} from "../../Common/FileIO/Xml";
  11. import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
  12. import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
  13. import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
  14. import {VoiceEntry} from "../VoiceData/VoiceEntry";
  15. import {InstrumentalGroup} from "../InstrumentalGroup";
  16. import {SubInstrument} from "../SubInstrument";
  17. import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
  18. import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
  19. import {Label} from "../Label";
  20. type RepetitionInstructionReader = any;
  21. type RepetitionCalculator = any;
  22. export class MusicSheetReader /*implements IMusicSheetReader*/ {
  23. //constructor(afterSheetReadingModules: IAfterSheetReadingModule[]) {
  24. // if (afterSheetReadingModules === undefined) {
  25. // this.afterSheetReadingModules = [];
  26. // } else {
  27. // this.afterSheetReadingModules = afterSheetReadingModules;
  28. // }
  29. // this.repetitionInstructionReader = MusicSymbolModuleFactory.createRepetitionInstructionReader();
  30. // this.repetitionCalculator = MusicSymbolModuleFactory.createRepetitionCalculator();
  31. //}
  32. private repetitionInstructionReader: RepetitionInstructionReader;
  33. private repetitionCalculator: RepetitionCalculator;
  34. // private afterSheetReadingModules: IAfterSheetReadingModule[];
  35. private musicSheet: MusicSheet;
  36. private completeNumberOfStaves: number = 0;
  37. private currentMeasure: SourceMeasure;
  38. private previousMeasure: SourceMeasure;
  39. private currentFraction: Fraction;
  40. public get CompleteNumberOfStaves(): number {
  41. return this.completeNumberOfStaves;
  42. }
  43. private static doCalculationsAfterDurationHasBeenSet(instrumentReaders: InstrumentReader[]): void {
  44. for (let instrumentReader of instrumentReaders) {
  45. instrumentReader.doCalculationsAfterDurationHasBeenSet();
  46. }
  47. }
  48. public createMusicSheet(root: IXmlElement, path: string): MusicSheet {
  49. try {
  50. return this._createMusicSheet(root, path);
  51. } catch (e) {
  52. Logging.log("MusicSheetReader.CreateMusicSheet", e);
  53. }
  54. }
  55. private _removeFromArray(list: any[], elem: any): void {
  56. let i: number = list.indexOf(elem);
  57. if (i !== -1) {
  58. list.splice(i, 1);
  59. }
  60. }
  61. // Trim from a string also newlines
  62. private trimString(str: string): string {
  63. return str.replace(/^\s+|\s+$/g, "");
  64. }
  65. private _lastElement<T>(list: T[]): T {
  66. return list[list.length - 1];
  67. }
  68. //public SetPhonicScoreInterface(phonicScoreInterface: IPhonicScoreInterface): void {
  69. // this.phonicScoreInterface = phonicScoreInterface;
  70. //}
  71. //public ReadMusicSheetParameters(sheetObject: MusicSheetParameterObject, root: IXmlElement, path: string): MusicSheetParameterObject {
  72. // this.musicSheet = new MusicSheet();
  73. // if (root !== undefined) {
  74. // this.pushSheetLabels(root, path);
  75. // if (this.musicSheet.Title !== undefined) {
  76. // sheetObject.Title = this.musicSheet.Title.text;
  77. // }
  78. // if (this.musicSheet.Composer !== undefined) {
  79. // sheetObject.Composer = this.musicSheet.Composer.text;
  80. // }
  81. // if (this.musicSheet.Lyricist !== undefined) {
  82. // sheetObject.Lyricist = this.musicSheet.Lyricist.text;
  83. // }
  84. // let partlistNode: IXmlElement = root.element("part-list");
  85. // let partList: IXmlElement[] = partlistNode.elements();
  86. // this.createInstrumentGroups(partList);
  87. // for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
  88. // let instr: Instrument = this.musicSheet.Instruments[idx];
  89. // sheetObject.InstrumentList.push(__init(new MusicSheetParameterObject.LibrarySheetInstrument(), { name: instr.name }));
  90. // }
  91. // }
  92. // return sheetObject;
  93. //}
  94. private _createMusicSheet(root: IXmlElement, path: string): MusicSheet {
  95. let instrumentReaders: InstrumentReader[] = [];
  96. let sourceMeasureCounter: number = 0;
  97. this.musicSheet = new MusicSheet();
  98. this.musicSheet.Path = path;
  99. if (root === undefined) {
  100. throw new MusicSheetReadingException("Undefined root element");
  101. }
  102. this.pushSheetLabels(root, path);
  103. let partlistNode: IXmlElement = root.element("part-list");
  104. if (partlistNode === undefined) {
  105. throw new MusicSheetReadingException("Undefined partListNode");
  106. }
  107. let partInst: IXmlElement[] = root.elements("part");
  108. console.log(partInst.length + " parts");
  109. let partList: IXmlElement[] = partlistNode.elements();
  110. //Logging.debug("Starting initializeReading");
  111. this.initializeReading(partList, partInst, instrumentReaders);
  112. //Logging.debug("Done initializeReading");
  113. let couldReadMeasure: boolean = true;
  114. this.currentFraction = new Fraction(0, 1);
  115. let guitarPro: boolean = false;
  116. let encoding: IXmlElement = root.element("identification");
  117. if (encoding !== undefined) {
  118. encoding = encoding.element("encoding");
  119. }
  120. if (encoding !== undefined) {
  121. encoding = encoding.element("software");
  122. }
  123. if (encoding !== undefined && encoding.value === "Guitar Pro 5") {
  124. guitarPro = true;
  125. }
  126. while (couldReadMeasure) {
  127. if (this.currentMeasure !== undefined && this.currentMeasure.endsPiece) {
  128. sourceMeasureCounter = 0;
  129. }
  130. this.currentMeasure = new SourceMeasure(this.completeNumberOfStaves);
  131. for (let instrumentReader of instrumentReaders) {
  132. try {
  133. couldReadMeasure = couldReadMeasure && instrumentReader.readNextXmlMeasure(this.currentMeasure, this.currentFraction, guitarPro);
  134. } catch (e) {
  135. let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/InstrumentError", "Error while reading instruments.");
  136. throw new MusicSheetReadingException(errorMsg, e);
  137. }
  138. }
  139. if (couldReadMeasure) {
  140. //Logging.debug("couldReadMeasure: 1");
  141. this.musicSheet.addMeasure(this.currentMeasure);
  142. //Logging.debug("couldReadMeasure: 2");
  143. this.checkIfRhythmInstructionsAreSetAndEqual(instrumentReaders);
  144. //Logging.debug("couldReadMeasure: 3");
  145. this.checkSourceMeasureForundefinedEntries();
  146. //Logging.debug("couldReadMeasure: 4");
  147. this.setSourceMeasureDuration(instrumentReaders, sourceMeasureCounter);
  148. //Logging.debug("couldReadMeasure: 5");
  149. MusicSheetReader.doCalculationsAfterDurationHasBeenSet(instrumentReaders);
  150. //Logging.debug("couldReadMeasure: 6");
  151. this.currentMeasure.AbsoluteTimestamp = this.currentFraction.clone();
  152. this.musicSheet.SheetErrors.finalizeMeasure(this.currentMeasure.MeasureNumber);
  153. this.currentFraction.Add(this.currentMeasure.Duration);
  154. this.previousMeasure = this.currentMeasure;
  155. //Logging.debug("couldReadMeasure: 7");
  156. }
  157. }
  158. if (this.repetitionInstructionReader !== undefined) {
  159. this.repetitionInstructionReader.removeRedundantInstructions();
  160. if (this.repetitionCalculator !== undefined) {
  161. this.repetitionCalculator.calculateRepetitions(this.musicSheet, this.repetitionInstructionReader.RepetitionInstructions);
  162. }
  163. }
  164. this.musicSheet.checkForInstrumentWithNoVoice();
  165. this.musicSheet.fillStaffList();
  166. //this.musicSheet.DefaultStartTempoInBpm = this.musicSheet.SheetPlaybackSetting.BeatsPerMinute;
  167. //for (let idx: number = 0, len: number = this.afterSheetReadingModules.length; idx < len; ++idx) {
  168. // let afterSheetReadingModule: IAfterSheetReadingModule = this.afterSheetReadingModules[idx];
  169. // afterSheetReadingModule.calculate(this.musicSheet);
  170. //}
  171. return this.musicSheet;
  172. }
  173. private initializeReading(partList: IXmlElement[], partInst: IXmlElement[], instrumentReaders: InstrumentReader[]): void {
  174. let instrumentDict: { [_: string]: Instrument; } = this.createInstrumentGroups(partList);
  175. this.completeNumberOfStaves = this.getCompleteNumberOfStavesFromXml(partInst);
  176. if (partInst.length !== 0) {
  177. // (*) this.repetitionInstructionReader.MusicSheet = this.musicSheet;
  178. this.currentFraction = new Fraction(0, 1);
  179. this.currentMeasure = undefined;
  180. this.previousMeasure = undefined;
  181. }
  182. let counter: number = 0;
  183. for (let node of partInst) {
  184. let idNode: IXmlAttribute = node.attribute("id");
  185. if (idNode) {
  186. let currentInstrument: Instrument = instrumentDict[idNode.value];
  187. let xmlMeasureList: IXmlElement[] = node.elements("measure");
  188. let instrumentNumberOfStaves: number = 1;
  189. try {
  190. instrumentNumberOfStaves = this.getInstrumentNumberOfStavesFromXml(node);
  191. } catch (err) {
  192. let errorMsg: string = ITextTranslation.translateText(
  193. "ReaderErrorMessages/InstrumentStavesNumberError",
  194. "Invalid number of staves at instrument: "
  195. );
  196. this.musicSheet.SheetErrors.push(errorMsg + currentInstrument.Name);
  197. continue;
  198. }
  199. currentInstrument.createStaves(instrumentNumberOfStaves);
  200. instrumentReaders.push(new InstrumentReader(this.repetitionInstructionReader, xmlMeasureList, currentInstrument));
  201. if (this.repetitionInstructionReader !== undefined) {
  202. this.repetitionInstructionReader.XmlMeasureList[counter] = xmlMeasureList;
  203. }
  204. counter++;
  205. }
  206. }
  207. }
  208. private checkIfRhythmInstructionsAreSetAndEqual(instrumentReaders: InstrumentReader[]): void {
  209. let rhythmInstructions: RhythmInstruction[] = [];
  210. for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
  211. if (this.currentMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
  212. let last: AbstractNotationInstruction = this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions[
  213. this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.length - 1
  214. ];
  215. if (last instanceof RhythmInstruction) {
  216. rhythmInstructions.push(<RhythmInstruction>last);
  217. }
  218. }
  219. }
  220. let maxRhythmValue: number = 0.0;
  221. let index: number = -1;
  222. for (let idx: number = 0, len: number = rhythmInstructions.length; idx < len; ++idx) {
  223. let rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
  224. if (rhythmInstruction.Rhythm.RealValue > maxRhythmValue) {
  225. if (this.areRhythmInstructionsMixed(rhythmInstructions) && rhythmInstruction.SymbolEnum !== RhythmSymbolEnum.NONE) {
  226. continue;
  227. }
  228. maxRhythmValue = rhythmInstruction.Rhythm.RealValue;
  229. index = rhythmInstructions.indexOf(rhythmInstruction);
  230. }
  231. }
  232. if (rhythmInstructions.length > 0 && rhythmInstructions.length < this.completeNumberOfStaves) {
  233. let rhythmInstruction: RhythmInstruction = rhythmInstructions[index].clone();
  234. for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
  235. if (
  236. this.currentMeasure.FirstInstructionsStaffEntries[i] !== undefined &&
  237. !(this._lastElement(this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions) instanceof RhythmInstruction)
  238. ) {
  239. this.currentMeasure.FirstInstructionsStaffEntries[i].removeAllInstructionsOfTypeRhythmInstruction();
  240. this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction.clone());
  241. }
  242. if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
  243. this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
  244. this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction.clone());
  245. }
  246. }
  247. for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
  248. let instrumentReader: InstrumentReader = instrumentReaders[idx];
  249. instrumentReader.ActiveRhythm = rhythmInstruction;
  250. }
  251. }
  252. if (rhythmInstructions.length === 0 && this.currentMeasure === this.musicSheet.SourceMeasures[0]) {
  253. let rhythmInstruction: RhythmInstruction = new RhythmInstruction(new Fraction(4, 4, false), 4, 4, RhythmSymbolEnum.NONE);
  254. for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
  255. if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
  256. this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
  257. } else {
  258. this.currentMeasure.FirstInstructionsStaffEntries[i].removeAllInstructionsOfTypeRhythmInstruction();
  259. }
  260. this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction);
  261. }
  262. for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
  263. let instrumentReader: InstrumentReader = instrumentReaders[idx];
  264. instrumentReader.ActiveRhythm = rhythmInstruction;
  265. }
  266. }
  267. for (let idx: number = 0, len: number = rhythmInstructions.length; idx < len; ++idx) {
  268. let rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
  269. if (rhythmInstruction.Rhythm.RealValue < maxRhythmValue) {
  270. if (this._lastElement(
  271. this.currentMeasure.FirstInstructionsStaffEntries[rhythmInstructions.indexOf(rhythmInstruction)].Instructions
  272. ) instanceof RhythmInstruction) {
  273. // TODO Test correctness
  274. let instrs: AbstractNotationInstruction[] =
  275. this.currentMeasure.FirstInstructionsStaffEntries[rhythmInstructions.indexOf(rhythmInstruction)].Instructions;
  276. instrs[instrs.length - 1] = rhythmInstructions[index].clone();
  277. }
  278. }
  279. if (
  280. Math.abs(rhythmInstruction.Rhythm.RealValue - maxRhythmValue) < 0.000001 &&
  281. rhythmInstruction.SymbolEnum !== RhythmSymbolEnum.NONE &&
  282. this.areRhythmInstructionsMixed(rhythmInstructions)
  283. ) {
  284. rhythmInstruction.SymbolEnum = RhythmSymbolEnum.NONE;
  285. }
  286. }
  287. }
  288. private areRhythmInstructionsMixed(rhythmInstructions: RhythmInstruction[]): boolean {
  289. for (let i: number = 1; i < rhythmInstructions.length; i++) {
  290. if (
  291. Math.abs(rhythmInstructions[i].Rhythm.RealValue - rhythmInstructions[0].Rhythm.RealValue) < 0.000001 &&
  292. rhythmInstructions[i].SymbolEnum !== rhythmInstructions[0].SymbolEnum
  293. ) {
  294. return true;
  295. }
  296. }
  297. return false;
  298. }
  299. private setSourceMeasureDuration(instrumentReaders: InstrumentReader[], sourceMeasureCounter: number): void {
  300. let activeRhythm: Fraction = new Fraction(0, 1);
  301. let instrumentsMaxTieNoteFractions: Fraction[] = [];
  302. for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
  303. let instrumentReader: InstrumentReader = instrumentReaders[idx];
  304. instrumentsMaxTieNoteFractions.push(instrumentReader.MaxTieNoteFraction);
  305. let activeRythmMeasure: Fraction = instrumentReader.ActiveRhythm.Rhythm;
  306. if (activeRhythm < activeRythmMeasure) {
  307. activeRhythm = new Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, false);
  308. }
  309. }
  310. let instrumentsDurations: Fraction[] = this.currentMeasure.calculateInstrumentsDuration(this.musicSheet, instrumentsMaxTieNoteFractions);
  311. let maxInstrumentDuration: Fraction = new Fraction(0, 1);
  312. for (let idx: number = 0, len: number = instrumentsDurations.length; idx < len; ++idx) {
  313. let instrumentsDuration: Fraction = instrumentsDurations[idx];
  314. if (maxInstrumentDuration < instrumentsDuration) {
  315. maxInstrumentDuration = instrumentsDuration;
  316. }
  317. }
  318. if (Fraction.Equal(maxInstrumentDuration, activeRhythm)) {
  319. this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
  320. } else {
  321. if (maxInstrumentDuration < activeRhythm) {
  322. maxInstrumentDuration = this.currentMeasure.reverseCheck(this.musicSheet, maxInstrumentDuration);
  323. this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
  324. }
  325. }
  326. this.currentMeasure.ImplicitMeasure = this.checkIfMeasureIsImplicit(maxInstrumentDuration, activeRhythm);
  327. if (!this.currentMeasure.ImplicitMeasure) {
  328. sourceMeasureCounter++;
  329. }
  330. this.currentMeasure.Duration = maxInstrumentDuration;
  331. this.currentMeasure.MeasureNumber = sourceMeasureCounter;
  332. for (let i: number = 0; i < instrumentsDurations.length; i++) {
  333. let instrumentsDuration: Fraction = instrumentsDurations[i];
  334. if (
  335. (this.currentMeasure.ImplicitMeasure && instrumentsDuration !== maxInstrumentDuration) ||
  336. instrumentsDuration !== activeRhythm && // FIXME
  337. !this.allInstrumentsHaveSameDuration(instrumentsDurations, maxInstrumentDuration)
  338. ) {
  339. let firstStaffIndexOfInstrument: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.musicSheet.Instruments[i]);
  340. for (let staffIndex: number = 0; staffIndex < this.musicSheet.Instruments[i].Staves.length; staffIndex++) {
  341. if (!this.staffMeasureIsEmpty(firstStaffIndexOfInstrument + staffIndex)) {
  342. this.currentMeasure.setErrorInStaffMeasure(firstStaffIndexOfInstrument + staffIndex, true);
  343. let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MissingNotesError",
  344. "Given Notes don't correspond to measure duration.");
  345. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  346. }
  347. }
  348. }
  349. }
  350. }
  351. private checkFractionsForEquivalence(maxInstrumentDuration: Fraction, activeRhythm: Fraction): void {
  352. if (activeRhythm.Denominator > maxInstrumentDuration.Denominator) {
  353. let factor: number = activeRhythm.Denominator / maxInstrumentDuration.Denominator;
  354. maxInstrumentDuration.multiplyWithFactor(factor);
  355. }
  356. }
  357. private checkIfMeasureIsImplicit(maxInstrumentDuration: Fraction, activeRhythm: Fraction): boolean {
  358. if (this.previousMeasure === undefined && maxInstrumentDuration < activeRhythm) {
  359. return true;
  360. }
  361. if (this.previousMeasure !== undefined) {
  362. return Fraction.plus(this.previousMeasure.Duration, maxInstrumentDuration).CompareTo(activeRhythm) === 0;
  363. }
  364. return false;
  365. }
  366. private allInstrumentsHaveSameDuration(instrumentsDurations: Fraction[], maxInstrumentDuration: Fraction): boolean {
  367. let counter: number = 0;
  368. for (let idx: number = 0, len: number = instrumentsDurations.length; idx < len; ++idx) {
  369. let instrumentsDuration: Fraction = instrumentsDurations[idx];
  370. if (instrumentsDuration === maxInstrumentDuration) {
  371. counter++;
  372. }
  373. }
  374. return (counter === instrumentsDurations.length && maxInstrumentDuration !== new Fraction(0, 1));
  375. }
  376. private staffMeasureIsEmpty(index: number): boolean {
  377. let counter: number = 0;
  378. for (let i: number = 0; i < this.currentMeasure.VerticalSourceStaffEntryContainers.length; i++) {
  379. if (this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[index] === undefined) {
  380. counter++;
  381. }
  382. }
  383. return (counter === this.currentMeasure.VerticalSourceStaffEntryContainers.length);
  384. }
  385. private checkSourceMeasureForundefinedEntries(): void {
  386. for (let i: number = this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
  387. for (let j: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length - 1; j >= 0; j--) {
  388. let sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[j];
  389. if (sourceStaffEntry !== undefined) {
  390. for (let k: number = sourceStaffEntry.VoiceEntries.length - 1; k >= 0; k--) {
  391. let voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[k];
  392. if (voiceEntry.Notes.length === 0) {
  393. this._removeFromArray(voiceEntry.ParentVoice.VoiceEntries, voiceEntry);
  394. this._removeFromArray(sourceStaffEntry.VoiceEntries, voiceEntry);
  395. }
  396. }
  397. }
  398. if (sourceStaffEntry !== undefined && sourceStaffEntry.VoiceEntries.length === 0) {
  399. this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[j] = undefined;
  400. }
  401. }
  402. }
  403. for (let i: number = this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
  404. let counter: number = 0;
  405. for (let idx: number = 0, len: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length; idx < len; ++idx) {
  406. let sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[idx];
  407. if (sourceStaffEntry === undefined) {
  408. counter++;
  409. }
  410. }
  411. if (counter === this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length) {
  412. this._removeFromArray(this.currentMeasure.VerticalSourceStaffEntryContainers, this.currentMeasure.VerticalSourceStaffEntryContainers[i]);
  413. }
  414. }
  415. }
  416. private pushSheetLabels(root: IXmlElement, filePath: string): void {
  417. this.readComposer(root);
  418. this.readTitle(root);
  419. if (this.musicSheet.Title === undefined || this.musicSheet.Composer === undefined) {
  420. this.readTitleAndComposerFromCredits(root);
  421. }
  422. if (this.musicSheet.Title === undefined) {
  423. try {
  424. let barI: number = Math.max(
  425. 0, filePath.lastIndexOf("/"), filePath.lastIndexOf("\\")
  426. );
  427. let filename: string = filePath.substr(barI);
  428. let filenameSplits: string[] = filename.split(".", 1);
  429. this.musicSheet.Title = new Label(filenameSplits[0]);
  430. } catch (ex) {
  431. Logging.log("MusicSheetReader.pushSheetLabels: ", ex);
  432. }
  433. }
  434. }
  435. // Checks whether _elem_ has an attribute with value _val_.
  436. private presentAttrsWithValue(elem: IXmlElement, val: string): boolean {
  437. for (let attr of elem.attributes()) {
  438. if (attr.value === val) {
  439. return true;
  440. }
  441. }
  442. return false;
  443. }
  444. private readComposer(root: IXmlElement): void {
  445. let identificationNode: IXmlElement = root.element("identification");
  446. if (identificationNode !== undefined) {
  447. let creators: IXmlElement[] = identificationNode.elements("creator");
  448. for (let idx: number = 0, len: number = creators.length; idx < len; ++idx) {
  449. let creator: IXmlElement = creators[idx];
  450. if (creator.hasAttributes) {
  451. if (this.presentAttrsWithValue(creator, "composer")) {
  452. this.musicSheet.Composer = new Label(this.trimString(creator.value));
  453. continue;
  454. }
  455. if (this.presentAttrsWithValue(creator, "lyricist") || this.presentAttrsWithValue(creator, "poet")) {
  456. this.musicSheet.Lyricist = new Label(this.trimString(creator.value));
  457. }
  458. }
  459. }
  460. }
  461. }
  462. private readTitleAndComposerFromCredits(root: IXmlElement): void {
  463. let systemYCoordinates: number = this.computeSystemYCoordinates(root);
  464. if (systemYCoordinates === 0) {
  465. return;
  466. }
  467. let largestTitleCreditSize: number = 1;
  468. let finalTitle: string = undefined;
  469. let largestCreditYInfo: number = 0;
  470. let finalSubtitle: string = undefined;
  471. let possibleTitle: string = undefined;
  472. let creditElements: IXmlElement[] = root.elements("credit");
  473. for (let idx: number = 0, len: number = creditElements.length; idx < len; ++idx) {
  474. let credit: IXmlElement = creditElements[idx];
  475. if (!credit.attribute("page")) {
  476. return;
  477. }
  478. if (credit.attribute("page").value === "1") {
  479. let creditChild: IXmlElement = undefined;
  480. if (credit !== undefined) {
  481. creditChild = credit.element("credit-words");
  482. if (!creditChild.attribute("justify")) {
  483. break;
  484. }
  485. let creditJustify: string = creditChild.attribute("justify").value;
  486. let creditY: string = creditChild.attribute("default-y").value;
  487. let creditYInfo: number = parseFloat(creditY);
  488. if (creditYInfo > systemYCoordinates) {
  489. if (this.musicSheet.Title === undefined) {
  490. let creditSize: string = creditChild.attribute("font-size").value;
  491. let titleCreditSizeInt: number = parseFloat(creditSize);
  492. if (largestTitleCreditSize < titleCreditSizeInt) {
  493. largestTitleCreditSize = titleCreditSizeInt;
  494. finalTitle = creditChild.value;
  495. }
  496. }
  497. if (this.musicSheet.Subtitle === undefined) {
  498. if (creditJustify !== "right" && creditJustify !== "left") {
  499. if (largestCreditYInfo < creditYInfo) {
  500. largestCreditYInfo = creditYInfo;
  501. if (possibleTitle) {
  502. finalSubtitle = possibleTitle;
  503. possibleTitle = creditChild.value;
  504. } else {
  505. possibleTitle = creditChild.value;
  506. }
  507. }
  508. }
  509. }
  510. if (!(this.musicSheet.Composer !== undefined && this.musicSheet.Lyricist !== undefined)) {
  511. switch (creditJustify) {
  512. case "right":
  513. this.musicSheet.Composer = new Label(this.trimString(creditChild.value));
  514. break;
  515. case "left":
  516. this.musicSheet.Lyricist = new Label(this.trimString(creditChild.value));
  517. break;
  518. default:
  519. break;
  520. }
  521. }
  522. }
  523. }
  524. }
  525. }
  526. if (this.musicSheet.Title === undefined && finalTitle) {
  527. this.musicSheet.Title = new Label(this.trimString(finalTitle));
  528. }
  529. if (this.musicSheet.Subtitle === undefined && finalSubtitle) {
  530. this.musicSheet.Subtitle = new Label(this.trimString(finalSubtitle));
  531. }
  532. }
  533. private computeSystemYCoordinates(root: IXmlElement): number {
  534. if (root.element("defaults") === undefined) {
  535. return 0;
  536. }
  537. let paperHeight: number = 0;
  538. let topSystemDistance: number = 0;
  539. let defi: string = root.element("defaults").element("page-layout").element("page-height").value;
  540. paperHeight = parseFloat(defi);
  541. let found: boolean = false;
  542. let parts: IXmlElement[] = root.elements("part");
  543. for (let idx: number = 0, len: number = parts.length; idx < len; ++idx) {
  544. let measures: IXmlElement[] = parts[idx].elements("measure");
  545. for (let idx2: number = 0, len2: number = measures.length; idx2 < len2; ++idx2) {
  546. let measure: IXmlElement = measures[idx2];
  547. if (measure.element("print") !== undefined) {
  548. let systemLayouts: IXmlElement[] = measure.element("print").elements("system-layout");
  549. for (let idx3: number = 0, len3: number = systemLayouts.length; idx3 < len3; ++idx3) {
  550. let syslab: IXmlElement = systemLayouts[idx3];
  551. if (syslab.element("top-system-distance") !== undefined) {
  552. let topSystemDistanceString: string = syslab.element("top-system-distance").value;
  553. topSystemDistance = parseFloat(topSystemDistanceString);
  554. found = true;
  555. break;
  556. }
  557. }
  558. break;
  559. }
  560. }
  561. if (found) {
  562. break;
  563. }
  564. }
  565. if (root.element("defaults").element("system-layout") !== undefined) {
  566. let syslay: IXmlElement = root.element("defaults").element("system-layout");
  567. if (syslay.element("top-system-distance") !== undefined) {
  568. let topSystemDistanceString: string = root.element("defaults").element("system-layout").element("top-system-distance").value;
  569. topSystemDistance = parseFloat(topSystemDistanceString);
  570. }
  571. }
  572. if (topSystemDistance === 0) {
  573. return 0;
  574. }
  575. return paperHeight - topSystemDistance;
  576. }
  577. private readTitle(root: IXmlElement): void {
  578. let titleNode: IXmlElement = root.element("work");
  579. let titleNodeChild: IXmlElement = undefined;
  580. if (titleNode !== undefined) {
  581. titleNodeChild = titleNode.element("work-title");
  582. if (titleNodeChild !== undefined && titleNodeChild.value) {
  583. this.musicSheet.Title = new Label(this.trimString(titleNodeChild.value));
  584. }
  585. }
  586. let movementNode: IXmlElement = root.element("movement-title");
  587. let finalSubTitle: string = "";
  588. if (movementNode !== undefined) {
  589. if (this.musicSheet.Title === undefined) {
  590. this.musicSheet.Title = new Label(this.trimString(movementNode.value));
  591. } else {
  592. finalSubTitle = this.trimString(movementNode.value);
  593. }
  594. }
  595. if (titleNode !== undefined) {
  596. let subtitleNodeChild: IXmlElement = titleNode.element("work-number");
  597. if (subtitleNodeChild !== undefined) {
  598. let workNumber: string = subtitleNodeChild.value;
  599. if (workNumber) {
  600. if (finalSubTitle) {
  601. finalSubTitle = workNumber;
  602. } else {
  603. finalSubTitle = finalSubTitle + ", " + workNumber;
  604. }
  605. }
  606. }
  607. }
  608. if (finalSubTitle
  609. ) {
  610. this.musicSheet.Subtitle = new Label(finalSubTitle);
  611. }
  612. }
  613. private createInstrumentGroups(entryList: IXmlElement[]): { [_: string]: Instrument; } {
  614. let instrumentId: number = 0;
  615. let instrumentDict: { [_: string]: Instrument; } = {};
  616. let currentGroup: InstrumentalGroup;
  617. try {
  618. let entryArray: IXmlElement[] = entryList;
  619. for (let idx: number = 0, len: number = entryArray.length; idx < len; ++idx) {
  620. let node: IXmlElement = entryArray[idx];
  621. if (node.name === "score-part") {
  622. let instrIdString: string = node.attribute("id").value;
  623. let instrument: Instrument = new Instrument(instrumentId, instrIdString, this.musicSheet, currentGroup);
  624. instrumentId++;
  625. let partElements: IXmlElement[] = node.elements();
  626. for (let idx2: number = 0, len2: number = partElements.length; idx2 < len2; ++idx2) {
  627. let partElement: IXmlElement = partElements[idx2];
  628. try {
  629. if (partElement.name === "part-name") {
  630. instrument.Name = partElement.value;
  631. } else if (partElement.name === "score-instrument") {
  632. let subInstrument: SubInstrument = new SubInstrument(instrument);
  633. subInstrument.idString = partElement.firstAttribute.value;
  634. instrument.SubInstruments.push(subInstrument);
  635. let subElement: IXmlElement = partElement.element("instrument-name");
  636. if (subElement !== undefined) {
  637. subInstrument.name = subElement.value;
  638. subInstrument.setMidiInstrument(subElement.value);
  639. }
  640. } else if (partElement.name === "midi-instrument") {
  641. let subInstrument: SubInstrument = instrument.getSubInstrument(partElement.firstAttribute.value);
  642. for (let idx3: number = 0, len3: number = instrument.SubInstruments.length; idx3 < len3; ++idx3) {
  643. let subInstr: SubInstrument = instrument.SubInstruments[idx3];
  644. if (subInstr.idString === partElement.value) {
  645. subInstrument = subInstr;
  646. break;
  647. }
  648. }
  649. let instrumentElements: IXmlElement[] = partElement.elements();
  650. for (let idx3: number = 0, len3: number = instrumentElements.length; idx3 < len3; ++idx3) {
  651. let instrumentElement: IXmlElement = instrumentElements[idx3];
  652. try {
  653. if (instrumentElement.name === "midi-channel") {
  654. if (parseInt(instrumentElement.value, 10) === 10) {
  655. instrument.MidiInstrumentId = MidiInstrument.Percussion;
  656. }
  657. } else if (instrumentElement.name === "midi-program") {
  658. if (instrument.SubInstruments.length > 0 && instrument.MidiInstrumentId !== MidiInstrument.Percussion) {
  659. subInstrument.midiInstrumentID = <MidiInstrument>Math.max(0, parseInt(instrumentElement.value, 10) - 1);
  660. }
  661. } else if (instrumentElement.name === "midi-unpitched") {
  662. subInstrument.fixedKey = Math.max(0, parseInt(instrumentElement.value, 10));
  663. } else if (instrumentElement.name === "volume") {
  664. try {
  665. let result: number = <number>parseFloat(instrumentElement.value);
  666. subInstrument.volume = result / 127.0;
  667. } catch (ex) {
  668. Logging.debug("ExpressionReader.readExpressionParameters", "read volume", ex);
  669. }
  670. } else if (instrumentElement.name === "pan") {
  671. try {
  672. let result: number = <number>parseFloat(instrumentElement.value);
  673. subInstrument.pan = result / 64.0;
  674. } catch (ex) {
  675. Logging.debug("ExpressionReader.readExpressionParameters", "read pan", ex);
  676. }
  677. }
  678. } catch (ex) {
  679. Logging.log("MusicSheetReader.createInstrumentGroups midi settings: ", ex);
  680. }
  681. }
  682. }
  683. } catch (ex) {
  684. Logging.log("MusicSheetReader.createInstrumentGroups: ", ex);
  685. }
  686. }
  687. if (instrument.SubInstruments.length === 0) {
  688. let subInstrument: SubInstrument = new SubInstrument(instrument);
  689. instrument.SubInstruments.push(subInstrument);
  690. }
  691. instrumentDict[instrIdString] = instrument;
  692. if (currentGroup !== undefined) {
  693. currentGroup.InstrumentalGroups.push(instrument);
  694. this.musicSheet.Instruments.push(instrument);
  695. } else {
  696. this.musicSheet.InstrumentalGroups.push(instrument);
  697. this.musicSheet.Instruments.push(instrument);
  698. }
  699. } else {
  700. if ((node.name === "part-group") && (node.attribute("type").value === "start")) {
  701. let iG: InstrumentalGroup = new InstrumentalGroup("group", this.musicSheet, currentGroup);
  702. if (currentGroup !== undefined) {
  703. currentGroup.InstrumentalGroups.push(iG);
  704. } else {
  705. this.musicSheet.InstrumentalGroups.push(iG);
  706. }
  707. currentGroup = iG;
  708. } else {
  709. if ((node.name === "part-group") && (node.attribute("type").value === "stop")) {
  710. if (currentGroup !== undefined) {
  711. if (currentGroup.InstrumentalGroups.length === 1) {
  712. let instr: InstrumentalGroup = currentGroup.InstrumentalGroups[0];
  713. if (currentGroup.Parent !== undefined) {
  714. currentGroup.Parent.InstrumentalGroups.push(instr);
  715. this._removeFromArray(currentGroup.Parent.InstrumentalGroups, currentGroup);
  716. } else {
  717. this.musicSheet.InstrumentalGroups.push(instr);
  718. this._removeFromArray(this.musicSheet.InstrumentalGroups, currentGroup);
  719. }
  720. }
  721. currentGroup = currentGroup.Parent;
  722. }
  723. }
  724. }
  725. }
  726. }
  727. } catch (e) {
  728. let errorMsg: string = ITextTranslation.translateText(
  729. "ReaderErrorMessages/InstrumentError", "Error while reading Instruments"
  730. );
  731. throw new MusicSheetReadingException(errorMsg, e);
  732. }
  733. for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
  734. let instrument: Instrument = this.musicSheet.Instruments[idx];
  735. if (!instrument.Name) {
  736. instrument.Name = "Instr. " + instrument.IdString;
  737. }
  738. }
  739. return instrumentDict;
  740. }
  741. private getCompleteNumberOfStavesFromXml(partInst: IXmlElement[]): number {
  742. let num: number = 0;
  743. for (let partNode of partInst) {
  744. let xmlMeasureList: IXmlElement[] = partNode.elements("measure");
  745. if (xmlMeasureList.length > 0) {
  746. let xmlMeasure: IXmlElement = xmlMeasureList[0];
  747. if (xmlMeasure !== undefined) {
  748. let stavesNode: IXmlElement = xmlMeasure.element("attributes");
  749. if (stavesNode !== undefined) {
  750. stavesNode = stavesNode.element("staves");
  751. }
  752. if (stavesNode === undefined) {
  753. num++;
  754. } else {
  755. num += parseInt(stavesNode.value, 10);
  756. }
  757. }
  758. }
  759. }
  760. if (isNaN(num) || num <= 0) {
  761. let errorMsg: string = ITextTranslation.translateText(
  762. "ReaderErrorMessages/StaffError", "Invalid number of staves."
  763. );
  764. throw new MusicSheetReadingException(errorMsg);
  765. }
  766. return num;
  767. }
  768. private getInstrumentNumberOfStavesFromXml(partNode: IXmlElement): number {
  769. let num: number = 0;
  770. let xmlMeasure: IXmlElement = partNode.element("measure");
  771. if (xmlMeasure !== undefined) {
  772. let attributes: IXmlElement = xmlMeasure.element("attributes");
  773. let staves: IXmlElement = undefined;
  774. if (attributes !== undefined) {
  775. staves = attributes.element("staves");
  776. }
  777. if (attributes === undefined || staves === undefined) {
  778. num = 1;
  779. } else {
  780. num = parseInt(staves.value, 10);
  781. }
  782. }
  783. if (isNaN(num) || num <= 0) {
  784. let errorMsg: string = ITextTranslation.translateText(
  785. "ReaderErrorMessages/StaffError", "Invalid number of Staves."
  786. );
  787. throw new MusicSheetReadingException(errorMsg);
  788. }
  789. return num;
  790. }
  791. }