VoiceGenerator.ts 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. import {Instrument} from "../Instrument";
  2. import {LinkedVoice} from "../VoiceData/LinkedVoice";
  3. import {Voice} from "../VoiceData/Voice";
  4. import {MusicSheet} from "../MusicSheet";
  5. import {VoiceEntry} from "../VoiceData/VoiceEntry";
  6. import {Note} from "../VoiceData/Note";
  7. import {SourceMeasure} from "../VoiceData/SourceMeasure";
  8. import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
  9. import {Beam} from "../VoiceData/Beam";
  10. import {Tie} from "../VoiceData/Tie";
  11. import {Tuplet} from "../VoiceData/Tuplet";
  12. import {Fraction} from "../../Common/DataObjects/fraction";
  13. //import {MusicSymbolModuleFactory} from "./InstrumentReader";
  14. import {IXmlElement} from "../../Common/FileIO/Xml";
  15. import {ITextTranslation} from "../Interfaces/ITextTranslation";
  16. import {ArticulationEnum} from "../VoiceData/VoiceEntry";
  17. import {Slur} from "../VoiceData/Expressions/ContinuousExpressions/Slur";
  18. import {LyricsEntry} from "../VoiceData/Lyrics/LyricsEntry";
  19. import {MusicSheetReadingException} from "../Exceptions";
  20. type SlurReader = any;
  21. export class VoiceGenerator {
  22. constructor(instrument: Instrument, voiceId: number, slurReader: SlurReader, mainVoice: Voice = null) {
  23. this.musicSheet = instrument.GetMusicSheet;
  24. this.slurReader = slurReader;
  25. if (mainVoice != null)
  26. this.voice = new LinkedVoice(instrument, voiceId, mainVoice);
  27. else {
  28. this.voice = new Voice(instrument, voiceId);
  29. }
  30. instrument.Voices.push(this.voice);
  31. //this.lyricsReader = MusicSymbolModuleFactory.createLyricsReader(this.musicSheet);
  32. //this.articulationReader = MusicSymbolModuleFactory.createArticulationReader();
  33. }
  34. private slurReader: SlurReader;
  35. //private lyricsReader: LyricsReader;
  36. //private articulationReader: ArticulationReader;
  37. private musicSheet: MusicSheet;
  38. private voice: Voice;
  39. private currentVoiceEntry: VoiceEntry;
  40. private currentNote: Note;
  41. private currentMeasure: SourceMeasure;
  42. private currentStaffEntry: SourceStaffEntry;
  43. private lastBeamTag: string = "";
  44. private openBeam: Beam;
  45. private openGraceBeam: Beam;
  46. private openTieDict: { [_: number]: Tie; } = {};
  47. private currentOctaveShift: number = 0;
  48. private tupletDict: { [_: number]: Tuplet; } = {};
  49. private openTupletNumber: number = 0;
  50. public get GetVoice(): Voice {
  51. return this.voice;
  52. }
  53. public get OctaveShift(): number {
  54. return this.currentOctaveShift;
  55. }
  56. public set OctaveShift(value: number) {
  57. this.currentOctaveShift = value;
  58. }
  59. public createVoiceEntry(musicTimestamp: Fraction, parentStaffEntry: SourceStaffEntry, addToVoice: boolean): void {
  60. this.currentVoiceEntry = new VoiceEntry(musicTimestamp.clone(), this.voice, parentStaffEntry);
  61. if (addToVoice)
  62. this.voice.VoiceEntries.push(this.currentVoiceEntry);
  63. if (parentStaffEntry.VoiceEntries.indexOf(this.currentVoiceEntry) === -1)
  64. parentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
  65. }
  66. public read(noteNode: IXmlElement, noteDuration: number, divisions: number, restNote: boolean, graceNote: boolean,
  67. parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
  68. measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean): Note {
  69. this.currentStaffEntry = parentStaffEntry;
  70. this.currentMeasure = parentMeasure;
  71. try {
  72. this.currentNote = restNote ? this.addRestNote(noteDuration, divisions) : this.addSingleNote(noteNode, noteDuration, divisions, graceNote, chord, guitarPro);
  73. if (this.lyricsReader != null && noteNode.Element("lyric") != null) {
  74. this.lyricsReader.addLyricEntry(noteNode, this.currentVoiceEntry);
  75. this.voice.Parent.HasLyrics = true;
  76. }
  77. var notationNode: IXmlElement = noteNode.Element("notations");
  78. if (notationNode != null) {
  79. var articNode: IXmlElement = null;
  80. if (this.articulationReader != null) {
  81. this.readArticulations(notationNode, this.currentVoiceEntry);
  82. }
  83. var slurNodes: IXmlElement[] = null;
  84. if (this.slurReader != null && (slurNodes = notationNode.Elements("slur")).Any())
  85. this.slurReader.addSlur(slurNodes, this.currentNote);
  86. var tupletNodeList: IXmlElement[] = null;
  87. if ((tupletNodeList = notationNode.Elements("tuplet")))
  88. this.openTupletNumber = this.addTuplet(noteNode, tupletNodeList);
  89. if (notationNode.Element("arpeggiate") != null && !graceNote)
  90. this.currentVoiceEntry.ArpeggiosNotesIndices.push(this.currentVoiceEntry.Notes.indexOf(this.currentNote));
  91. var tiedNodeList: IXmlElement[] = null;
  92. if ((tiedNodeList = notationNode.Elements("tied")))
  93. this.addTie(tiedNodeList, measureStartAbsoluteTimestamp, maxTieNoteFraction);
  94. var toRemove: number[] = new number[]();
  95. var openTieDictArr: KeyValuePair<number, Tie>[] = this.openTieDict.ToArray();
  96. for (var idx: number = 0, len = openTieDictArr.length; idx < len; ++idx) {
  97. var openTie: KeyValuePair<number, Tie> = openTieDictArr[idx];
  98. var tie: Tie = openTie.Value;
  99. if (tie.Start.ParentStaffEntry.Timestamp + tie.Start.Length < this.currentStaffEntry.Timestamp)
  100. toRemove.push(openTie.Key);
  101. }
  102. for (var idx: number = 0, len = toRemove.length; idx < len; ++idx) {
  103. var i: number = toRemove[idx];
  104. this.openTieDict.Remove(i);
  105. }
  106. }
  107. if (noteNode.Element("time-modification") != null && notationNode == null) {
  108. this.handleTimeModificationNode(noteNode);
  109. }
  110. }
  111. catch (err) {
  112. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteError",
  113. "Ignored erroneous Note.");
  114. this.musicSheet.SheetErrors.pushTemp(errorMsg);
  115. }
  116. return this.currentNote;
  117. }
  118. public checkForOpenGraceNotes(): void {
  119. if (this.currentStaffEntry != null && this.currentStaffEntry.VoiceEntries.length == 0 && this.currentVoiceEntry.GraceVoiceEntriesBefore != null && this.currentVoiceEntry.GraceVoiceEntriesBefore.length > 0) {
  120. var voice: Voice = this.currentVoiceEntry.ParentVoice;
  121. var horizontalIndex: number = this.currentMeasure.VerticalSourceStaffEntryContainers.indexOf(this.currentStaffEntry.VerticalContainerParent);
  122. var verticalIndex: number = this.currentStaffEntry.VerticalContainerParent.StaffEntries.indexOf(this.currentStaffEntry);
  123. var previousStaffEntry: SourceStaffEntry = this.currentMeasure.getPreviousSourceStaffEntryFromIndex(verticalIndex, horizontalIndex);
  124. if (previousStaffEntry != null) {
  125. var previousVoiceEntry: VoiceEntry = null;
  126. for (var idx: number = 0, len = previousStaffEntry.VoiceEntries.length; idx < len; ++idx) {
  127. var voiceEntry: VoiceEntry = previousStaffEntry.VoiceEntries[idx];
  128. if (voiceEntry.ParentVoice == voice) {
  129. previousVoiceEntry = voiceEntry;
  130. previousVoiceEntry.GraceVoiceEntriesAfter = VoiceEntry[];
  131. for (var idx2: number = 0, len2 = this.currentVoiceEntry.GraceVoiceEntriesBefore.length; idx2 < len2; ++idx2) {
  132. var graceVoiceEntry: VoiceEntry = this.currentVoiceEntry.GraceVoiceEntriesBefore[idx2];
  133. previousVoiceEntry.GraceVoiceEntriesAfter.push(graceVoiceEntry);
  134. }
  135. this.currentVoiceEntry.GraceVoiceEntriesBefore.Clear();
  136. this.currentStaffEntry = null;
  137. break;
  138. }
  139. }
  140. }
  141. }
  142. }
  143. public checkForStaffEntryLink(index: number, currentStaff: Staff, currentStaffEntry: SourceStaffEntry,
  144. currentMeasure: SourceMeasure): SourceStaffEntry {
  145. var staffEntryLink: StaffEntryLink = new StaffEntryLink(this.currentVoiceEntry);
  146. staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
  147. currentStaffEntry.Link = staffEntryLink;
  148. var linkMusicTimestamp: Fraction = new Fraction(this.currentVoiceEntry.Timestamp);
  149. var verticalSourceStaffEntryContainer: VerticalSourceStaffEntryContainer = currentMeasure.getVerticalContainerByTimestamp(linkMusicTimestamp);
  150. currentStaffEntry = verticalSourceStaffEntryContainer[index];
  151. if (currentStaffEntry == null) {
  152. currentStaffEntry = new SourceStaffEntry(verticalSourceStaffEntryContainer, currentStaff);
  153. verticalSourceStaffEntryContainer[index] = currentStaffEntry;
  154. }
  155. currentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
  156. staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
  157. currentStaffEntry.Link = staffEntryLink;
  158. return currentStaffEntry;
  159. }
  160. public checkForOpenBeam(): void {
  161. if (this.openBeam != null && this.currentNote != null)
  162. this.handleOpenBeam();
  163. }
  164. public checkOpenTies(): void {
  165. var toRemove: number[] = new number[]();
  166. var openTieDictArr: KeyValuePair<number, Tie>[] = this.openTieDict.ToArray();
  167. for (var idx: number = 0, len = openTieDictArr.length; idx < len; ++idx) {
  168. var openTie: KeyValuePair<number, Tie> = openTieDictArr[idx];
  169. var tie: Tie = openTie.Value;
  170. if (tie.Start.ParentStaffEntry.Timestamp + tie.Start.Length < tie.Start.ParentStaffEntry.VerticalContainerParent.ParentMeasure.Duration)
  171. toRemove.push(openTie.Key);
  172. }
  173. for (var idx: number = 0, len = toRemove.length; idx < len; ++idx) {
  174. var i: number = toRemove[idx];
  175. this.openTieDict.Remove(i);
  176. }
  177. }
  178. public hasVoiceEntry(): boolean {
  179. return this.currentVoiceEntry != null;
  180. }
  181. public getNoteDurationFromType(type: string): Fraction {
  182. switch (type) {
  183. case "1024th":
  184. return new Fraction(1, 1024);
  185. case "512th":
  186. return new Fraction(1, 512);
  187. case "256th":
  188. return new Fraction(1, 256);
  189. case "128th":
  190. return new Fraction(1, 128);
  191. case "64th":
  192. return new Fraction(1, 64);
  193. case "32th":
  194. case "32nd":
  195. return new Fraction(1, 32);
  196. case "16th":
  197. return new Fraction(1, 16);
  198. case "eighth":
  199. return new Fraction(1, 8);
  200. case "quarter":
  201. return new Fraction(1, 4);
  202. case "half":
  203. return new Fraction(1, 2);
  204. case "whole":
  205. return new Fraction(1, 1);
  206. case "breve":
  207. return new Fraction(2, 1);
  208. case "long":
  209. return new Fraction(4, 1);
  210. case "maxima":
  211. return new Fraction(8, 1);
  212. default:
  213. {
  214. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError",
  215. "Invalid note duration.");
  216. throw new MusicSheetReadingException(errorMsg, 0);
  217. }
  218. }
  219. }
  220. private readArticulations(notationNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
  221. var articNode: IXmlElement;
  222. if ((articNode = notationNode.Element("articulations")) != null)
  223. this.articulationReader.addArticulationExpression(articNode, currentVoiceEntry);
  224. var fermaNode: IXmlElement = null;
  225. if ((fermaNode = notationNode.Element("fermata")) != null)
  226. this.articulationReader.addFermata(fermaNode, currentVoiceEntry);
  227. var tecNode: IXmlElement = null;
  228. if ((tecNode = notationNode.Element("technical")) != null)
  229. this.articulationReader.addTechnicalArticulations(tecNode, currentVoiceEntry);
  230. var ornaNode: IXmlElement = null;
  231. if ((ornaNode = notationNode.Element("ornaments")) != null)
  232. this.articulationReader.addOrnament(ornaNode, currentVoiceEntry);
  233. }
  234. private addSingleNote(node: IXmlElement, noteDuration: number, divisions: number, graceNote: boolean, chord: boolean,
  235. guitarPro: boolean): Note {
  236. var noteAlter: AccidentalEnum = AccidentalEnum.NONE;
  237. var noteStep: NoteEnum = NoteEnum.C;
  238. var noteOctave: number = 0;
  239. var playbackInstrumentId: string = null;
  240. var xmlnodeElementsArr: IXmlElement[] = node.Elements().ToArray();
  241. for (var idx: number = 0, len = xmlnodeElementsArr.length; idx < len; ++idx) {
  242. var noteElement: IXmlElement = xmlnodeElementsArr[idx];
  243. try {
  244. if (noteElement.Name == "pitch") {
  245. var noteElementsArr: IXmlElement[] = noteElement.Elements().ToArray();
  246. for (var idx2: number = 0, len2 = noteElementsArr.length; idx2 < len2; ++idx2) {
  247. var pitchElement: IXmlElement = noteElementsArr[idx2];
  248. try {
  249. if (pitchElement.Name == "step") {
  250. try {
  251. switch (pitchElement.Value) {
  252. case "C":
  253. {
  254. noteStep = NoteEnum.C;
  255. break;
  256. }
  257. case "D":
  258. {
  259. noteStep = NoteEnum.D;
  260. break;
  261. }
  262. case "E":
  263. {
  264. noteStep = NoteEnum.E;
  265. break;
  266. }
  267. case "F":
  268. {
  269. noteStep = NoteEnum.F;
  270. break;
  271. }
  272. case "G":
  273. {
  274. noteStep = NoteEnum.G;
  275. break;
  276. }
  277. case "A":
  278. {
  279. noteStep = NoteEnum.A;
  280. break;
  281. }
  282. case "B":
  283. {
  284. noteStep = NoteEnum.B;
  285. break;
  286. }
  287. }
  288. }
  289. catch (e) {
  290. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NotePitchError",
  291. "Invalid pitch while reading note.");
  292. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  293. throw new MusicSheetReadingException("", e, 0);
  294. }
  295. }
  296. else if (pitchElement.Name == "alter") {
  297. try {
  298. noteAlter = <AccidentalEnum>StringToNumberConverter.ToInteger(pitchElement.Value);
  299. }
  300. catch (e) {
  301. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteAlterationError",
  302. "Invalid alteration while reading note.");
  303. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  304. throw new MusicSheetReadingException("", e, 0);
  305. }
  306. }
  307. else if (pitchElement.Name == "octave") {
  308. try {
  309. noteOctave = <number>StringToNumberConverter.ToInteger(pitchElement.Value);
  310. }
  311. catch (e) {
  312. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteOctaveError",
  313. "Invalid octave value while reading note.");
  314. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  315. throw new MusicSheetReadingException("", e, 0);
  316. }
  317. }
  318. }
  319. catch (ex) {
  320. Logger.DefaultLogger.LogError(LogLevel.NORMAL,
  321. "VoiceGenerator.addSingleNote read Step: ", ex);
  322. }
  323. }
  324. }
  325. else if (noteElement.Name == "unpitched") {
  326. var displayStep: IXmlElement = null;
  327. if ((displayStep = noteElement.Element("display-step")) != null) {
  328. noteStep = <NoteEnum>Enum.Parse(/*typeof*/NoteEnum, displayStep.Value);
  329. }
  330. var octave: IXmlElement = null;
  331. if ((octave = noteElement.Element("display-octave")) != null) {
  332. noteOctave = <number>(StringToNumberConverter.ToInteger(octave.Value));
  333. if (guitarPro)
  334. noteOctave += 1;
  335. }
  336. }
  337. else if (noteElement.Name == "instrument") {
  338. if (noteElement.FirstAttribute != null)
  339. playbackInstrumentId = noteElement.FirstAttribute.Value;
  340. }
  341. }
  342. catch (ex) {
  343. Logger.DefaultLogger.LogError(LogLevel.NORMAL, "VoiceGenerator.addSingleNote: ", ex);
  344. }
  345. }
  346. noteOctave -= Pitch.XmlOctaveDifference;
  347. var pitch: Pitch = new Pitch(noteStep, noteOctave, noteAlter);
  348. var noteLength: Fraction = new Fraction(noteDuration, divisions);
  349. var note: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
  350. note.PlaybackInstrumentId = playbackInstrumentId;
  351. if (!graceNote)
  352. this.currentVoiceEntry.Notes.push(note);
  353. else this.handleGraceNote(node, note);
  354. if (node.Elements("beam").Any() && !chord) {
  355. this.createBeam(node, note, graceNote);
  356. }
  357. return note;
  358. }
  359. private addRestNote(noteDuration: number, divisions: number): Note {
  360. var restFraction: Fraction = new Fraction(noteDuration, divisions);
  361. var restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, null);
  362. this.currentVoiceEntry.Notes.push(restNote);
  363. if (this.openBeam != null)
  364. this.openBeam.ExtendedNoteList.push(restNote);
  365. return restNote;
  366. }
  367. private createBeam(node: IXmlElement, note: Note, grace: boolean): void {
  368. try {
  369. var beamNode: IXmlElement = node.Element("beam");
  370. var beamAttr: IXmlAttribute = null;
  371. if (beamNode != null && beamNode.HasAttributes)
  372. beamAttr = beamNode.Attribute("number");
  373. if (beamAttr != null) {
  374. var beamNumber: number = StringToNumberConverter.ToInteger(beamAttr.Value);
  375. var mainBeamNode: IXmlElement[] = node.Elements("beam");
  376. var currentBeamTag: string = mainBeamNode.First().Value;
  377. if (beamNumber == 1 && mainBeamNode != null) {
  378. if (currentBeamTag == "begin" && this.lastBeamTag != currentBeamTag) {
  379. if (grace) {
  380. if (this.openGraceBeam != null)
  381. this.handleOpenBeam();
  382. this.openGraceBeam = new Beam();
  383. }
  384. else {
  385. if (this.openBeam != null)
  386. this.handleOpenBeam();
  387. this.openBeam = new Beam();
  388. }
  389. }
  390. this.lastBeamTag = currentBeamTag;
  391. }
  392. var sameVoiceEntry: boolean = false;
  393. if (grace) {
  394. if (this.openGraceBeam == null)
  395. return
  396. for (var idx: number = 0, len = this.openGraceBeam.Notes.length; idx < len; ++idx) {
  397. var beamNote: Note = this.openGraceBeam.Notes[idx];
  398. if (this.currentVoiceEntry == beamNote.ParentVoiceEntry)
  399. sameVoiceEntry = true;
  400. }
  401. if (!sameVoiceEntry) {
  402. this.openGraceBeam.addNoteToBeam(note);
  403. if (currentBeamTag == "end" && beamNumber == 1)
  404. this.openGraceBeam = null;
  405. }
  406. }
  407. else {
  408. if (this.openBeam == null)
  409. return
  410. for (var idx: number = 0, len = this.openBeam.Notes.length; idx < len; ++idx) {
  411. var beamNote: Note = this.openBeam.Notes[idx];
  412. if (this.currentVoiceEntry == beamNote.ParentVoiceEntry)
  413. sameVoiceEntry = true;
  414. }
  415. if (!sameVoiceEntry) {
  416. this.openBeam.addNoteToBeam(note);
  417. if (currentBeamTag == "end" && beamNumber == 1)
  418. this.openBeam = null;
  419. }
  420. }
  421. }
  422. }
  423. catch (e) {
  424. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/BeamError",
  425. "Error while reading beam.");
  426. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  427. throw new MusicSheetReadingException("", e, 0);
  428. }
  429. }
  430. private handleOpenBeam(): void {
  431. if (this.openBeam.Notes.length == 1) {
  432. var beamNote: Note = this.openBeam.Notes[0];
  433. beamNote.NoteBeam = null;
  434. this.openBeam = null;
  435. return
  436. }
  437. if (this.currentNote == this.openBeam.Notes.Last())
  438. this.openBeam = null;
  439. else {
  440. var beamLastNote: Note = this.openBeam.Notes.Last();
  441. var beamLastNoteStaffEntry: SourceStaffEntry = beamLastNote.ParentStaffEntry;
  442. var horizontalIndex: number = this.currentMeasure.getVerticalContainerIndexByTimestamp(beamLastNoteStaffEntry.Timestamp);
  443. var verticalIndex: number = beamLastNoteStaffEntry.VerticalContainerParent.StaffEntries.indexOf(beamLastNoteStaffEntry);
  444. if (horizontalIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1) {
  445. var nextStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[horizontalIndex + 1][verticalIndex];
  446. if (nextStaffEntry != null) {
  447. for (var idx: number = 0, len = nextStaffEntry.VoiceEntries.length; idx < len; ++idx) {
  448. var voiceEntry: VoiceEntry = nextStaffEntry.VoiceEntries[idx];
  449. if (voiceEntry.ParentVoice == this.voice) {
  450. var candidateNote: Note = voiceEntry.Notes[0];
  451. if (candidateNote.Length <= new Fraction(1, 8)) {
  452. this.openBeam.addNoteToBeam(candidateNote);
  453. this.openBeam = null;
  454. }
  455. else {
  456. this.openBeam = null;
  457. }
  458. }
  459. }
  460. }
  461. }
  462. else {
  463. this.openBeam = null;
  464. }
  465. }
  466. }
  467. private handleGraceNote(node: IXmlElement, note: Note): void {
  468. var graceChord: boolean = false;
  469. var type: string = "";
  470. if (node.Elements("type").Any()) {
  471. var typeNode: IXmlElement[] = node.Elements("type");
  472. if (typeNode.Any()) {
  473. type = typeNode.First().Value;
  474. try {
  475. note.Length = this.getNoteDurationFromType(type);
  476. note.Length.Numerator = 1;
  477. }
  478. catch (e) {
  479. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError",
  480. "Invalid note duration.");
  481. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  482. throw new MusicSheetReadingException("", e, 0);
  483. }
  484. }
  485. }
  486. var graceNode: IXmlElement = node.Element("grace");
  487. if (graceNode != null && graceNode.Attributes().Any()) {
  488. if (graceNode.Attribute("slash") != null) {
  489. var slash: string = graceNode.Attribute("slash").Value;
  490. if (slash == "yes")
  491. note.GraceNoteSlash = true;
  492. }
  493. }
  494. if (node.Element("chord") != null)
  495. graceChord = true;
  496. var graceVoiceEntry: VoiceEntry = null;
  497. if (!graceChord) {
  498. graceVoiceEntry = new VoiceEntry(new Fraction(new Fraction(0, 1)), this.currentVoiceEntry.ParentVoice,
  499. this.currentStaffEntry);
  500. if (this.currentVoiceEntry.GraceVoiceEntriesBefore == null)
  501. this.currentVoiceEntry.GraceVoiceEntriesBefore = new List<VoiceEntry>();
  502. this.currentVoiceEntry.GraceVoiceEntriesBefore.push(graceVoiceEntry);
  503. }
  504. else {
  505. if (this.currentVoiceEntry.GraceVoiceEntriesBefore != null && this.currentVoiceEntry.GraceVoiceEntriesBefore.length > 0)
  506. graceVoiceEntry = this.currentVoiceEntry.GraceVoiceEntriesBefore.Last();
  507. }
  508. if (graceVoiceEntry != null) {
  509. graceVoiceEntry.Notes.push(note);
  510. note.ParentVoiceEntry = graceVoiceEntry;
  511. }
  512. }
  513. private addTuplet(node: IXmlElement, tupletNodeList: IXmlElement[]): number {
  514. if (tupletNodeList != null && tupletNodeList.length() > 1) {
  515. var timeModNode: IXmlElement = node.Element("time-modification");
  516. if (timeModNode != null)
  517. timeModNode = timeModNode.Element("actual-notes");
  518. var tupletNodeListArr: IXmlElement[] = tupletNodeList.ToArray();
  519. for (var idx: number = 0, len = tupletNodeListArr.length; idx < len; ++idx) {
  520. var tupletNode: IXmlElement = tupletNodeListArr[idx];
  521. if (tupletNode != null && tupletNode.Attributes().Any()) {
  522. var type: string = tupletNode.Attribute("type").Value;
  523. if (type == "start") {
  524. var tupletNumber: number = 1;
  525. if (tupletNode.Attribute("nummber") != null)
  526. tupletNumber = StringToNumberConverter.ToInteger(tupletNode.Attribute("number").Value);
  527. var tupletLabelNumber: number = 0;
  528. if (timeModNode != null) {
  529. try {
  530. tupletLabelNumber = StringToNumberConverter.ToInteger(timeModNode.Value);
  531. }
  532. catch (e) {
  533. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/TupletNoteDurationError",
  534. "Invalid tuplet note duration.");
  535. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  536. throw new MusicSheetReadingException("", e, 0);
  537. }
  538. }
  539. var tuplet: Tuplet = new Tuplet(tupletLabelNumber);
  540. if (this.tupletDict.ContainsKey(tupletNumber)) {
  541. this.tupletDict.Remove(tupletNumber);
  542. if (this.tupletDict.length == 0)
  543. this.openTupletNumber = 0;
  544. else if (this.tupletDict.length > 1)
  545. this.openTupletNumber--;
  546. }
  547. this.tupletDict.push(tupletNumber, tuplet);
  548. var subnotelist: List<Note> = new List<Note>();
  549. subnotelist.push(this.currentNote);
  550. tuplet.Notes.push(subnotelist);
  551. tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
  552. this.currentNote.NoteTuplet = tuplet;
  553. this.openTupletNumber = tupletNumber;
  554. }
  555. else if (type == "stop") {
  556. var tupletNumber: number = 1;
  557. if (tupletNode.Attribute("nummber") != null)
  558. tupletNumber = StringToNumberConverter.ToInteger(tupletNode.Attribute("number").Value);
  559. if (this.tupletDict.ContainsKey(tupletNumber)) {
  560. var tuplet: Tuplet = this.tupletDict[tupletNumber];
  561. var subnotelist: List<Note> = new List<Note>();
  562. subnotelist.push(this.currentNote);
  563. tuplet.Notes.push(subnotelist);
  564. tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
  565. this.currentNote.NoteTuplet = tuplet;
  566. this.tupletDict.Remove(tupletNumber);
  567. if (this.tupletDict.length == 0)
  568. this.openTupletNumber = 0;
  569. else if (this.tupletDict.length > 1)
  570. this.openTupletNumber--;
  571. }
  572. }
  573. }
  574. }
  575. }
  576. else if (tupletNodeList.First() != null) {
  577. var n: IXmlElement = tupletNodeList.First();
  578. if (n.HasAttributes) {
  579. var type: string = n.Attribute("type").Value;
  580. var tupletnumber: number = 1;
  581. var noTupletNumbering: boolean = false;
  582. try {
  583. if (n.Attribute("number") != null)
  584. tupletnumber = StringToNumberConverter.ToInteger(n.Attribute("number").Value);
  585. }
  586. catch (err) {
  587. noTupletNumbering = true;
  588. }
  589. if (type == "start") {
  590. var tupletLabelNumber: number = 0;
  591. var timeModNode: IXmlElement = node.Element("time-modification");
  592. if (timeModNode != null)
  593. timeModNode = timeModNode.Element("actual-notes");
  594. if (timeModNode != null) {
  595. try {
  596. tupletLabelNumber = StringToNumberConverter.ToInteger(timeModNode.Value);
  597. }
  598. catch (e) {
  599. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/TupletNoteDurationError",
  600. "Invalid tuplet note duration.");
  601. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  602. throw new MusicSheetReadingException("", e, 0);
  603. }
  604. }
  605. if (noTupletNumbering) {
  606. this.openTupletNumber++;
  607. tupletnumber = this.openTupletNumber;
  608. }
  609. var tuplet: Tuplet;
  610. if (this.tupletDict.ContainsKey(tupletnumber)) {
  611. tuplet = this.tupletDict[tupletnumber];
  612. }
  613. else {
  614. tuplet = new Tuplet(tupletLabelNumber);
  615. this.tupletDict.push(tupletnumber, tuplet);
  616. }
  617. var subnotelist: List<Note> = new List<Note>();
  618. subnotelist.push(this.currentNote);
  619. tuplet.Notes.push(subnotelist);
  620. tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
  621. this.currentNote.NoteTuplet = tuplet;
  622. this.openTupletNumber = tupletnumber;
  623. }
  624. else if (type == "stop") {
  625. if (noTupletNumbering)
  626. tupletnumber = this.openTupletNumber;
  627. if (this.tupletDict.ContainsKey(tupletnumber)) {
  628. var tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
  629. var subnotelist: List<Note> = new List<Note>();
  630. subnotelist.push(this.currentNote);
  631. tuplet.Notes.push(subnotelist);
  632. tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
  633. this.currentNote.NoteTuplet = tuplet;
  634. if (this.tupletDict.length == 0)
  635. this.openTupletNumber = 0;
  636. else if (this.tupletDict.length > 1)
  637. this.openTupletNumber--;
  638. this.tupletDict.Remove(tupletnumber);
  639. }
  640. }
  641. }
  642. }
  643. return this.openTupletNumber;
  644. }
  645. private handleTimeModificationNode(noteNode: IXmlElement): void {
  646. if (this.tupletDict.length != 0) {
  647. try {
  648. var tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
  649. var notes: List<Note> = tuplet.Notes.Last();
  650. var lastTupletVoiceEntry: VoiceEntry = notes[0].ParentVoiceEntry;
  651. var noteList: List<Note>;
  652. if (lastTupletVoiceEntry.Timestamp == this.currentVoiceEntry.Timestamp)
  653. noteList = notes;
  654. else {
  655. noteList = new List<Note>();
  656. tuplet.Notes.push(noteList);
  657. tuplet.Fractions.push(this.getTupletNoteDurationFromType(noteNode));
  658. }
  659. noteList.push(this.currentNote);
  660. this.currentNote.NoteTuplet = tuplet;
  661. }
  662. catch (ex) {
  663. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/TupletNumberError",
  664. "Invalid tuplet number.");
  665. this.musicSheet.SheetErrors.AddErrorMessageInTempList(errorMsg);
  666. throw ex;
  667. }
  668. }
  669. else if (this.currentVoiceEntry.Notes.length > 0) {
  670. var firstNote: Note = this.currentVoiceEntry.Notes[0];
  671. if (firstNote.NoteTuplet != null) {
  672. var tuplet: Tuplet = firstNote.NoteTuplet;
  673. var notes: List<Note> = tuplet.Notes.Last();
  674. notes.push(this.currentNote);
  675. this.currentNote.NoteTuplet = tuplet;
  676. }
  677. }
  678. }
  679. private addTie(tieNodeList: IXmlElement[], measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction): void {
  680. if (tieNodeList != null) {
  681. if (tieNodeList.length() == 1) {
  682. var tieNode: IXmlElement = tieNodeList.First();
  683. if (tieNode != null && tieNode.Attributes().Any()) {
  684. var type: string = tieNode.Attribute("type").Value;
  685. try {
  686. if (type == "start") {
  687. var number: number = this.findCurrentNoteInTieDict(this.currentNote);
  688. if (number < 0)
  689. this.openTieDict.Remove(number);
  690. var newTieNumber: number = this.getNextAvailableNumberForTie();
  691. var tie: Tie = new Tie(this.currentNote);
  692. this.openTieDict.push(newTieNumber, tie);
  693. if (this.currentNote.NoteBeam != null)
  694. if (this.currentNote.NoteBeam.Notes[0] == this.currentNote) {
  695. tie.BeamStartTimestamp = measureStartAbsoluteTimestamp + this.currentVoiceEntry.Timestamp;
  696. }
  697. else {
  698. for (var idx: number = 0, len = this.currentNote.NoteBeam.Notes.length; idx < len; ++idx) {
  699. var note: Note = this.currentNote.NoteBeam.Notes[idx];
  700. if (note.NoteTie != null && note.NoteTie != tie && note.NoteTie.BeamStartTimestamp != null) {
  701. tie.BeamStartTimestamp = note.NoteTie.BeamStartTimestamp;
  702. break;
  703. }
  704. }
  705. if (this.currentNote == this.currentNote.NoteBeam.Notes.Last())
  706. tie.BeamStartTimestamp = measureStartAbsoluteTimestamp + this.currentVoiceEntry.Timestamp;
  707. }
  708. }
  709. else if (type == "stop") {
  710. var tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
  711. var tie: Tie = this.openTieDict[tieNumber];
  712. if (tie !== undefined) {
  713. var tieStartNote: Note = tie.Start;
  714. tieStartNote.NoteTie = tie;
  715. tieStartNote.Length.Add(this.currentNote.Length);
  716. tie.Fractions.push(this.currentNote.Length);
  717. if (maxTieNoteFraction < this.currentStaffEntry.Timestamp + this.currentNote.Length)
  718. maxTieNoteFraction = this.currentStaffEntry.Timestamp + this.currentNote.Length;
  719. this.currentVoiceEntry.Notes.Remove(this.currentNote);
  720. if (this.currentVoiceEntry.Articulations.length == 1 && this.currentVoiceEntry.Articulations[0] == ArticulationEnum.fermata && !tieStartNote.ParentVoiceEntry.Articulations.Contains(ArticulationEnum.fermata))
  721. tieStartNote.ParentVoiceEntry.Articulations.push(ArticulationEnum.fermata);
  722. if (this.currentNote.NoteBeam != null) {
  723. var noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
  724. if (noteBeamIndex == 0 && tie.BeamStartTimestamp == null)
  725. tie.BeamStartTimestamp = measureStartAbsoluteTimestamp + this.currentVoiceEntry.Timestamp;
  726. var noteBeam: Beam = this.currentNote.NoteBeam;
  727. noteBeam.Notes[noteBeamIndex] = tieStartNote;
  728. tie.TieBeam = noteBeam;
  729. }
  730. if (this.currentNote.NoteTuplet != null) {
  731. var noteTupletIndex: number = this.currentNote.NoteTuplet.getNoteIndex(this.currentNote);
  732. var index: number = this.currentNote.NoteTuplet.Notes[noteTupletIndex].indexOf(this.currentNote);
  733. var noteTuplet: Tuplet = this.currentNote.NoteTuplet;
  734. noteTuplet.Notes[noteTupletIndex][index] = tieStartNote;
  735. tie.TieTuplet = noteTuplet;
  736. }
  737. for (var idx: number = 0, len = this.currentNote.NoteSlurs.length; idx < len; ++idx) {
  738. var slur: Slur = this.currentNote.NoteSlurs[idx];
  739. if (slur.StartNote == this.currentNote) {
  740. slur.StartNote = tie.Start;
  741. slur.StartNote.NoteSlurs.push(slur);
  742. }
  743. if (slur.EndNote == this.currentNote) {
  744. slur.EndNote = tie.Start;
  745. slur.EndNote.NoteSlurs.push(slur);
  746. }
  747. }
  748. //var lyricsEntriesArr: KeyValuePair<number, LyricsEntry>[] = this.currentVoiceEntry.LyricsEntries.ToArray();
  749. for (let lyricsEntry in this.currentVoiceEntry.LyricsEntries) {
  750. let val: LyricsEntry = this.currentVoiceEntry.LyricsEntries[lyricsEntry];
  751. if (!tieStartNote.ParentVoiceEntry.LyricsEntries[lyricsEntry] === undefined) {
  752. tieStartNote.ParentVoiceEntry.LyricsEntries[lyricsEntry] = val;
  753. val.Parent = tieStartNote.ParentVoiceEntry;
  754. }
  755. }
  756. this.openTieDict.Remove(tieNumber);
  757. }
  758. }
  759. }
  760. catch (err) {
  761. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/TieError", "Error while reading tie.");
  762. this.musicSheet.SheetErrors.pushTemp(errorMsg);
  763. }
  764. }
  765. }
  766. else if (tieNodeList.length() == 2) {
  767. var tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
  768. if (tieNumber >= 0) {
  769. var tie: Tie = this.openTieDict[tieNumber];
  770. var tieStartNote: Note = tie.Start;
  771. tieStartNote.Length.Add(this.currentNote.Length);
  772. tie.Fractions.push(this.currentNote.Length);
  773. if (this.currentNote.NoteBeam != null) {
  774. var noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
  775. if (noteBeamIndex == 0 && tie.BeamStartTimestamp == null)
  776. tie.BeamStartTimestamp = measureStartAbsoluteTimestamp + this.currentVoiceEntry.Timestamp;
  777. var noteBeam: Beam = this.currentNote.NoteBeam;
  778. noteBeam.Notes[noteBeamIndex] = tieStartNote;
  779. tie.TieBeam = noteBeam;
  780. }
  781. for (var idx: number = 0, len = this.currentNote.NoteSlurs.length; idx < len; ++idx) {
  782. var slur: Slur = this.currentNote.NoteSlurs[idx];
  783. if (slur.StartNote == this.currentNote) {
  784. slur.StartNote = tie.Start;
  785. slur.StartNote.NoteSlurs.push(slur);
  786. }
  787. if (slur.EndNote == this.currentNote) {
  788. slur.EndNote = tie.Start;
  789. slur.EndNote.NoteSlurs.push(slur);
  790. }
  791. }
  792. var lyricsEntries: KeyValuePair<number, LyricsEntry>[] = this.currentVoiceEntry.LyricsEntries.ToArray();
  793. for (var idx: number = 0, len = lyricsEntries.length; idx < len; ++idx) {
  794. var lyricsEntry: KeyValuePair<number, LyricsEntry> = lyricsEntries[idx];
  795. if (!tieStartNote.ParentVoiceEntry.LyricsEntries.ContainsKey(lyricsEntry.Key)) {
  796. tieStartNote.ParentVoiceEntry.LyricsEntries.push(lyricsEntry.Key, lyricsEntry.Value);
  797. lyricsEntry.Value.Parent = tieStartNote.ParentVoiceEntry;
  798. }
  799. }
  800. if (maxTieNoteFraction < this.currentStaffEntry.Timestamp + this.currentNote.Length)
  801. maxTieNoteFraction = this.currentStaffEntry.Timestamp + this.currentNote.Length;
  802. this.currentVoiceEntry.Notes.Remove(this.currentNote);
  803. }
  804. }
  805. }
  806. }
  807. private getNextAvailableNumberForTie(): number {
  808. var keys: number[] = this.openTieDict.keys();
  809. if (keys.length == 0)
  810. return 1;
  811. keys.Sort();
  812. for (var i: number = 0; i < keys.length; i++) {
  813. if (i + 1 != keys[i])
  814. return i + 1;
  815. }
  816. return keys[keys.length - 1] + 1;
  817. }
  818. private findCurrentNoteInTieDict(candidateNote: Note): number {
  819. var openTieDictArr: KeyValuePair<number, Tie>[] = this.openTieDict.ToArray();
  820. for (var idx: number = 0, len = openTieDictArr.length; idx < len; ++idx) {
  821. var keyValuePair: KeyValuePair<number, Tie> = openTieDictArr[idx];
  822. if (keyValuePair.Value.Start.Pitch.FundamentalNote == candidateNote.Pitch.FundamentalNote && keyValuePair.Value.Start.Pitch.Octave == candidateNote.Pitch.Octave) {
  823. return keyValuePair.Key;
  824. }
  825. }
  826. return -1;
  827. }
  828. private getTupletNoteDurationFromType(xmlNode: IXmlElement): Fraction {
  829. if (xmlNode.Element("type") != null) {
  830. var typeNode: IXmlElement = xmlNode.Element("type");
  831. if (typeNode != null) {
  832. var type: string = typeNode.Value;
  833. try {
  834. return this.getNoteDurationFromType(type);
  835. }
  836. catch (e) {
  837. var errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid note duration.");
  838. this.musicSheet.SheetErrors.pushTemp(errorMsg);
  839. throw new MusicSheetReadingException("", e);
  840. }
  841. }
  842. }
  843. return null;
  844. }
  845. }