LyricsReader.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import {LyricWord} from "../../VoiceData/Lyrics/LyricsWord";
  2. import {VoiceEntry} from "../../VoiceData/VoiceEntry";
  3. import {IXmlElement} from "../../../Common/FileIO/Xml";
  4. import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
  5. import {ITextTranslation} from "../../Interfaces/ITextTranslation";
  6. import {MusicSheet} from "../../MusicSheet";
  7. export class LyricsReader {
  8. private openLyricWords: { [_: number]: LyricWord; } = {};
  9. private currentLyricWord: LyricWord;
  10. private musicSheet: MusicSheet;
  11. constructor(musicSheet: MusicSheet) {
  12. this.musicSheet = musicSheet;
  13. }
  14. /**
  15. * This method adds a single LyricEntry to a VoiceEntry
  16. * @param {IXmlElement[]} lyricNodeList
  17. * @param {VoiceEntry} currentVoiceEntry
  18. */
  19. public addLyricEntry(lyricNodeList: IXmlElement[], currentVoiceEntry: VoiceEntry): void {
  20. if (lyricNodeList !== undefined) {
  21. const lyricNodeListArr: IXmlElement[] = lyricNodeList;
  22. for (let idx: number = 0, len: number = lyricNodeListArr.length; idx < len; ++idx) {
  23. const lyricNode: IXmlElement = lyricNodeListArr[idx];
  24. try {
  25. let syllabic: string = "single"; // Single as default
  26. if (lyricNode.element("text") !== undefined) {
  27. let textNode: IXmlElement = lyricNode.element("text");
  28. if (lyricNode.element("syllabic") !== undefined) {
  29. syllabic = lyricNode.element("syllabic").value;
  30. }
  31. if (textNode !== undefined) {
  32. const text: string = textNode.value;
  33. // <elision> separates Multiple syllabels on a single LyricNote
  34. // "-" text indicating separated syllabel should be ignored
  35. // we calculate the Dash element much later
  36. if (lyricNode.element("elision") !== undefined && text === "-") {
  37. const lyricNodeChildren: IXmlElement[] = lyricNode.elements();
  38. let elisionIndex: number = 0;
  39. for (let i: number = 0; i < lyricNodeChildren.length; i++) {
  40. const child: IXmlElement = lyricNodeChildren[i];
  41. if (child.name === "elision") {
  42. elisionIndex = i;
  43. break;
  44. }
  45. }
  46. let nextText: IXmlElement = undefined;
  47. let nextSyllabic: IXmlElement = undefined;
  48. // read the next nodes
  49. if (elisionIndex > 0) {
  50. for (let i: number = elisionIndex; i < lyricNodeChildren.length; i++) {
  51. const child: IXmlElement = lyricNodeChildren[i];
  52. if (child.name === "text") {
  53. nextText = child;
  54. }
  55. if (child.name === "syllabic") {
  56. nextSyllabic = child;
  57. }
  58. }
  59. }
  60. if (nextText !== undefined && nextSyllabic !== undefined) {
  61. textNode = nextText;
  62. syllabic = "middle";
  63. }
  64. }
  65. let currentLyricVerseNumber: number = 1;
  66. if (lyricNode.attributes() !== undefined && lyricNode.attribute("number") !== undefined) {
  67. try {
  68. currentLyricVerseNumber = parseInt(lyricNode.attribute("number").value, 10);
  69. } catch (err) {
  70. try {
  71. const result: string[] = lyricNode.attribute("number").value.toLowerCase().split("verse");
  72. if (result.length > 1) {
  73. currentLyricVerseNumber = parseInt(result[1], 10);
  74. }
  75. } catch (err) {
  76. const errorMsg: string =
  77. ITextTranslation.translateText("ReaderErrorMessages/LyricVerseNumberError", "Invalid lyric verse number");
  78. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  79. continue;
  80. }
  81. }
  82. }
  83. let lyricsEntry: LyricsEntry = undefined;
  84. if (syllabic === "single" || syllabic === "end") {
  85. if (this.openLyricWords[currentLyricVerseNumber] !== undefined) { // word end given or some word still open
  86. this.currentLyricWord = this.openLyricWords[currentLyricVerseNumber];
  87. lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry);
  88. this.currentLyricWord.Syllables.push(lyricsEntry);
  89. delete this.openLyricWords[currentLyricVerseNumber];
  90. this.currentLyricWord = undefined;
  91. } else { // single syllable given or end given while no word has been started
  92. lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, undefined, currentVoiceEntry);
  93. }
  94. lyricsEntry.extend = lyricNode.element("extend") !== undefined;
  95. } else if (syllabic === "begin") { // first finishing, if a word already is open (can only happen, when wrongly given)
  96. if (this.openLyricWords[currentLyricVerseNumber] !== undefined) {
  97. delete this.openLyricWords[currentLyricVerseNumber];
  98. this.currentLyricWord = undefined;
  99. }
  100. this.currentLyricWord = new LyricWord();
  101. this.openLyricWords[currentLyricVerseNumber] = this.currentLyricWord;
  102. lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry);
  103. this.currentLyricWord.Syllables.push(lyricsEntry);
  104. } else if (syllabic === "middle") {
  105. if (this.openLyricWords[currentLyricVerseNumber] !== undefined) {
  106. this.currentLyricWord = this.openLyricWords[currentLyricVerseNumber];
  107. lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry);
  108. this.currentLyricWord.Syllables.push(lyricsEntry);
  109. } else {
  110. // in case the wrong syllabel information is given, create a single Entry and add it to currentVoiceEntry
  111. lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, undefined, currentVoiceEntry);
  112. }
  113. }
  114. // add each LyricEntry to currentVoiceEntry
  115. if (lyricsEntry !== undefined) {
  116. // only add the lyric entry if not another entry has already been given:
  117. if (!currentVoiceEntry.LyricsEntries[currentLyricVerseNumber] !== undefined) {
  118. currentVoiceEntry.LyricsEntries.setValue(currentLyricVerseNumber, lyricsEntry);
  119. }
  120. // save in currentInstrument the verseNumber (only once)
  121. if (!currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers[currentLyricVerseNumber] !== undefined) {
  122. currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers.push(currentLyricVerseNumber);
  123. }
  124. }
  125. }
  126. }
  127. } catch (err) {
  128. const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/LyricError", "Error while reading lyric entry.");
  129. this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
  130. continue;
  131. }
  132. }
  133. // Squash to unique numbers
  134. currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers =
  135. currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers.filter((lvn, index, self) => self.indexOf(lvn) === index);
  136. }
  137. }
  138. }