package com.xenoage.zong.io.midi.out.score;
import com.xenoage.utils.annotations.Const;
import com.xenoage.utils.collections.CList;
import com.xenoage.utils.collections.IList;
import com.xenoage.utils.math.Fraction;
import com.xenoage.zong.core.Score;
import com.xenoage.zong.core.position.Time;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import static com.xenoage.utils.collections.CList.clist;
import static com.xenoage.utils.kernel.Range.range;
import static com.xenoage.utils.math.Fraction._0;
/**
* The length of each measure column in a score.
*
* @author Andreas Wenger
*/
@Const @Data @AllArgsConstructor(access = AccessLevel.PRIVATE)
public class MeasureBeats {
private final IList<Fraction> measureLengths;
/**
* Finds the {@link MeasureBeats} in a {@link Score}.
*/
public static MeasureBeats findMeasureBeats(Score score) {
CList<Fraction> lengths = clist();
for (int iMeasure : range(score.getMeasuresCount()))
lengths.add(score.getMeasureBeats(iMeasure));
return new MeasureBeats(lengths.close());
}
/**
* Gets the length of the given measure column in beats.
* @param measure the index of the measure. May be also one measure after
* the score ends, then 0 is returned.
*/
public Fraction getLength(int measure) {
if (measure == measureLengths.size())
return _0;
return measureLengths.get(measure);
}
/**
* Computes the beats between the given two {@link Time}s.
*/
public Fraction computeBeatsBetween(Time startTime, Time endTime) {
if (startTime.compareTo(endTime) > 0) {
throw new IllegalArgumentException("startTime > endTime");
}
else if (startTime.measure == endTime.measure) {
//simple case: same measure
return endTime.beat.sub(startTime.beat);
}
else {
//first measure
Fraction ret = getLength(startTime.measure).sub(startTime.beat);
//measures inbetween
for (int iMeasure : range(startTime.measure + 1, endTime.measure - 1))
ret = ret.add(getLength(iMeasure));
//last measure
ret = ret.add(endTime.beat);
return ret;
}
}
}