RepetitionInstructionReader.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import {MusicSheet} from "../../MusicSheet";
  2. import {IXmlElement} from "../../../Common/FileIO/Xml";
  3. import {SourceMeasure} from "../../VoiceData/SourceMeasure";
  4. import {RepetitionInstruction, RepetitionInstructionEnum, AlignmentType} from "../../VoiceData/Instructions/RepetitionInstruction";
  5. import {StringUtil} from "../../../Common/Strings/StringUtil";
  6. export class RepetitionInstructionReader {
  7. /**
  8. * A global list of all repetition instructions in the musicsheet.
  9. */
  10. public repetitionInstructions: RepetitionInstruction[];
  11. public xmlMeasureList: IXmlElement[][];
  12. private musicSheet: MusicSheet;
  13. private currentMeasureIndex: number;
  14. public set MusicSheet(value: MusicSheet) {
  15. this.musicSheet = value;
  16. this.xmlMeasureList = new Array(this.musicSheet.Instruments.length);
  17. this.repetitionInstructions = [];
  18. }
  19. /**
  20. * is called when starting reading an xml measure
  21. * @param measure
  22. * @param currentMeasureIndex
  23. */
  24. public prepareReadingMeasure(measure: SourceMeasure, currentMeasureIndex: number): void {
  25. this.currentMeasureIndex = currentMeasureIndex;
  26. }
  27. public handleLineRepetitionInstructions(barlineNode: IXmlElement, pieceEndingDetected: boolean): void {
  28. pieceEndingDetected = false;
  29. if (barlineNode.elements().length > 0) {
  30. let location: string = "";
  31. let hasRepeat: boolean = false;
  32. let direction: string = "";
  33. let type: string = "";
  34. let style: string = "";
  35. let endingIndices: number[] = [];
  36. // read barline style
  37. let styleNode: IXmlElement = barlineNode.element("bar-style");
  38. // if location is ommited in Xml, right is implied (from documentation)
  39. if (styleNode !== undefined) {
  40. style = styleNode.value;
  41. }
  42. if (barlineNode.attributes().length > 0 && barlineNode.attribute("location") !== undefined) {
  43. location = barlineNode.attribute("location").value;
  44. } else {
  45. location = "right";
  46. }
  47. let barlineNodeElements: IXmlElement[] = barlineNode.elements();
  48. // read repeat- or ending line information
  49. for (let idx: number = 0, len: number = barlineNodeElements.length; idx < len; ++idx) {
  50. let childNode: IXmlElement = barlineNodeElements[idx];
  51. if ("repeat" === childNode.name && childNode.hasAttributes) {
  52. hasRepeat = true;
  53. direction = childNode.attribute("direction").value;
  54. } else if ( "ending" === childNode.name && childNode.hasAttributes &&
  55. childNode.attribute("type") !== undefined && childNode.attribute("number") !== undefined) {
  56. type = childNode.attribute("type").value;
  57. let num: string = childNode.attribute("number").value;
  58. // Parse the given ending indices:
  59. // handle cases like: "1, 2" or "1 + 2" or even "1 - 3, 6"
  60. let separatedEndingIndices: string[] = num.split("[,+]");
  61. for (let idx2: number = 0, len2: number = separatedEndingIndices.length; idx2 < len2; ++idx2) {
  62. let separatedEndingIndex: string = separatedEndingIndices[idx2];
  63. let indices: string[] = separatedEndingIndex.match("[0-9]");
  64. // check if possibly something like "1-3" is given..
  65. if (separatedEndingIndex.search("-") !== -1 && indices.length === 2) {
  66. let startIndex: number = parseInt(indices[0], 10);
  67. let endIndex: number = parseInt(indices[1], 10);
  68. for (let index: number = startIndex; index <= endIndex; index++) {
  69. endingIndices.push(index);
  70. }
  71. } else {
  72. for (let idx3: number = 0, len3: number = indices.length; idx3 < len3; ++idx3) {
  73. let index: string = indices[idx3];
  74. endingIndices.push(parseInt(index, 10));
  75. }
  76. }
  77. }
  78. }
  79. }
  80. // reset measure counter if not lastMeasure
  81. if (style === "light-heavy" && endingIndices.length === 0 && !hasRepeat) {
  82. pieceEndingDetected = true;
  83. }
  84. if (hasRepeat || endingIndices.length > 0) {
  85. if (location === "left") {
  86. if (type === "start") {
  87. let newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.Ending,
  88. AlignmentType.Begin, undefined, endingIndices);
  89. this.addInstruction(this.repetitionInstructions, newInstruction);
  90. }
  91. if (direction === "forward") {
  92. // start new Repetition
  93. let newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.StartLine);
  94. this.addInstruction(this.repetitionInstructions, newInstruction);
  95. }
  96. } else { // location right
  97. if (type === "stop" || type === "discontinue") {
  98. let newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.Ending,
  99. AlignmentType.End, undefined, endingIndices);
  100. this.addInstruction(this.repetitionInstructions, newInstruction);
  101. }
  102. if (direction === "backward") {
  103. let newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.BackJumpLine);
  104. this.addInstruction(this.repetitionInstructions, newInstruction);
  105. }
  106. }
  107. }
  108. }
  109. }
  110. public handleRepetitionInstructionsFromWordsOrSymbols(directionTypeNode: IXmlElement, relativeMeasurePosition: number): boolean {
  111. let wordsNode: IXmlElement = directionTypeNode.element("words");
  112. if (wordsNode !== undefined) {
  113. // must Trim string and ToLower before compare
  114. let innerText: string = wordsNode.value.trim().toLowerCase();
  115. if (StringUtil.StringContainsSeparatedWord(innerText, "d.s. al fine") ||
  116. StringUtil.StringContainsSeparatedWord(innerText, "d. s. al fine")) {
  117. let measureIndex: number = this.currentMeasureIndex;
  118. if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
  119. measureIndex--;
  120. }
  121. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DalSegnoAlFine);
  122. this.addInstruction(this.repetitionInstructions, newInstruction);
  123. return true;
  124. }
  125. if (StringUtil.StringContainsSeparatedWord(innerText, "d.s. al coda") ||
  126. StringUtil.StringContainsSeparatedWord(innerText, "d. s. al coda")) {
  127. let measureIndex: number = this.currentMeasureIndex;
  128. if (relativeMeasurePosition < 0.5) {
  129. measureIndex--;
  130. }
  131. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DalSegnoAlCoda);
  132. this.addInstruction(this.repetitionInstructions, newInstruction);
  133. return true;
  134. }
  135. if (StringUtil.StringContainsSeparatedWord(innerText, "d.c. al fine") ||
  136. StringUtil.StringContainsSeparatedWord(innerText, "d. c. al fine")) {
  137. let measureIndex: number = this.currentMeasureIndex;
  138. if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
  139. measureIndex--;
  140. }
  141. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DaCapoAlFine);
  142. this.addInstruction(this.repetitionInstructions, newInstruction);
  143. return true;
  144. }
  145. if (StringUtil.StringContainsSeparatedWord(innerText, "d.c. al coda") ||
  146. StringUtil.StringContainsSeparatedWord(innerText, "d. c. al coda")) {
  147. let measureIndex: number = this.currentMeasureIndex;
  148. if (relativeMeasurePosition < 0.5) {
  149. measureIndex--;
  150. }
  151. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DaCapoAlCoda);
  152. this.addInstruction(this.repetitionInstructions, newInstruction);
  153. return true;
  154. }
  155. if (StringUtil.StringContainsSeparatedWord(innerText, "d.c.") ||
  156. StringUtil.StringContainsSeparatedWord(innerText, "d. c.") ||
  157. StringUtil.StringContainsSeparatedWord(innerText, "dacapo") ||
  158. StringUtil.StringContainsSeparatedWord(innerText, "da capo")) {
  159. let measureIndex: number = this.currentMeasureIndex;
  160. if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
  161. measureIndex--;
  162. }
  163. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DaCapo);
  164. this.addInstruction(this.repetitionInstructions, newInstruction);
  165. return true;
  166. }
  167. if (StringUtil.StringContainsSeparatedWord(innerText, "d.s.") ||
  168. StringUtil.StringContainsSeparatedWord(innerText, "d. s.") ||
  169. StringUtil.StringContainsSeparatedWord(innerText, "dalsegno") ||
  170. StringUtil.StringContainsSeparatedWord(innerText, "dal segno")) {
  171. let measureIndex: number = this.currentMeasureIndex;
  172. if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
  173. measureIndex--;
  174. }
  175. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DalSegno);
  176. this.addInstruction(this.repetitionInstructions, newInstruction);
  177. return true;
  178. }
  179. if (StringUtil.StringContainsSeparatedWord(innerText, "tocoda") ||
  180. StringUtil.StringContainsSeparatedWord(innerText, "to coda") ||
  181. StringUtil.StringContainsSeparatedWord(innerText, "a coda") ||
  182. StringUtil.StringContainsSeparatedWord(innerText, "a la coda")) {
  183. let measureIndex: number = this.currentMeasureIndex;
  184. if (relativeMeasurePosition < 0.5) {
  185. measureIndex--;
  186. }
  187. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.ToCoda);
  188. this.addInstruction(this.repetitionInstructions, newInstruction);
  189. return true;
  190. }
  191. if (StringUtil.StringContainsSeparatedWord(innerText, "fine")) {
  192. let measureIndex: number = this.currentMeasureIndex;
  193. if (relativeMeasurePosition < 0.5) {
  194. measureIndex--;
  195. }
  196. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Fine);
  197. this.addInstruction(this.repetitionInstructions, newInstruction);
  198. return true;
  199. }
  200. if (StringUtil.StringContainsSeparatedWord(innerText, "coda")) {
  201. let measureIndex: number = this.currentMeasureIndex;
  202. if (relativeMeasurePosition > 0.5) {
  203. measureIndex++;
  204. }
  205. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Coda);
  206. this.addInstruction(this.repetitionInstructions, newInstruction);
  207. return true;
  208. }
  209. if (StringUtil.StringContainsSeparatedWord(innerText, "segno")) {
  210. let measureIndex: number = this.currentMeasureIndex;
  211. if (relativeMeasurePosition > 0.5) {
  212. measureIndex++;
  213. }
  214. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Segno);
  215. this.addInstruction(this.repetitionInstructions, newInstruction);
  216. return true;
  217. }
  218. } else if (directionTypeNode.element("segno") !== undefined) {
  219. let measureIndex: number = this.currentMeasureIndex;
  220. if (relativeMeasurePosition > 0.5) {
  221. measureIndex++;
  222. }
  223. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Segno);
  224. this.addInstruction(this.repetitionInstructions, newInstruction);
  225. return true;
  226. } else if (directionTypeNode.element("coda") !== undefined) {
  227. let measureIndex: number = this.currentMeasureIndex;
  228. if (relativeMeasurePosition > 0.5) {
  229. measureIndex++;
  230. }
  231. let newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Coda);
  232. this.addInstruction(this.repetitionInstructions, newInstruction);
  233. return true;
  234. }
  235. return false;
  236. }
  237. public removeRedundantInstructions(): void {
  238. let segnoCount: number = 0;
  239. let codaCount: number = 0;
  240. let fineCount: number = 0;
  241. let toCodaCount: number = 0;
  242. let dalSegnaCount: number = 0;
  243. for (let index: number = 0; index < this.repetitionInstructions.length; index++) {
  244. let instruction: RepetitionInstruction = this.repetitionInstructions[index];
  245. switch (instruction.type) {
  246. case RepetitionInstructionEnum.Coda:
  247. if (toCodaCount > 0) {
  248. if (this.findInstructionInPreviousMeasure(index, instruction.measureIndex, RepetitionInstructionEnum.ToCoda)) {
  249. instruction.type = RepetitionInstructionEnum.None;
  250. }
  251. }
  252. if (codaCount === 0 && toCodaCount === 0) {
  253. instruction.type = RepetitionInstructionEnum.ToCoda;
  254. instruction.alignment = AlignmentType.End;
  255. instruction.measureIndex--;
  256. }
  257. break;
  258. case RepetitionInstructionEnum.Segno:
  259. if (segnoCount - dalSegnaCount > 0) { // two segnos in a row
  260. let foundInstruction: boolean = false;
  261. for (let idx: number = 0, len: number = this.repetitionInstructions.length; idx < len; ++idx) {
  262. let instr: RepetitionInstruction = this.repetitionInstructions[idx];
  263. if (instruction.measureIndex - instr.measureIndex === 1) {
  264. switch (instr.type) {
  265. case RepetitionInstructionEnum.BackJumpLine:
  266. if (toCodaCount - codaCount > 0) { // open toCoda existing
  267. instr.type = RepetitionInstructionEnum.DalSegnoAlCoda;
  268. } else {
  269. instr.type = RepetitionInstructionEnum.DalSegno;
  270. }
  271. instruction.type = RepetitionInstructionEnum.None;
  272. foundInstruction = true;
  273. break;
  274. case RepetitionInstructionEnum.DalSegno:
  275. case RepetitionInstructionEnum.DalSegnoAlFine:
  276. case RepetitionInstructionEnum.DalSegnoAlCoda:
  277. instruction.type = RepetitionInstructionEnum.None;
  278. foundInstruction = true;
  279. break;
  280. default:
  281. break;
  282. }
  283. }
  284. if (foundInstruction) {
  285. break;
  286. }
  287. }
  288. if (foundInstruction) {
  289. break;
  290. }
  291. // convert to dal segno instruction:
  292. if (toCodaCount - codaCount > 0) { // open toCoda existing
  293. instruction.type = RepetitionInstructionEnum.DalSegnoAlCoda;
  294. } else {
  295. instruction.type = RepetitionInstructionEnum.DalSegno;
  296. }
  297. instruction.alignment = AlignmentType.End;
  298. instruction.measureIndex--;
  299. }
  300. break;
  301. default:
  302. break;
  303. }
  304. // check if this instruction already exists or is otherwise redundant:
  305. if (this.backwardSearchForPreviousIdenticalInstruction(index, instruction) || instruction.type === RepetitionInstructionEnum.None) {
  306. this.repetitionInstructions.splice(index, 1);
  307. index--;
  308. } else {
  309. switch (instruction.type) {
  310. case RepetitionInstructionEnum.Fine:
  311. fineCount++;
  312. break;
  313. case RepetitionInstructionEnum.ToCoda:
  314. toCodaCount++;
  315. break;
  316. case RepetitionInstructionEnum.Coda:
  317. codaCount++;
  318. break;
  319. case RepetitionInstructionEnum.Segno:
  320. segnoCount++;
  321. break;
  322. case RepetitionInstructionEnum.DalSegnoAlFine:
  323. case RepetitionInstructionEnum.DalSegnoAlCoda:
  324. dalSegnaCount++;
  325. break;
  326. default:
  327. break;
  328. }
  329. }
  330. }
  331. this.repetitionInstructions.sort(RepetitionInstruction.compare);
  332. }
  333. private findInstructionInPreviousMeasure(currentInstructionIndex: number, currentMeasureIndex: number, searchedType: RepetitionInstructionEnum): boolean {
  334. for (let index: number = currentInstructionIndex - 1; index >= 0; index--) {
  335. let instruction: RepetitionInstruction = this.repetitionInstructions[index];
  336. if (currentMeasureIndex - instruction.measureIndex === 1 && instruction.type === searchedType) {
  337. return true;
  338. }
  339. }
  340. return false;
  341. }
  342. private backwardSearchForPreviousIdenticalInstruction(currentInstructionIndex: number, currentInstruction: RepetitionInstruction): boolean {
  343. for (let index: number = currentInstructionIndex - 1; index >= 0; index--) {
  344. let instruction: RepetitionInstruction = this.repetitionInstructions[index];
  345. if (instruction.equals(currentInstruction)) {
  346. return true;
  347. }
  348. }
  349. return false;
  350. }
  351. private addInstruction(currentRepetitionInstructions: RepetitionInstruction[], newInstruction: RepetitionInstruction): void {
  352. let addInstruction: boolean = true;
  353. for (let idx: number = 0, len: number = currentRepetitionInstructions.length; idx < len; ++idx) {
  354. let repetitionInstruction: RepetitionInstruction = currentRepetitionInstructions[idx];
  355. if (newInstruction.equals(repetitionInstruction)) {
  356. addInstruction = false;
  357. break;
  358. }
  359. }
  360. if (addInstruction) {
  361. currentRepetitionInstructions.push(newInstruction);
  362. }
  363. }
  364. }