package com.xenoage.zong.core.music.util;
import com.xenoage.utils.math.Fraction;
import static com.xenoage.utils.math.Fraction.fr;
/**
* This class contains methods that
* provide information about a duration,
* e.g. how much flags or which symbol it needs.
*
* @author Andreas Wenger
*/
public final class DurationInfo {
/** Duration of a note symbol. */
public enum Type {
Whole,
Half,
Quarter,
Eighth,
_16th,
_32th,
_64th,
_128th,
_256th;
}
/**
* Gets the type of symbol that
* must be used for the given duration for a notehead.
*/
public static Type getNoteheadSymbolType(Fraction duration) {
int length = duration.getNumerator() * 4 / duration.getDenominator(); //looseness does not matter
if (length >= 4)
return Type.Whole;
else if (length >= 2)
return Type.Half;
else
return Type.Quarter;
}
/**
* Gets the type of rest that must be used for the given duration.
* If the duration doesn't fit to a type exactly, the next bigger
* type is returned (so that tuplets are handled correctly) - TODO: better strategy needed?
*/
public static Type getRestType(Fraction duration) {
float length = duration.getNumerator() * 4f / duration.getDenominator(); //looseness does not matter
if (length <= 1 / 64f)
return Type._256th;
else if (length <= 1 / 32f)
return Type._128th;
else if (length <= 1 / 16f)
return Type._64th;
else if (length <= 1 / 8f)
return Type._32th;
else if (length <= 1 / 4f)
return Type._16th;
else if (length <= 1 / 2f)
return Type.Eighth;
else if (length <= 1)
return Type.Quarter;
else if (length <= 2)
return Type.Half;
else
return Type.Whole;
}
/**
* Gets the number of flags of a unbeamed chord with the given
* duration. If the duration is equal or greater than 1/4,
* 0 is returned.
*/
public static int getFlagsCount(Fraction duration) {
int n = duration.getNumerator();
int d = duration.getDenominator();
//quick test for common cases
if (n == 1) {
if (d <= 4)
return 0; //4th or bigger
switch (d) {
case 8:
return 1; //8th
case 16:
return 2; //16th
case 32:
return 3; //32th
case 64:
return 4; //64th
}
}
//otherwise: compute the value
while (n > 2) {
d /= 2; //one flag less (e.g. from 64th to 32nd)
n /= 2; //get rid of the dot (e.g. from 3/64 to 1/32)
}
if (d >= 256)
return 6;
else if (d >= 128)
return 5;
else if (d >= 64)
return 4;
else if (d >= 32)
return 3;
else if (d >= 16)
return 2;
else if (d >= 8)
return 1;
else
return 0;
}
/**
* Gets the duration of the given base duration with the
* given number of dots.
*/
public static Fraction getDuration(Fraction baseDuration, int dots) {
Fraction ret = baseDuration;
if (dots == 1)
ret = ret.mult(fr(3, 2)); //+ 50%
else if (dots == 2)
ret = ret.mult(fr(7, 4)); //+ 50% + 25%
return ret;
}
/**
* Gets the number of dots of the given fraction.
* At maximum two dots are returned, otherwise 0 is returned.
*/
public static int getDots(Fraction duration) {
int num = duration.getNumerator();
//try to find dots:
//find the base fraction
int dotDenominator = 256; //start with 256th notes
while (dotDenominator > 1 && duration.compareTo(fr(1, dotDenominator)) > 0) {
dotDenominator /= 2;
}
//find the dots
if (num == 3) {
//1/dotDenominator with one dot
return 1;
}
else if (num == 7) {
//1/dotDenominator with two dots
return 2;
}
else {
//more than 2 dots? Not handled.
return 0;
}
}
/**
* Gets the base duration of the given duration, that is the
* duration without dots.
*/
public static Fraction getBaseDuration(Fraction duration) {
Fraction ret = duration;
int dots = getDots(duration);
if (dots == 1)
ret = ret.mult(fr(2, 3)); //remove the added 50%
else if (dots == 2)
ret = ret.mult(fr(4, 7)); //remove the added (50% + 25%)
return ret;
}
}