src/MusicalScore/MusicParts/MusicPartManagerIterator.ts
import {Fraction} from "../../Common/DataObjects/Fraction";
import {Repetition} from "../MusicSource/Repetition";
import {DynamicsContainer} from "../VoiceData/HelperObjects/DynamicsContainer";
import {MappingSourceMusicPart} from "../MusicSource/MappingSourceMusicPart";
import {SourceMeasure} from "../VoiceData/SourceMeasure";
import {VoiceEntry} from "../VoiceData/VoiceEntry";
import {Instrument} from "../Instrument";
import {VerticalSourceStaffEntryContainer} from "../VoiceData/VerticalSourceStaffEntryContainer";
import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
import {ContinuousDynamicExpression} from "../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
import {InstantaneousDynamicExpression} from "../VoiceData/Expressions/InstantaneousDynamicExpression";
import {MultiTempoExpression} from "../VoiceData/Expressions/MultiTempoExpression";
import {AbstractExpression} from "../VoiceData/Expressions/AbstractExpression";
import log from "loglevel";
import { MusicSheet } from "../MusicSheet";
export class MusicPartManagerIterator {
constructor(musicSheet: MusicSheet, startTimestamp?: Fraction, endTimestamp?: Fraction) {
try {
this.frontReached = true;
this.musicSheet = musicSheet;
this.currentVoiceEntries = undefined;
this.frontReached = false;
for (const rep of this.musicSheet.Repetitions) {
this.setRepetitionIterationCount(rep, 1);
}
this.activeDynamicExpressions = new Array(this.musicSheet.getCompleteNumberOfStaves());
this.currentMeasure = this.musicSheet.SourceMeasures[0];
if (!startTimestamp) { return; }
do {
this.moveToNext();
} while ((!this.currentVoiceEntries || this.currentTimeStamp.lt(startTimestamp)) && !this.endReached);
for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
if (this.activeDynamicExpressions[staffIndex]) {
if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
const continuousDynamic: ContinuousDynamicExpression =
<ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
} else {
const instantaneousDynamic: InstantaneousDynamicExpression =
<InstantaneousDynamicExpression>this.activeDynamicExpressions[staffIndex];
this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaneousDynamic, staffIndex));
}
}
}
this.currentTempoChangingExpression = this.activeTempoExpression;
} catch (err) {
log.info("MusicPartManagerIterator: " + err);
}
}
public backJumpOccurred: boolean;
public forwardJumpOccurred: boolean;
private musicSheet: MusicSheet;
private currentMappingPart: MappingSourceMusicPart;
private currentMeasure: SourceMeasure;
private currentMeasureIndex: number = 0;
private currentPartIndex: number = 0;
private currentVoiceEntryIndex: number = -1;
private currentDynamicEntryIndex: number = 0;
private currentTempoEntryIndex: number = 0;
private currentVoiceEntries: VoiceEntry[];
private currentDynamicChangingExpressions: DynamicsContainer[] = [];
private currentTempoChangingExpression: MultiTempoExpression;
// FIXME: replace these two with a real Dictionary!
private repetitionIterationCountDictKeys: Repetition[];
private repetitionIterationCountDictValues: number[];
private currentRepetition: Repetition = undefined;
private endReached: boolean = false;
private frontReached: boolean = false;
public currentTimeStamp: Fraction = new Fraction(0, 1);
private currentEnrolledMeasureTimestamp: Fraction = new Fraction(0, 1);
private currentRelativeInMeasureTimestamp: Fraction = new Fraction(0, 1);
private currentVerticalContainerInMeasureTimestamp: Fraction = new Fraction(0, 1);
private jumpResponsibleRepetition: Repetition = undefined;
private currentBpm: number;
private activeDynamicExpressions: AbstractExpression[] = [];
private activeTempoExpression: MultiTempoExpression;
public SkipInvisibleNotes: boolean = true;
public get EndReached(): boolean {
return this.endReached;
}
public get FrontReached(): boolean {
return this.frontReached;
}
public get CurrentMeasure(): SourceMeasure {
return this.currentMeasure;
}
public get CurrentRepetition(): Repetition {
return this.currentRepetition;
}
public get CurrentRepetitionIteration(): number {
if (this.CurrentRepetition) {
return this.getRepetitionIterationCount(this.CurrentRepetition);
}
return 0;
}
public get CurrentJumpResponsibleRepetitionIterationBeforeJump(): number {
if (this.jumpResponsibleRepetition) {
return this.getRepetitionIterationCount(this.jumpResponsibleRepetition) - 1;
}
return 0;
}
public get CurrentBpm(): number {
return this.currentBpm;
}
public get CurrentVoiceEntries(): VoiceEntry[] {
return this.currentVoiceEntries;
}
public get CurrentMeasureIndex(): number {
return this.currentMeasureIndex;
}
public get CurrentEnrolledTimestamp(): Fraction {
return Fraction.plus(this.currentEnrolledMeasureTimestamp, this.currentVerticalContainerInMeasureTimestamp);
}
public get CurrentSourceTimestamp(): Fraction {
return this.currentTimeStamp;
}
public get CurrentRelativeInMeasureTimestamp(): Fraction {
return this.currentRelativeInMeasureTimestamp;
}
public get JumpOccurred(): boolean {
return this.backJumpOccurred || this.forwardJumpOccurred;
}
public get ActiveTempoExpression(): MultiTempoExpression {
return this.activeTempoExpression;
}
public get ActiveDynamicExpressions(): AbstractExpression[] {
return this.activeDynamicExpressions;
}
public get CurrentTempoChangingExpression(): MultiTempoExpression {
return this.currentTempoChangingExpression;
}
public get JumpResponsibleRepetition(): Repetition {
return this.jumpResponsibleRepetition;
}
/**
* Creates a clone of this iterator which has the same actual position.
*/
public clone(startTimeStamp: Fraction = undefined, endTimeStamp: Fraction = undefined): MusicPartManagerIterator {
const ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.musicSheet, startTimeStamp ?? this.currentTimeStamp, endTimeStamp);
ret.currentVoiceEntryIndex = this.currentVoiceEntryIndex;
ret.currentMappingPart = this.currentMappingPart;
ret.currentPartIndex = this.currentPartIndex;
ret.currentVoiceEntries = this.currentVoiceEntries;
ret.endReached = this.endReached;
ret.frontReached = this.frontReached;
// alternative method to set currentTimeStamp? may not fully affect current iterator position
// ret.currentTimeStamp = this.currentTimeStamp;
return ret;
}
/**
* Returns the visible voice entries for the provided instrument of the current iterator position.
* @param instrument
* Returns: A List of voiceEntries. If there are no entries the List has a Count of 0 (it does not return null).
*/
public CurrentVisibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
const voiceEntries: VoiceEntry[] = [];
if (!this.currentVoiceEntries) {
return voiceEntries;
}
if (instrument) {
for (const entry of this.currentVoiceEntries) {
if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
this.getVisibleEntries(entry, voiceEntries);
return voiceEntries;
}
}
} else {
for (const entry of this.currentVoiceEntries) {
this.getVisibleEntries(entry, voiceEntries);
}
}
return voiceEntries;
}
/**
* Returns the visible voice entries for the provided instrument of the current iterator position.
* @param instrument
* Returns: A List of voiceEntries. If there are no entries the List has a Count of 0 (it does not return null).
*/
public CurrentAudibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
const voiceEntries: VoiceEntry[] = [];
if (!this.currentVoiceEntries) {
return voiceEntries;
}
if (instrument) {
for (const entry of this.currentVoiceEntries) {
if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
this.getAudibleEntries(entry, voiceEntries);
return voiceEntries;
}
}
} else {
for (const entry of this.currentVoiceEntries) {
this.getAudibleEntries(entry, voiceEntries);
}
}
return voiceEntries;
}
/**
* Returns the audible dynamics of the current iterator position.
* Returns: A List of Dynamics. If there are no entries the List has a Count of 0 (it does not return null).
*/
public getCurrentDynamicChangingExpressions(): DynamicsContainer[] {
return this.currentDynamicChangingExpressions;
}
/**
* Returns the score following voice entries for the provided instrument of the current iterator position.
* @param instrument
* Returns: A List of voiceEntries. If there are no entries the List has a Count of 0
* (it does not return null).
*/
public CurrentScoreFollowingVoiceEntries(instrument?: Instrument): VoiceEntry[] {
const voiceEntries: VoiceEntry[] = [];
if (!this.currentVoiceEntries) {
return voiceEntries;
}
if (instrument) {
for (const entry of this.currentVoiceEntries) {
if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
this.getScoreFollowingEntries(entry, voiceEntries);
return voiceEntries;
}
}
} else {
for (const entry of this.currentVoiceEntries) {
this.getScoreFollowingEntries(entry, voiceEntries);
}
}
return voiceEntries;
}
//public currentPlaybackSettings(): PlaybackSettings {
// return this.manager.MusicSheet.SheetPlaybackSetting;
//}
// move to previous
public moveToPrevious(): void {
// this.forwardJumpOccurred = this.backJumpOccurred = false;
if (this.frontReached) {
return;
}
if (this.currentVoiceEntries) {
this.currentVoiceEntries = [];
}
this.recursiveMoveBack();
}
public moveToPreviousVisibleVoiceEntry(notesOnly: boolean): void {
while (!this.frontReached) {
this.moveToPrevious();
if (this.checkEntries(notesOnly)) {
return;
}
}
}
public moveToNext(): void {
this.forwardJumpOccurred = this.backJumpOccurred = false;
if (this.endReached) { return; }
if (this.frontReached) {
this.frontReached = false;
this.currentVoiceEntryIndex = -1;
}
if (this.currentVoiceEntries) {
this.currentVoiceEntries = [];
}
this.recursiveMove();
if (!this.currentMeasure) {
this.currentTimeStamp = new Fraction(99999, 1);
this.currentMeasure = this.musicSheet.SourceMeasures.last();
}
}
public moveToNextVisibleVoiceEntry(notesOnly: boolean): void {
while (!this.endReached) {
this.moveToNext();
if (this.checkEntries(notesOnly)) { return; }
}
}
private resetRepetitionIterationCount(repetition: Repetition): number {
this.setRepetitionIterationCount(repetition, 1);
return 1;
}
private incrementRepetitionIterationCount(repetition: Repetition): number {
if (this.repetitionIterationCountDictKeys.indexOf(repetition) === -1) {
return this.setRepetitionIterationCount(repetition, 1);
} else {
return this.setRepetitionIterationCount(repetition, this.getRepetitionIterationCount(repetition) + 1);
}
}
private setRepetitionIterationCount(repetition: Repetition, iterationCount: number): number {
const i: number = this.repetitionIterationCountDictKeys.indexOf(repetition);
if (i === -1) {
this.repetitionIterationCountDictKeys.push(repetition);
this.repetitionIterationCountDictValues.push(iterationCount);
} else {
this.repetitionIterationCountDictValues[i] = iterationCount;
}
return iterationCount;
}
private getRepetitionIterationCount(rep: Repetition): number {
const i: number = this.repetitionIterationCountDictKeys.indexOf(rep);
if (i !== -1) {
return this.repetitionIterationCountDictValues[i];
}
}
/* private moveTempoIndexToTimestamp(measureNumber: number): void {
for (let index: number = 0; index < this.manager.MusicSheet.TimestampSortedTempoExpressionsList.length; index++) {
if (this.manager.MusicSheet.TimestampSortedTempoExpressionsList[index].SourceMeasureParent.MeasureNumber >= measureNumber) {
this.currentTempoEntryIndex = Math.Max(-1, index - 1);
return
}
}
}
private getNextTempoEntryTimestamp(): Fraction {
if (this.currentTempoEntryIndex >= this.manager.MusicSheet.TimestampSortedTempoExpressionsList.length - 1) {
return new Fraction(99999, 1);
}
return this.manager.MusicSheet.TimestampSortedTempoExpressionsList[this.currentTempoEntryIndex + 1].SourceMeasureParent.AbsoluteTimestamp +
this.manager.MusicSheet.TimestampSortedTempoExpressionsList[this.currentTempoEntryIndex + 1].Timestamp;
}
private moveToNextDynamic(): void {
this.currentDynamicEntryIndex++;
this.currentDynamicChangingExpressions.Clear();
let curDynamicEntry: DynamicsContainer = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[this.currentDynamicEntryIndex];
this.currentDynamicChangingExpressions.push(curDynamicEntry);
let tsNow: Fraction = curDynamicEntry.parMultiExpression().AbsoluteTimestamp;
for (let i: number = this.currentDynamicEntryIndex + 1; i < this.manager.MusicSheet.TimestampSortedDynamicExpressionsList.length; i++) {
curDynamicEntry = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[i];
if ((curDynamicEntry.parMultiExpression().AbsoluteTimestamp !== tsNow)) { break; }
this.currentDynamicEntryIndex = i;
this.currentDynamicChangingExpressions.push(curDynamicEntry);
}
}
private moveDynamicIndexToTimestamp(absoluteTimestamp: Fraction): void {
let dynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
for (let index: number = 0; index < dynamics.length; index++) {
if (dynamics[index].parMultiExpression().AbsoluteTimestamp.gte(absoluteTimestamp)) {
this.currentDynamicEntryIndex = Math.Max(0, index - 1);
return
}
}
}
private getNextDynamicsEntryTimestamp(): Fraction {
if (this.currentDynamicEntryIndex >= this.manager.MusicSheet.TimestampSortedDynamicExpressionsList.length - 1) {
return new Fraction(99999, 1);
}
return this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[this.currentDynamicEntryIndex + 1].parMultiExpression().AbsoluteTimestamp;
}
*/
private handleRepetitionsAtMeasureBegin(): void {
for (let idx: number = 0, len: number = this.currentMeasure.FirstRepetitionInstructions.length; idx < len; ++idx) {
const repetitionInstruction: RepetitionInstruction = this.currentMeasure.FirstRepetitionInstructions[idx];
if (!repetitionInstruction.parentRepetition) { continue; }
const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
this.currentRepetition = currentRepetition;
if (currentRepetition.StartIndex === this.currentMeasureIndex) {
if (
this.JumpResponsibleRepetition !== undefined &&
currentRepetition !== this.JumpResponsibleRepetition &&
currentRepetition.StartIndex >= this.JumpResponsibleRepetition.StartIndex &&
currentRepetition.EndIndex <= this.JumpResponsibleRepetition.EndIndex
) {
this.resetRepetitionIterationCount(currentRepetition);
}
}
}
}
private handleRepetitionsAtMeasureEnd(): void {
for (let idx: number = 0, len: number = this.currentMeasure.LastRepetitionInstructions.length; idx < len; ++idx) {
const repetitionInstruction: RepetitionInstruction = this.currentMeasure.LastRepetitionInstructions[idx];
const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
if (!currentRepetition) { continue; }
if (currentRepetition.BackwardJumpInstructions.indexOf(repetitionInstruction) > -1) {
if (this.getRepetitionIterationCount(currentRepetition) < currentRepetition.UserNumberOfRepetitions) {
this.doBackJump(currentRepetition);
this.backJumpOccurred = true;
return;
}
}
if (repetitionInstruction === currentRepetition.forwardJumpInstruction) {
if (
this.JumpResponsibleRepetition !== undefined
&& currentRepetition !== this.JumpResponsibleRepetition
&& currentRepetition.StartIndex >= this.JumpResponsibleRepetition.StartIndex
&& currentRepetition.EndIndex <= this.JumpResponsibleRepetition.EndIndex
) {
this.resetRepetitionIterationCount(currentRepetition);
}
const forwardJumpTargetMeasureIndex: number = currentRepetition.getForwardJumpTargetForIteration(
this.getRepetitionIterationCount(currentRepetition)
);
if (forwardJumpTargetMeasureIndex >= 0) {
this.currentMeasureIndex = forwardJumpTargetMeasureIndex;
this.currentMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex];
this.currentVoiceEntryIndex = -1;
this.jumpResponsibleRepetition = currentRepetition;
this.forwardJumpOccurred = true;
return;
}
if (forwardJumpTargetMeasureIndex === -2) {
this.endReached = true;
}
}
}
this.currentMeasureIndex++;
if (this.JumpResponsibleRepetition !== undefined && this.currentMeasureIndex > this.JumpResponsibleRepetition.EndIndex) {
this.jumpResponsibleRepetition = undefined;
}
}
private doBackJump(currentRepetition: Repetition): void {
this.currentMeasureIndex = currentRepetition.getBackwardJumpTarget();
this.currentMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex];
this.currentVoiceEntryIndex = -1;
this.incrementRepetitionIterationCount(currentRepetition);
this.jumpResponsibleRepetition = currentRepetition;
}
private activateCurrentRhythmInstructions(): void {
if (
this.currentMeasure !== undefined &&
this.currentMeasure.FirstInstructionsStaffEntries.length > 0 &&
this.currentMeasure.FirstInstructionsStaffEntries[0] !== undefined
) {
const instructions: AbstractNotationInstruction[] = this.currentMeasure.FirstInstructionsStaffEntries[0].Instructions;
for (let idx: number = 0, len: number = instructions.length; idx < len; ++idx) {
const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
if (abstractNotationInstruction instanceof RhythmInstruction) {
this.musicSheet.SheetPlaybackSetting.rhythm = (<RhythmInstruction>abstractNotationInstruction).Rhythm;
}
}
}
}
private activateCurrentDynamicOrTempoInstructions(): void {
const timeSortedDynamics: DynamicsContainer[] = this.musicSheet.TimestampSortedDynamicExpressionsList;
while (
this.currentDynamicEntryIndex > 0 && (
this.currentDynamicEntryIndex >= timeSortedDynamics.length ||
this.CurrentSourceTimestamp.lte(timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp)
)
) {
this.currentDynamicEntryIndex--;
}
while (
this.currentDynamicEntryIndex < timeSortedDynamics.length &&
timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp.lt(this.CurrentSourceTimestamp)
) {
this.currentDynamicEntryIndex++;
}
while (
this.currentDynamicEntryIndex < timeSortedDynamics.length
&& timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp.Equals(this.CurrentSourceTimestamp)
) {
const dynamicsContainer: DynamicsContainer = timeSortedDynamics[this.currentDynamicEntryIndex];
const staffIndex: number = dynamicsContainer.staffNumber;
if (this.CurrentSourceTimestamp.Equals(dynamicsContainer.parMultiExpression().AbsoluteTimestamp)) {
if (dynamicsContainer.continuousDynamicExpression) {
this.activeDynamicExpressions[staffIndex] = dynamicsContainer.continuousDynamicExpression;
} else if (dynamicsContainer.instantaneousDynamicExpression) {
this.activeDynamicExpressions[staffIndex] = dynamicsContainer.instantaneousDynamicExpression;
}
}
this.currentDynamicEntryIndex++;
}
this.currentDynamicChangingExpressions = [];
for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
if (this.activeDynamicExpressions[staffIndex]) {
let startTime: Fraction;
let endTime: Fraction;
if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
const continuousDynamic: ContinuousDynamicExpression = <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
startTime = continuousDynamic.StartMultiExpression.AbsoluteTimestamp;
endTime = continuousDynamic.EndMultiExpression.AbsoluteTimestamp;
if (startTime.lte(this.CurrentSourceTimestamp) && this.CurrentSourceTimestamp.lte(endTime)) {
this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
}
} else {
const instantaneousDynamic: InstantaneousDynamicExpression = <InstantaneousDynamicExpression>this.activeDynamicExpressions[staffIndex];
if (this.CurrentSourceTimestamp.Equals(instantaneousDynamic.ParentMultiExpression.AbsoluteTimestamp)) {
this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaneousDynamic, staffIndex));
}
}
}
}
const timeSortedTempoExpressions: MultiTempoExpression[] = this.musicSheet.TimestampSortedTempoExpressionsList;
while (this.currentTempoEntryIndex > 0 && (
this.currentTempoEntryIndex >= timeSortedTempoExpressions.length
|| this.CurrentSourceTimestamp.lte(timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp)
)) {
this.currentTempoEntryIndex--;
}
while (
this.currentTempoEntryIndex < timeSortedTempoExpressions.length &&
timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp.lt(this.CurrentSourceTimestamp)
) {
this.currentTempoEntryIndex++;
}
while (
this.currentTempoEntryIndex < timeSortedTempoExpressions.length
&& timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp.Equals(this.CurrentSourceTimestamp)
) {
this.activeTempoExpression = timeSortedTempoExpressions[this.currentTempoEntryIndex];
this.currentTempoEntryIndex++;
}
this.currentTempoChangingExpression = undefined;
if (this.activeTempoExpression) {
let endTime: Fraction = this.activeTempoExpression.AbsoluteTimestamp;
if (this.activeTempoExpression.ContinuousTempo) {
endTime = this.activeTempoExpression.ContinuousTempo.AbsoluteEndTimestamp;
}
if ( this.activeTempoExpression.AbsoluteTimestamp.lte(this.CurrentSourceTimestamp)
|| this.CurrentSourceTimestamp.lte(endTime)
) {
this.currentTempoChangingExpression = this.activeTempoExpression;
}
}
}
private recursiveMoveBack(): void {
if (this.currentVoiceEntryIndex > 0 ) {
this.currentVoiceEntryIndex--;
const currentContainer: VerticalSourceStaffEntryContainer = this.currentMeasure.VerticalSourceStaffEntryContainers[this.currentVoiceEntryIndex];
this.currentVoiceEntries = this.getVoiceEntries(currentContainer);
this.currentVerticalContainerInMeasureTimestamp = currentContainer.Timestamp;
this.currentRelativeInMeasureTimestamp = this.currentVerticalContainerInMeasureTimestamp;
this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
this.activateCurrentDynamicOrTempoInstructions();
// re-check endReached
const selectionEnd: Fraction = this.musicSheet.SelectionEnd;
if (selectionEnd && this.currentTimeStamp.lt(selectionEnd)) {
this.endReached = false;
}
this.currentMeasureIndex = this.musicSheet.SourceMeasures.indexOf(this.CurrentMeasure);
return;
}
else if (this.currentVoiceEntryIndex === 0 && this.currentMeasureIndex !== 0) {
const m: SourceMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex-1];
this.currentMeasureIndex--;
this.currentMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex];
const currentContainer: VerticalSourceStaffEntryContainer = m.VerticalSourceStaffEntryContainers[m.VerticalSourceStaffEntryContainers.length-1];
this.currentVoiceEntries = this.getVoiceEntries(currentContainer);
this.currentVerticalContainerInMeasureTimestamp = currentContainer.Timestamp;
this.currentVoiceEntryIndex = m.VerticalSourceStaffEntryContainers.length-1;
this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, currentContainer.Timestamp);
this.activateCurrentDynamicOrTempoInstructions();
// re-check endReached
const selectionEnd: Fraction = this.musicSheet.SelectionEnd;
if (selectionEnd && this.currentTimeStamp.lt(selectionEnd)) {
this.endReached = false;
}
return;
}
// we reached the beginning
this.frontReached = true;
this.currentTimeStamp = new Fraction(-1, 1);
}
private recursiveMove(): void {
this.currentVoiceEntryIndex++; // TODO handle hidden part: skip hidden voice if requested by parameter
if (this.currentVoiceEntryIndex === 0) {
this.handleRepetitionsAtMeasureBegin();
this.activateCurrentRhythmInstructions();
}
// everything fine, no complications
if (this.currentVoiceEntryIndex >= 0 && this.currentVoiceEntryIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length) {
const currentContainer: VerticalSourceStaffEntryContainer = this.currentMeasure.VerticalSourceStaffEntryContainers[this.currentVoiceEntryIndex];
this.currentVoiceEntries = this.getVoiceEntries(currentContainer);
this.currentVerticalContainerInMeasureTimestamp = currentContainer.Timestamp;
this.currentRelativeInMeasureTimestamp = this.currentVerticalContainerInMeasureTimestamp;
this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
const selectionEnd: Fraction = this.musicSheet.SelectionEnd;
// TODO handle selectionEnd undefined, can happen in Beethoven Ferne Geliebte
if (selectionEnd && this.currentTimeStamp.gte(selectionEnd)) {
this.endReached = true;
}
this.activateCurrentDynamicOrTempoInstructions();
return;
}
this.currentEnrolledMeasureTimestamp.Add(this.currentMeasure.Duration);
this.handleRepetitionsAtMeasureEnd();
if (this.currentMeasureIndex >= 0 && this.currentMeasureIndex < this.musicSheet.SourceMeasures.length) {
this.currentMeasure = this.musicSheet.SourceMeasures[this.currentMeasureIndex];
this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
this.currentVoiceEntryIndex = -1;
this.recursiveMove();
return;
}
// we reached the end
this.currentVerticalContainerInMeasureTimestamp = new Fraction();
this.currentMeasure = undefined;
this.currentVoiceEntries = undefined;
this.endReached = true;
}
/**
* helper function for moveToNextVisibleVoiceEntry and moveToPreviousVisibleVoiceEntry
* Get all entries and check if there is at least one valid entry in the list
* @param notesOnly
*/
private checkEntries(notesOnly: boolean): boolean {
const tlist: VoiceEntry[] = this.CurrentVisibleVoiceEntries();
if (tlist.length > 0) {
if (!notesOnly) { return true; }
for (let idx: number = 0, len: number = tlist.length; idx < len; ++idx) {
const entry: VoiceEntry = tlist[idx];
if (entry.Notes[0].Pitch) { return true; }
}
}
return false;
}
private getVisibleEntries(entry: VoiceEntry, visibleEntries: VoiceEntry[]): void {
if (entry.ParentVoice.Visible) {
let anyNoteVisible: boolean = false;
for (const note of entry.Notes) {
if (note.PrintObject) {
anyNoteVisible = true;
break;
}
}
if (!anyNoteVisible && this.SkipInvisibleNotes) {
return;
}
visibleEntries.push(entry);
}
}
private getAudibleEntries(entry: VoiceEntry, audibleEntries: VoiceEntry[]): void {
if (entry.ParentVoice.Audible) {
audibleEntries.push(entry);
}
}
private getScoreFollowingEntries(entry: VoiceEntry, followingEntries: VoiceEntry[]): void {
if (entry.ParentVoice.Following && entry.ParentVoice.Parent.Following) {
followingEntries.push(entry);
}
}
private getVoiceEntries(container: VerticalSourceStaffEntryContainer): VoiceEntry[] {
const entries: VoiceEntry[] = [];
for (const sourceStaffEntry of container.StaffEntries) {
if (!sourceStaffEntry) { continue; }
for (const voiceEntry of sourceStaffEntry.VoiceEntries) {
entries.push(voiceEntry);
}
}
return entries;
}
}