package com.xenoage.zong.io.midi.out.time; import com.xenoage.utils.annotations.Const; import com.xenoage.utils.collections.CList; import com.xenoage.utils.collections.IList; import com.xenoage.utils.collections.TriMap; import com.xenoage.utils.kernel.Tuple3; import com.xenoage.zong.core.position.Time; import lombok.EqualsAndHashCode; import lombok.val; import java.util.Collections; import static com.xenoage.utils.collections.CList.clist; import static com.xenoage.utils.math.Fraction._0; import static com.xenoage.utils.math.MathUtils.max; import static com.xenoage.utils.math.MathUtils.min; import static com.xenoage.zong.core.position.Time.time; import static java.lang.Long.MAX_VALUE; import static java.lang.Long.MIN_VALUE; /** * Time mapping within a repetition. */ @Const @EqualsAndHashCode public final class RepTimes { public final int repetition; private final TriMap<Long, Time, Long> timeMap; //cache public final IList<Time> timesSorted; public final long minTick; public final long maxTick; public final Time minTime; public final Time maxTime; public final long minMs; public final long maxMs; RepTimes(int repetition, TriMap<Long, Time, Long> timeMap) { if (timeMap.size() == 0) throw new IllegalArgumentException("At least one time is required"); this.repetition = repetition; this.timeMap = timeMap; //compute list of times and find minimum and maximum values CList<Time> times = clist(timeMap.size()); long minTick = MAX_VALUE; long maxTick = MIN_VALUE; Time minTime = time(Integer.MAX_VALUE, _0); Time maxTime = time(-1, _0); long minMs = MAX_VALUE; long maxMs = MIN_VALUE; for (val tick : timeMap.getKeys1()) { val time = timeMap.getBy1(tick); times.add(time.get2()); minTick = min(minTick, time.get1()); maxTick = max(maxTick, time.get1()); minTime = min(minTime, time.get2()); maxTime = max(maxTime, time.get2()); minMs = min(minMs, time.get3()); maxMs = max(maxMs, time.get3()); } Collections.sort(times); this.timesSorted = times.close(); this.minTick = minTick; this.maxTick = maxTick; this.minTime = minTime; this.maxTime = maxTime; this.minMs = minMs; this.maxMs = maxMs; } /** * Returns the {@link MidiTime} at the given MIDI tick, or null. */ public MidiTime getByTick(long tick) { return get(timeMap.getBy1(tick)); } /** * Returns the {@link MidiTime} at the given {@link Time}, or null. */ public MidiTime getByTime(Time time) { return get(timeMap.getBy2(time)); } /** * Returns the {@link MidiTime} at the given MIDI millisecond, or null. */ public MidiTime getByMs(long ms) { return get(timeMap.getBy3(ms)); } private MidiTime get(Tuple3<Long, Time, Long> value) { if (value == null) return null; return new MidiTime(value.get1(), new RepTime(repetition, value.get2()), value.get3()); } public boolean containsTick(long tick) { return tick >= minTick && tick <= maxTick; } public boolean containsTime(Time time) { return time.compareTo(minTime) >= 0 && time.compareTo(maxTime) <= 0; } public boolean containsMs(long ms) { return ms >= minMs && ms <= maxMs; } }