/*
* Copyright (C) 2013-2017 たんらる
*/
package fourthline.mmlTools.core;
import java.util.Arrays;
import java.util.List;
/**
* 音符の時間変換値
* @author たんらる
*/
public final class MMLTicks {
private static final MMLTickTable tickTable = MMLTickTable.createTickTable();
public static int getTick(String gt) throws UndefinedTickException {
String str = gt;
while (!tickTable.getTable().containsKey(str)) {
if (str.length() == 0) {
break;
}
char ch = str.charAt(str.length()-1);
if (!Character.isDigit(ch)) {
int len = str.length();
str = str.substring(0, len - 1);
} else {
throw new UndefinedTickException(gt);
}
}
return tickTable.getTable().get(str);
}
private static Integer minimum = null;
public static int minimumTick() {
if (minimum != null) {
return minimum;
}
Integer v = null;
for (Integer i : tickTable.getTable().values()) {
if ( (v == null) || (i < v) ) {
v = i;
}
}
minimum = v;
return minimum;
}
private String noteName;
int tick;
boolean needTie;
/**
* tick長をMML文字列に変換します.
* @param noteName ノート名
* @param tick tick長
*/
public MMLTicks(String noteName, int tick) {
this(noteName, tick, true);
}
/**
* tick長をMML文字列に変換します.
* @param noteName ノート名
* @param tick tick長
* @param needTie noteNameを連結するときに tie が必要かどうかを指定します. 休符 or 音量ゼロのときは, falseを指定してください.
*/
public MMLTicks(String noteName, int tick, boolean needTie) {
this.noteName = noteName;
this.tick = tick;
this.needTie = needTie;
}
private String mmlNotePart(String phoneticString) {
return mmlNotePart(Arrays.asList(phoneticString));
}
private String mmlNotePart(List<String> phoneticString) {
StringBuilder sb = new StringBuilder();
phoneticString.forEach(t -> {
if (needTie) {
sb.append('&');
}
sb.append(noteName).append(t);
});
return sb.toString();
}
private String makeMMLText(StringBuilder sb, int remTick) throws UndefinedTickException {
// 1~64の分割
if (remTick > 0) {
for (int base = 1; base <= 64; base *= 2) {
int baseTick = getTick(""+base);
if (tickTable.getInvTable().containsKey(remTick)) {
sb.append( mmlNotePart(tickTable.getInvTable().get(remTick)) );
remTick = 0;
break;
}
while (remTick >= baseTick) {
sb.append( mmlNotePart(""+base) );
remTick -= baseTick;
}
}
if (remTick > 0) {
throw new UndefinedTickException(remTick + "/" + tick);
}
}
if (needTie) {
return sb.substring(1);
} else {
return sb.toString();
}
}
/**
* noteNameとtickをMMLの文字列に変換します.
* needTieがtrueのときは、'&' による連結を行います.
* @return MML文字列
* @throws UndefinedTickException 変換に失敗した
*/
public String toMMLText() throws UndefinedTickException {
int remTick = tick;
StringBuilder sb = new StringBuilder();
// "1."
int mTick = getTick("1.");
int tick1 = getTick("1");
while (remTick > (tick1*2)) {
sb.append( mmlNotePart("1.") );
remTick -= mTick;
}
return makeMMLText(sb, remTick);
}
/**
* Base長を使って変換します. (調律用)
* @param base 使用する調律指定
* @return MML文字列
* @throws UndefinedTickException 変換に失敗した
*/
public String toMMLTextByBase(TuningBase base) throws UndefinedTickException {
int remTick = tick;
StringBuilder sb = new StringBuilder();
int baseTick = base.getTick();
while (remTick >= baseTick) {
sb.append( mmlNotePart(base.getBase()) );
remTick -= baseTick;
}
return makeMMLText(sb, remTick);
}
}