MusicPartManagerIterator.ts 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. import {MusicPartManager} from "./MusicPartManager";
  2. import {Fraction} from "../../Common/DataObjects/Fraction";
  3. import {Repetition} from "../MusicSource/Repetition";
  4. import {DynamicsContainer} from "../VoiceData/HelperObjects/DynamicsContainer";
  5. import {MappingSourceMusicPart} from "../MusicSource/MappingSourceMusicPart";
  6. import {SourceMeasure} from "../VoiceData/SourceMeasure";
  7. import {VoiceEntry} from "../VoiceData/VoiceEntry";
  8. import {Instrument} from "../Instrument";
  9. import {VerticalSourceStaffEntryContainer} from "../VoiceData/VerticalSourceStaffEntryContainer";
  10. import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
  11. import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
  12. import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
  13. import {ContinuousDynamicExpression} from "../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
  14. import {InstantaneousDynamicExpression} from "../VoiceData/Expressions/InstantaneousDynamicExpression";
  15. import {MultiTempoExpression} from "../VoiceData/Expressions/MultiTempoExpression";
  16. import {AbstractExpression} from "../VoiceData/Expressions/AbstractExpression";
  17. import log from "loglevel";
  18. export class MusicPartManagerIterator {
  19. constructor(manager: MusicPartManager, startTimestamp?: Fraction, endTimestamp?: Fraction) {
  20. try {
  21. this.frontReached = true;
  22. this.manager = manager;
  23. this.currentVoiceEntries = undefined;
  24. this.frontReached = false;
  25. for (const rep of manager.MusicSheet.Repetitions) {
  26. this.setRepetitionIterationCount(rep, 1);
  27. }
  28. this.activeDynamicExpressions = new Array(manager.MusicSheet.getCompleteNumberOfStaves());
  29. this.currentMeasure = this.manager.MusicSheet.SourceMeasures[0];
  30. if (startTimestamp === undefined) { return; }
  31. do {
  32. this.moveToNext();
  33. } while ((this.currentVoiceEntries === undefined || this.currentTimeStamp.lt(startTimestamp)) && !this.endReached);
  34. for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
  35. if (this.activeDynamicExpressions[staffIndex] !== undefined) {
  36. if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
  37. const continuousDynamic: ContinuousDynamicExpression =
  38. <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
  39. this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
  40. } else {
  41. const instantaneousDynamic: InstantaneousDynamicExpression =
  42. <InstantaneousDynamicExpression>this.activeDynamicExpressions[staffIndex];
  43. this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaneousDynamic, staffIndex));
  44. }
  45. }
  46. }
  47. this.currentTempoChangingExpression = this.activeTempoExpression;
  48. } catch (err) {
  49. log.info("MusicPartManagerIterator: " + err);
  50. }
  51. }
  52. public backJumpOccurred: boolean;
  53. public forwardJumpOccurred: boolean;
  54. private manager: MusicPartManager;
  55. private currentMappingPart: MappingSourceMusicPart;
  56. private currentMeasure: SourceMeasure;
  57. private currentMeasureIndex: number = 0;
  58. private currentPartIndex: number = 0;
  59. private currentVoiceEntryIndex: number = -1;
  60. private currentDynamicEntryIndex: number = 0;
  61. private currentTempoEntryIndex: number = 0;
  62. private currentVoiceEntries: VoiceEntry[];
  63. private currentDynamicChangingExpressions: DynamicsContainer[] = [];
  64. private currentTempoChangingExpression: MultiTempoExpression;
  65. // FIXME: replace these two with a real Dictionary!
  66. private repetitionIterationCountDictKeys: Repetition[];
  67. private repetitionIterationCountDictValues: number[];
  68. private currentRepetition: Repetition = undefined;
  69. private endReached: boolean = false;
  70. private frontReached: boolean = false;
  71. private currentTimeStamp: Fraction = new Fraction(0, 1);
  72. private currentEnrolledMeasureTimestamp: Fraction = new Fraction(0, 1);
  73. private currentVerticalContainerInMeasureTimestamp: Fraction = new Fraction(0, 1);
  74. private jumpResponsibleRepetition: Repetition = undefined;
  75. private activeDynamicExpressions: AbstractExpression[] = [];
  76. private activeTempoExpression: MultiTempoExpression;
  77. public get EndReached(): boolean {
  78. return this.endReached;
  79. }
  80. public get FrontReached(): boolean {
  81. return this.frontReached;
  82. }
  83. public get CurrentMeasure(): SourceMeasure {
  84. return this.currentMeasure;
  85. }
  86. public get CurrentRepetition(): Repetition {
  87. return this.currentRepetition;
  88. }
  89. public get CurrentRepetitionIteration(): number {
  90. if (this.CurrentRepetition !== undefined) {
  91. return this.getRepetitionIterationCount(this.CurrentRepetition);
  92. }
  93. return 0;
  94. }
  95. public get CurrentJumpResponsibleRepetitionIterationBeforeJump(): number {
  96. if (this.jumpResponsibleRepetition !== undefined) {
  97. return this.getRepetitionIterationCount(this.jumpResponsibleRepetition) - 1;
  98. }
  99. return 0;
  100. }
  101. public get CurrentVoiceEntries(): VoiceEntry[] {
  102. return this.currentVoiceEntries;
  103. }
  104. public get CurrentMeasureIndex(): number {
  105. return this.currentMeasureIndex;
  106. }
  107. public get CurrentEnrolledTimestamp(): Fraction {
  108. return Fraction.plus(this.currentEnrolledMeasureTimestamp, this.currentVerticalContainerInMeasureTimestamp);
  109. }
  110. public get CurrentSourceTimestamp(): Fraction {
  111. return this.currentTimeStamp;
  112. }
  113. public get JumpOccurred(): boolean {
  114. return this.backJumpOccurred || this.forwardJumpOccurred;
  115. }
  116. public get ActiveTempoExpression(): MultiTempoExpression {
  117. return this.activeTempoExpression;
  118. }
  119. public get ActiveDynamicExpressions(): AbstractExpression[] {
  120. return this.activeDynamicExpressions;
  121. }
  122. public get CurrentTempoChangingExpression(): MultiTempoExpression {
  123. return this.currentTempoChangingExpression;
  124. }
  125. public get JumpResponsibleRepetition(): Repetition {
  126. return this.jumpResponsibleRepetition;
  127. }
  128. /**
  129. * Creates a clone of this iterator which has the same actual position.
  130. */
  131. public clone(): MusicPartManagerIterator {
  132. const ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.manager);
  133. ret.currentVoiceEntryIndex = this.currentVoiceEntryIndex;
  134. ret.currentMappingPart = this.currentMappingPart;
  135. ret.currentPartIndex = this.currentPartIndex;
  136. ret.currentVoiceEntries = this.currentVoiceEntries;
  137. ret.endReached = this.endReached;
  138. ret.frontReached = this.frontReached;
  139. return ret;
  140. }
  141. /**
  142. * Returns the visible voice entries for the provided instrument of the current iterator position.
  143. * @param instrument
  144. * Returns: A List of voiceEntries. If there are no entries the List has a Count of 0 (it does not return null).
  145. */
  146. public CurrentVisibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
  147. const voiceEntries: VoiceEntry[] = [];
  148. if (this.currentVoiceEntries === undefined) {
  149. return voiceEntries;
  150. }
  151. if (instrument !== undefined) {
  152. for (const entry of this.currentVoiceEntries) {
  153. if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
  154. this.getVisibleEntries(entry, voiceEntries);
  155. return voiceEntries;
  156. }
  157. }
  158. } else {
  159. for (const entry of this.currentVoiceEntries) {
  160. this.getVisibleEntries(entry, voiceEntries);
  161. }
  162. }
  163. return voiceEntries;
  164. }
  165. /**
  166. * Returns the visible voice entries for the provided instrument of the current iterator position.
  167. * @param instrument
  168. * Returns: A List of voiceEntries. If there are no entries the List has a Count of 0 (it does not return null).
  169. */
  170. public CurrentAudibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
  171. const voiceEntries: VoiceEntry[] = [];
  172. if (this.currentVoiceEntries === undefined) {
  173. return voiceEntries;
  174. }
  175. if (instrument !== undefined) {
  176. for (const entry of this.currentVoiceEntries) {
  177. if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
  178. this.getAudibleEntries(entry, voiceEntries);
  179. return voiceEntries;
  180. }
  181. }
  182. } else {
  183. for (const entry of this.currentVoiceEntries) {
  184. this.getAudibleEntries(entry, voiceEntries);
  185. }
  186. }
  187. return voiceEntries;
  188. }
  189. /**
  190. * Returns the audible dynamics of the current iterator position.
  191. * Returns: A List of Dynamics. If there are no entries the List has a Count of 0 (it does not return null).
  192. */
  193. public getCurrentDynamicChangingExpressions(): DynamicsContainer[] {
  194. return this.currentDynamicChangingExpressions;
  195. }
  196. /**
  197. * Returns the score following voice entries for the provided instrument of the current iterator position.
  198. * @param instrument
  199. * Returns: A List of voiceEntries. If there are no entries the List has a Count of 0
  200. * (it does not return null).
  201. */
  202. public CurrentScoreFollowingVoiceEntries(instrument?: Instrument): VoiceEntry[] {
  203. const voiceEntries: VoiceEntry[] = [];
  204. if (this.currentVoiceEntries === undefined) {
  205. return voiceEntries;
  206. }
  207. if (instrument !== undefined) {
  208. for (const entry of this.currentVoiceEntries) {
  209. if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
  210. this.getScoreFollowingEntries(entry, voiceEntries);
  211. return voiceEntries;
  212. }
  213. }
  214. } else {
  215. for (const entry of this.currentVoiceEntries) {
  216. this.getScoreFollowingEntries(entry, voiceEntries);
  217. }
  218. }
  219. return voiceEntries;
  220. }
  221. //public currentPlaybackSettings(): PlaybackSettings {
  222. // return this.manager.MusicSheet.SheetPlaybackSetting;
  223. //}
  224. public moveToNext(): void {
  225. this.forwardJumpOccurred = this.backJumpOccurred = false;
  226. if (this.endReached) { return; }
  227. if (this.currentVoiceEntries !== undefined) {
  228. this.currentVoiceEntries = [];
  229. }
  230. this.recursiveMove();
  231. if (this.currentMeasure === undefined) {
  232. this.currentTimeStamp = new Fraction(99999, 1);
  233. }
  234. }
  235. public moveToNextVisibleVoiceEntry(notesOnly: boolean): void {
  236. while (!this.endReached) {
  237. this.moveToNext();
  238. if (this.checkEntries(notesOnly)) { return; }
  239. }
  240. }
  241. private resetRepetitionIterationCount(repetition: Repetition): number {
  242. this.setRepetitionIterationCount(repetition, 1);
  243. return 1;
  244. }
  245. private incrementRepetitionIterationCount(repetition: Repetition): number {
  246. if (this.repetitionIterationCountDictKeys.indexOf(repetition) === -1) {
  247. return this.setRepetitionIterationCount(repetition, 1);
  248. } else {
  249. return this.setRepetitionIterationCount(repetition, this.getRepetitionIterationCount(repetition) + 1);
  250. }
  251. }
  252. private setRepetitionIterationCount(repetition: Repetition, iterationCount: number): number {
  253. const i: number = this.repetitionIterationCountDictKeys.indexOf(repetition);
  254. if (i === -1) {
  255. this.repetitionIterationCountDictKeys.push(repetition);
  256. this.repetitionIterationCountDictValues.push(iterationCount);
  257. } else {
  258. this.repetitionIterationCountDictValues[i] = iterationCount;
  259. }
  260. return iterationCount;
  261. }
  262. private getRepetitionIterationCount(rep: Repetition): number {
  263. const i: number = this.repetitionIterationCountDictKeys.indexOf(rep);
  264. if (i !== -1) {
  265. return this.repetitionIterationCountDictValues[i];
  266. }
  267. }
  268. /* private moveTempoIndexToTimestamp(measureNumber: number): void {
  269. for (let index: number = 0; index < this.manager.MusicSheet.TimestampSortedTempoExpressionsList.length; index++) {
  270. if (this.manager.MusicSheet.TimestampSortedTempoExpressionsList[index].SourceMeasureParent.MeasureNumber >= measureNumber) {
  271. this.currentTempoEntryIndex = Math.Max(-1, index - 1);
  272. return
  273. }
  274. }
  275. }
  276. private getNextTempoEntryTimestamp(): Fraction {
  277. if (this.currentTempoEntryIndex >= this.manager.MusicSheet.TimestampSortedTempoExpressionsList.length - 1) {
  278. return new Fraction(99999, 1);
  279. }
  280. return this.manager.MusicSheet.TimestampSortedTempoExpressionsList[this.currentTempoEntryIndex + 1].SourceMeasureParent.AbsoluteTimestamp +
  281. this.manager.MusicSheet.TimestampSortedTempoExpressionsList[this.currentTempoEntryIndex + 1].Timestamp;
  282. }
  283. private moveToNextDynamic(): void {
  284. this.currentDynamicEntryIndex++;
  285. this.currentDynamicChangingExpressions.Clear();
  286. let curDynamicEntry: DynamicsContainer = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[this.currentDynamicEntryIndex];
  287. this.currentDynamicChangingExpressions.push(curDynamicEntry);
  288. let tsNow: Fraction = curDynamicEntry.parMultiExpression().AbsoluteTimestamp;
  289. for (let i: number = this.currentDynamicEntryIndex + 1; i < this.manager.MusicSheet.TimestampSortedDynamicExpressionsList.length; i++) {
  290. curDynamicEntry = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[i];
  291. if ((curDynamicEntry.parMultiExpression().AbsoluteTimestamp !== tsNow)) { break; }
  292. this.currentDynamicEntryIndex = i;
  293. this.currentDynamicChangingExpressions.push(curDynamicEntry);
  294. }
  295. }
  296. private moveDynamicIndexToTimestamp(absoluteTimestamp: Fraction): void {
  297. let dynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
  298. for (let index: number = 0; index < dynamics.length; index++) {
  299. if (dynamics[index].parMultiExpression().AbsoluteTimestamp.gte(absoluteTimestamp)) {
  300. this.currentDynamicEntryIndex = Math.Max(0, index - 1);
  301. return
  302. }
  303. }
  304. }
  305. private getNextDynamicsEntryTimestamp(): Fraction {
  306. if (this.currentDynamicEntryIndex >= this.manager.MusicSheet.TimestampSortedDynamicExpressionsList.length - 1) {
  307. return new Fraction(99999, 1);
  308. }
  309. return this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[this.currentDynamicEntryIndex + 1].parMultiExpression().AbsoluteTimestamp;
  310. }
  311. */
  312. private handleRepetitionsAtMeasureBegin(): void {
  313. for (let idx: number = 0, len: number = this.currentMeasure.FirstRepetitionInstructions.length; idx < len; ++idx) {
  314. const repetitionInstruction: RepetitionInstruction = this.currentMeasure.FirstRepetitionInstructions[idx];
  315. if (repetitionInstruction.parentRepetition === undefined) { continue; }
  316. const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
  317. this.currentRepetition = currentRepetition;
  318. if (currentRepetition.StartIndex === this.currentMeasureIndex) {
  319. if (
  320. this.JumpResponsibleRepetition !== undefined &&
  321. currentRepetition !== this.JumpResponsibleRepetition &&
  322. currentRepetition.StartIndex >= this.JumpResponsibleRepetition.StartIndex &&
  323. currentRepetition.EndIndex <= this.JumpResponsibleRepetition.EndIndex
  324. ) {
  325. this.resetRepetitionIterationCount(currentRepetition);
  326. }
  327. }
  328. }
  329. }
  330. private handleRepetitionsAtMeasureEnd(): void {
  331. for (let idx: number = 0, len: number = this.currentMeasure.LastRepetitionInstructions.length; idx < len; ++idx) {
  332. const repetitionInstruction: RepetitionInstruction = this.currentMeasure.LastRepetitionInstructions[idx];
  333. const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
  334. if (currentRepetition === undefined) { continue; }
  335. if (currentRepetition.BackwardJumpInstructions.indexOf(repetitionInstruction) > -1) {
  336. if (this.getRepetitionIterationCount(currentRepetition) < currentRepetition.UserNumberOfRepetitions) {
  337. this.doBackJump(currentRepetition);
  338. this.backJumpOccurred = true;
  339. return;
  340. }
  341. }
  342. if (repetitionInstruction === currentRepetition.forwardJumpInstruction) {
  343. if (
  344. this.JumpResponsibleRepetition !== undefined
  345. && currentRepetition !== this.JumpResponsibleRepetition
  346. && currentRepetition.StartIndex >= this.JumpResponsibleRepetition.StartIndex
  347. && currentRepetition.EndIndex <= this.JumpResponsibleRepetition.EndIndex
  348. ) {
  349. this.resetRepetitionIterationCount(currentRepetition);
  350. }
  351. const forwardJumpTargetMeasureIndex: number = currentRepetition.getForwardJumpTargetForIteration(
  352. this.getRepetitionIterationCount(currentRepetition)
  353. );
  354. if (forwardJumpTargetMeasureIndex >= 0) {
  355. this.currentMeasureIndex = forwardJumpTargetMeasureIndex;
  356. this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
  357. this.currentVoiceEntryIndex = -1;
  358. this.jumpResponsibleRepetition = currentRepetition;
  359. this.forwardJumpOccurred = true;
  360. return;
  361. }
  362. if (forwardJumpTargetMeasureIndex === -2) {
  363. this.endReached = true;
  364. }
  365. }
  366. }
  367. this.currentMeasureIndex++;
  368. if (this.JumpResponsibleRepetition !== undefined && this.currentMeasureIndex > this.JumpResponsibleRepetition.EndIndex) {
  369. this.jumpResponsibleRepetition = undefined;
  370. }
  371. }
  372. private doBackJump(currentRepetition: Repetition): void {
  373. this.currentMeasureIndex = currentRepetition.getBackwardJumpTarget();
  374. this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
  375. this.currentVoiceEntryIndex = -1;
  376. this.incrementRepetitionIterationCount(currentRepetition);
  377. this.jumpResponsibleRepetition = currentRepetition;
  378. }
  379. private activateCurrentRhythmInstructions(): void {
  380. if (
  381. this.currentMeasure !== undefined &&
  382. this.currentMeasure.FirstInstructionsStaffEntries.length > 0 &&
  383. this.currentMeasure.FirstInstructionsStaffEntries[0] !== undefined
  384. ) {
  385. const instructions: AbstractNotationInstruction[] = this.currentMeasure.FirstInstructionsStaffEntries[0].Instructions;
  386. for (let idx: number = 0, len: number = instructions.length; idx < len; ++idx) {
  387. const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
  388. if (abstractNotationInstruction instanceof RhythmInstruction) {
  389. this.manager.MusicSheet.SheetPlaybackSetting.rhythm = (<RhythmInstruction>abstractNotationInstruction).Rhythm;
  390. }
  391. }
  392. }
  393. }
  394. private activateCurrentDynamicOrTempoInstructions(): void {
  395. const timeSortedDynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
  396. while (
  397. this.currentDynamicEntryIndex > 0 && (
  398. this.currentDynamicEntryIndex >= timeSortedDynamics.length ||
  399. this.CurrentSourceTimestamp.lte(timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp)
  400. )
  401. ) {
  402. this.currentDynamicEntryIndex--;
  403. }
  404. while (
  405. this.currentDynamicEntryIndex < timeSortedDynamics.length &&
  406. timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp.lt(this.CurrentSourceTimestamp)
  407. ) {
  408. this.currentDynamicEntryIndex++;
  409. }
  410. while (
  411. this.currentDynamicEntryIndex < timeSortedDynamics.length
  412. && timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp.Equals(this.CurrentSourceTimestamp)
  413. ) {
  414. const dynamicsContainer: DynamicsContainer = timeSortedDynamics[this.currentDynamicEntryIndex];
  415. const staffIndex: number = dynamicsContainer.staffNumber;
  416. if (this.CurrentSourceTimestamp.Equals(dynamicsContainer.parMultiExpression().AbsoluteTimestamp)) {
  417. if (dynamicsContainer.continuousDynamicExpression !== undefined) {
  418. this.activeDynamicExpressions[staffIndex] = dynamicsContainer.continuousDynamicExpression;
  419. } else if (dynamicsContainer.instantaneousDynamicExpression !== undefined) {
  420. this.activeDynamicExpressions[staffIndex] = dynamicsContainer.instantaneousDynamicExpression;
  421. }
  422. }
  423. this.currentDynamicEntryIndex++;
  424. }
  425. this.currentDynamicChangingExpressions = [];
  426. for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
  427. if (this.activeDynamicExpressions[staffIndex] !== undefined) {
  428. let startTime: Fraction;
  429. let endTime: Fraction;
  430. if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
  431. const continuousDynamic: ContinuousDynamicExpression = <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
  432. startTime = continuousDynamic.StartMultiExpression.AbsoluteTimestamp;
  433. endTime = continuousDynamic.EndMultiExpression.AbsoluteTimestamp;
  434. if (startTime.lte(this.CurrentSourceTimestamp) && this.CurrentSourceTimestamp.lte(endTime)) {
  435. this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
  436. }
  437. } else {
  438. const instantaneousDynamic: InstantaneousDynamicExpression = <InstantaneousDynamicExpression>this.activeDynamicExpressions[staffIndex];
  439. if (this.CurrentSourceTimestamp.Equals(instantaneousDynamic.ParentMultiExpression.AbsoluteTimestamp)) {
  440. this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaneousDynamic, staffIndex));
  441. }
  442. }
  443. }
  444. }
  445. const timeSortedTempoExpressions: MultiTempoExpression[] = this.manager.MusicSheet.TimestampSortedTempoExpressionsList;
  446. while (this.currentTempoEntryIndex > 0 && (
  447. this.currentTempoEntryIndex >= timeSortedTempoExpressions.length
  448. || this.CurrentSourceTimestamp.lte(timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp)
  449. )) {
  450. this.currentTempoEntryIndex--;
  451. }
  452. while (
  453. this.currentTempoEntryIndex < timeSortedTempoExpressions.length &&
  454. timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp.lt(this.CurrentSourceTimestamp)
  455. ) {
  456. this.currentTempoEntryIndex++;
  457. }
  458. while (
  459. this.currentTempoEntryIndex < timeSortedTempoExpressions.length
  460. && timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp.Equals(this.CurrentSourceTimestamp)
  461. ) {
  462. this.activeTempoExpression = timeSortedTempoExpressions[this.currentTempoEntryIndex];
  463. this.currentTempoEntryIndex++;
  464. }
  465. this.currentTempoChangingExpression = undefined;
  466. if (this.activeTempoExpression !== undefined) {
  467. let endTime: Fraction = this.activeTempoExpression.AbsoluteTimestamp;
  468. if (this.activeTempoExpression.ContinuousTempo !== undefined) {
  469. endTime = this.activeTempoExpression.ContinuousTempo.AbsoluteEndTimestamp;
  470. }
  471. if ( this.activeTempoExpression.AbsoluteTimestamp.lte(this.CurrentSourceTimestamp)
  472. || this.CurrentSourceTimestamp.lte(endTime)
  473. ) {
  474. this.currentTempoChangingExpression = this.activeTempoExpression;
  475. }
  476. }
  477. }
  478. private recursiveMove(): void {
  479. this.currentVoiceEntryIndex++;
  480. if (this.currentVoiceEntryIndex === 0) {
  481. this.handleRepetitionsAtMeasureBegin();
  482. this.activateCurrentRhythmInstructions();
  483. }
  484. // everything fine, no complications
  485. if (this.currentVoiceEntryIndex >= 0 && this.currentVoiceEntryIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length) {
  486. const currentContainer: VerticalSourceStaffEntryContainer = this.currentMeasure.VerticalSourceStaffEntryContainers[this.currentVoiceEntryIndex];
  487. this.currentVoiceEntries = this.getVoiceEntries(currentContainer);
  488. this.currentVerticalContainerInMeasureTimestamp = currentContainer.Timestamp;
  489. this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
  490. const selectionEnd: Fraction = this.manager.MusicSheet.SelectionEnd;
  491. // TODO handle selectionEnd undefined, can happen in Beethoven Ferne Geliebte
  492. if (selectionEnd && this.currentTimeStamp.gte(selectionEnd)) {
  493. this.endReached = true;
  494. }
  495. this.activateCurrentDynamicOrTempoInstructions();
  496. return;
  497. }
  498. this.currentEnrolledMeasureTimestamp.Add(this.currentMeasure.Duration);
  499. this.handleRepetitionsAtMeasureEnd();
  500. if (this.currentMeasureIndex >= 0 && this.currentMeasureIndex < this.manager.MusicSheet.SourceMeasures.length) {
  501. this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
  502. this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
  503. this.currentVoiceEntryIndex = -1;
  504. this.recursiveMove();
  505. return;
  506. }
  507. // we reached the end
  508. this.currentVerticalContainerInMeasureTimestamp = new Fraction();
  509. this.currentMeasure = undefined;
  510. this.currentVoiceEntries = undefined;
  511. this.endReached = true;
  512. }
  513. /**
  514. * helper function for moveToNextVisibleVoiceEntry and moveToPreviousVisibleVoiceEntry
  515. * Get all entries and check if there is at least one valid entry in the list
  516. * @param notesOnly
  517. */
  518. private checkEntries(notesOnly: boolean): boolean {
  519. const tlist: VoiceEntry[] = this.CurrentVisibleVoiceEntries();
  520. if (tlist.length > 0) {
  521. if (!notesOnly) { return true; }
  522. for (let idx: number = 0, len: number = tlist.length; idx < len; ++idx) {
  523. const entry: VoiceEntry = tlist[idx];
  524. if (entry.Notes[0].Pitch !== undefined) { return true; }
  525. }
  526. }
  527. return false;
  528. }
  529. private getVisibleEntries(entry: VoiceEntry, visibleEntries: VoiceEntry[]): void {
  530. if (entry.ParentVoice.Visible) {
  531. visibleEntries.push(entry);
  532. }
  533. }
  534. private getAudibleEntries(entry: VoiceEntry, audibleEntries: VoiceEntry[]): void {
  535. if (entry.ParentVoice.Audible) {
  536. audibleEntries.push(entry);
  537. }
  538. }
  539. private getScoreFollowingEntries(entry: VoiceEntry, followingEntries: VoiceEntry[]): void {
  540. if (entry.ParentVoice.Following && entry.ParentVoice.Parent.Following) {
  541. followingEntries.push(entry);
  542. }
  543. }
  544. private getVoiceEntries(container: VerticalSourceStaffEntryContainer): VoiceEntry[] {
  545. const entries: VoiceEntry[] = [];
  546. for (const sourceStaffEntry of container.StaffEntries) {
  547. if (sourceStaffEntry === undefined) { continue; }
  548. for (const voiceEntry of sourceStaffEntry.VoiceEntries) {
  549. entries.push(voiceEntry);
  550. }
  551. }
  552. return entries;
  553. }
  554. }