package com.xenoage.zong.musiclayout.settings; import static com.xenoage.utils.CheckUtils.checkArgsNotNull; import static com.xenoage.utils.collections.CollectionUtils.getMax; import static com.xenoage.utils.collections.CollectionUtils.getMin; import static com.xenoage.utils.collections.CollectionUtils.map; import static com.xenoage.utils.math.Fraction._0; import static com.xenoage.utils.math.Fraction.fr; import java.util.Map; import com.xenoage.utils.annotations.NonNull; import com.xenoage.utils.math.Fraction; /** * Settings for space (in IS) chords need. * * @author Andreas Wenger */ public class ChordSpacings { //duration-to-width mapping private Map<Fraction, Float> durationWidths; //cache private Fraction durationWidthsLowestDuration = _0, durationWidthsHighestDuration = _0; private Map<Fraction, Float> durationWidthsCache = map(); public static final ChordSpacings defaultChordSpacingsNormal; public static final ChordSpacings defaultChordSpacingsGrace; static { Map<Fraction, Float> n = map(); n.put(fr(1,64), 1+1/2f); n.put(fr(1,32), 1+3/2f); n.put(fr(1,16), 2f); n.put(fr(1,8), 2+1/2f); n.put(fr(1,4), 3+1/2f); n.put(fr(3,8), 4+1/4f); n.put(fr(1,2), 4+3/4f); n.put(fr(1,1), 7+1/4f); defaultChordSpacingsNormal = new ChordSpacings(n); Map<Fraction, Float> g = map(); g.put(fr(1,16), 1+1/4f); g.put(fr(1,8), 1+1/2f); g.put(fr(1,4), 1+3/4f); defaultChordSpacingsGrace = new ChordSpacings(g); } public ChordSpacings(Map<Fraction, Float> durationWidths) { this.durationWidths = durationWidths; //init cache //find lowest and highest duration durationWidthsLowestDuration = getMin(durationWidths.keySet()); durationWidthsHighestDuration = getMax(durationWidths.keySet()); } /** * Computes and returns the width that fits to the given duration. */ public float getWidth(@NonNull Fraction duration) { checkArgsNotNull(duration); //if available, use defined width Float width = durationWidths.get(duration); if (width != null) return width; //if available, use cached computed width width = durationWidthsCache.get(duration); if (width != null) return width; //not found. find the greatest lesser duration and the lowest //greater duration and interpolate linearly. remember the result //to avoid this computation for the future. Fraction lowerDur = durationWidthsLowestDuration; Fraction higherDur = durationWidthsHighestDuration; for (Fraction d : durationWidths.keySet()) { if (d.compareTo(duration) <= 0 && d.compareTo(lowerDur) > 0) { lowerDur = d; } if (d.compareTo(duration) >= 0 && d.compareTo(higherDur) < 0) { higherDur = d; } } float lowerWidth = durationWidths.get(lowerDur); float higherWidth = durationWidths.get(higherDur); float durationWidth = (lowerWidth + higherWidth) * duration.toFloat() / (lowerDur.toFloat() + higherDur.toFloat()); durationWidthsCache.put(duration, durationWidth); return durationWidth; } }